aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/viml/parser/expressions.h
blob: 9d0bc9d4686a77e27a8793ee8b2d58964b50fafe (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
#ifndef NVIM_VIML_PARSER_EXPRESSIONS_H
#define NVIM_VIML_PARSER_EXPRESSIONS_H

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

#include "nvim/eval/typval.h"
#include "nvim/types.h"
#include "nvim/viml/parser/parser.h"

// Defines whether to ignore case:
//    ==   kCCStrategyUseOption
//    ==#  kCCStrategyMatchCase
//    ==?  kCCStrategyIgnoreCase
typedef enum {
  kCCStrategyUseOption = 0,  // 0 for xcalloc
  kCCStrategyMatchCase = '#',
  kCCStrategyIgnoreCase = '?',
} ExprCaseCompareStrategy;

/// Lexer token type
typedef enum {
  kExprLexInvalid = 0,  ///< Invalid token, indicaten an error.
  kExprLexMissing,  ///< Missing token, for use in parser.
  kExprLexSpacing,  ///< Spaces, tabs, newlines, etc.
  kExprLexEOC,  ///< End of command character: NL, |, just end of stream.

  kExprLexQuestion,  ///< Question mark, for use in ternary.
  kExprLexColon,  ///< Colon, for use in ternary.
  kExprLexOr,  ///< Logical or operator.
  kExprLexAnd,  ///< Logical and operator.
  kExprLexComparison,  ///< One of the comparison operators.
  kExprLexPlus,  ///< Plus sign.
  kExprLexMinus,  ///< Minus sign.
  kExprLexDot,  ///< Dot: either concat or subscript, also part of the float.
  kExprLexMultiplication,  ///< Multiplication, division or modulo operator.

  kExprLexNot,  ///< Not: !.

  kExprLexNumber,  ///< Integer number literal, or part of a float.
  kExprLexSingleQuotedString,  ///< Single quoted string literal.
  kExprLexDoubleQuotedString,  ///< Double quoted string literal.
  kExprLexOption,  ///< &optionname option value.
  kExprLexRegister,  ///< @r register value.
  kExprLexEnv,  ///< Environment $variable value.
  kExprLexPlainIdentifier,  ///< Identifier without scope: `abc`, `foo#bar`.

  kExprLexBracket,  ///< Bracket, either opening or closing.
  kExprLexFigureBrace,  ///< Figure brace, either opening or closing.
  kExprLexParenthesis,  ///< Parenthesis, either opening or closing.
  kExprLexComma,  ///< Comma.
  kExprLexArrow,  ///< Arrow, like from lambda expressions.
  kExprLexAssignment,  ///< Assignment: `=` or `{op}=`.
  // XXX When modifying this enum you need to also modify eltkn_type_tab in
  //     expressions.c and tests and, possibly, viml_pexpr_repr_token.
} LexExprTokenType;

typedef enum {
  kExprCmpEqual,  ///< Equality, inequality.
  kExprCmpMatches,  ///< Matches regex, not matches regex.
  kExprCmpGreater,  ///< `>` or `<=`
  kExprCmpGreaterOrEqual,  ///< `>=` or `<`.
  kExprCmpIdentical,  ///< `is` or `isnot`
} ExprComparisonType;

/// All possible option scopes
typedef enum {
  kExprOptScopeUnspecified = 0,
  kExprOptScopeGlobal = 'g',
  kExprOptScopeLocal = 'l',
} ExprOptScope;

/// All possible assignment types: `=` and `{op}=`.
typedef enum {
  kExprAsgnPlain = 0,  ///< Plain assignment: `=`.
  kExprAsgnAdd,  ///< Assignment augmented with addition: `+=`.
  kExprAsgnSubtract,  ///< Assignment augmented with subtraction: `-=`.
  kExprAsgnConcat,  ///< Assignment augmented with concatenation: `.=`.
} ExprAssignmentType;

#define EXPR_OPT_SCOPE_LIST \
  ((char[]){ kExprOptScopeGlobal, kExprOptScopeLocal })

/// All possible variable scopes
typedef enum {
  kExprVarScopeMissing = 0,
  kExprVarScopeScript = 's',
  kExprVarScopeGlobal = 'g',
  kExprVarScopeVim = 'v',
  kExprVarScopeBuffer = 'b',
  kExprVarScopeWindow = 'w',
  kExprVarScopeTabpage = 't',
  kExprVarScopeLocal = 'l',
  kExprVarScopeArguments = 'a',
} ExprVarScope;

#define EXPR_VAR_SCOPE_LIST \
  ((char[]) { \
    kExprVarScopeScript, kExprVarScopeGlobal, kExprVarScopeVim, \
    kExprVarScopeBuffer, kExprVarScopeWindow, kExprVarScopeTabpage, \
    kExprVarScopeLocal, kExprVarScopeBuffer, kExprVarScopeArguments, \
  })

/// Lexer token
typedef struct {
  ParserPosition start;
  size_t len;
  LexExprTokenType type;
  union {
    struct {
      ExprComparisonType type;  ///< Comparison type.
      ExprCaseCompareStrategy ccs;  ///< Case comparison strategy.
      bool inv;  ///< True if comparison is to be inverted.
    } cmp;  ///< For kExprLexComparison.

    struct {
      enum {
        kExprLexMulMul,  ///< Real multiplication.
        kExprLexMulDiv,  ///< Division.
        kExprLexMulMod,  ///< Modulo.
      } type;  ///< Multiplication type.
    } mul;  ///< For kExprLexMultiplication.

    struct {
      bool closing;  ///< True if bracket/etc is a closing one.
    } brc;  ///< For brackets/braces/parenthesis.

    struct {
      int name;  ///< Register name, may be -1 if name not present.
    } reg;  ///< For kExprLexRegister.

    struct {
      bool closed;  ///< True if quote was closed.
    } str;  ///< For kExprLexSingleQuotedString and kExprLexDoubleQuotedString.

    struct {
      const char *name;  ///< Option name start.
      size_t len;  ///< Option name length.
      ExprOptScope scope;  ///< Option scope: &l:, &g: or not specified.
    } opt;  ///< Option properties.

    struct {
      ExprVarScope scope;  ///< Scope character or 0 if not present.
      bool autoload;  ///< Has autoload characters.
    } var;  ///< For kExprLexPlainIdentifier

    struct {
      LexExprTokenType type;  ///< Suggested type for parsing incorrect code.
      const char *msg;  ///< Error message.
    } err;  ///< For kExprLexInvalid

    struct {
      union {
        float_T floating;
        uvarnumber_T integer;
      } val;  ///< Number value.
      uint8_t base;  ///< Base: 2, 8, 10 or 16.
      bool is_float;  ///< True if number is a floating-point.
    } num;  ///< For kExprLexNumber

    struct {
      ExprAssignmentType type;
    } ass;  ///< For kExprLexAssignment
  } data;  ///< Additional data, if needed.
} LexExprToken;

typedef enum {
  /// If set, “pointer” to the current byte in pstate will not be shifted
  kELFlagPeek = (1 << 0),
  /// Determines whether scope is allowed to come before the identifier
  kELFlagForbidScope = (1 << 1),
  /// Determines whether floating-point numbers are allowed
  ///
  /// I.e. whether dot is a decimal point separator or is not a part of
  /// a number at all.
  kELFlagAllowFloat = (1 << 2),
  /// Determines whether `is` and `isnot` are seen as comparison operators
  ///
  /// If set they are supposed to be just regular identifiers.
  kELFlagIsNotCmp = (1 << 3),
  /// Determines whether EOC tokens are allowed
  ///
  /// If set then it will yield Invalid token with E15 in place of EOC one if
  /// “EOC” is something like "|". It is fine with emitting EOC at the end of
  /// string still, with or without this flag set.
  kELFlagForbidEOC = (1 << 4),
  // XXX Whenever you add a new flag, alter klee_assume() statement in
  //     viml_expressions_lexer.c.
} LexExprFlags;

/// Expression AST node type
typedef enum {
  kExprNodeMissing = 0,
  kExprNodeOpMissing,
  kExprNodeTernary,  ///< Ternary operator.
  kExprNodeTernaryValue,  ///< Ternary operator, colon.
  kExprNodeRegister,  ///< Register.
  kExprNodeSubscript,  ///< Subscript.
  kExprNodeListLiteral,  ///< List literal.
  kExprNodeUnaryPlus,
  kExprNodeBinaryPlus,
  kExprNodeNested,  ///< Nested parenthesised expression.
  kExprNodeCall,  ///< Function call.
  /// Plain identifier: simple variable/function name
  ///
  /// Looks like "string", "g:Foo", etc: consists from a single
  /// kExprLexPlainIdentifier token.
  kExprNodePlainIdentifier,
  /// Plain dictionary key, for use with kExprNodeConcatOrSubscript
  kExprNodePlainKey,
  /// Complex identifier: variable/function name with curly braces
  kExprNodeComplexIdentifier,
  /// Figure brace expression which is not yet known
  ///
  /// May resolve to any of kExprNodeDictLiteral, kExprNodeLambda or
  /// kExprNodeCurlyBracesIdentifier.
  kExprNodeUnknownFigure,
  kExprNodeLambda,  ///< Lambda.
  kExprNodeDictLiteral,  ///< Dictionary literal.
  kExprNodeCurlyBracesIdentifier,  ///< Part of the curly braces name.
  kExprNodeComma,  ///< Comma “operator”.
  kExprNodeColon,  ///< Colon “operator”.
  kExprNodeArrow,  ///< Arrow “operator”.
  kExprNodeComparison,  ///< Various comparison operators.
  /// Concat operator
  ///
  /// To be only used in cases when it is known for sure it is not a subscript.
  kExprNodeConcat,
  /// Concat or subscript operator
  ///
  /// For cases when it is not obvious whether expression is a concat or
  /// a subscript. May only have either number or plain identifier as the second
  /// child. To make it easier to avoid curly braces in place of
  /// kExprNodePlainIdentifier node kExprNodePlainKey is used.
  kExprNodeConcatOrSubscript,
  kExprNodeInteger,  ///< Integral number.
  kExprNodeFloat,  ///< Floating-point number.
  kExprNodeSingleQuotedString,
  kExprNodeDoubleQuotedString,
  kExprNodeOr,
  kExprNodeAnd,
  kExprNodeUnaryMinus,
  kExprNodeBinaryMinus,
  kExprNodeNot,
  kExprNodeMultiplication,
  kExprNodeDivision,
  kExprNodeMod,
  kExprNodeOption,
  kExprNodeEnvironment,
  kExprNodeAssignment,
  // XXX When modifying this list also modify east_node_type_tab both in parser
  //     and in tests, and you most likely will also have to alter list of
  //     highlight groups stored in highlight_init_cmdline variable.
} ExprASTNodeType;

typedef struct expr_ast_node ExprASTNode;

/// Structure representing one AST node
struct expr_ast_node {
  ExprASTNodeType type;  ///< Node type.
  /// Node children: e.g. for 1 + 2 nodes 1 and 2 will be children of +.
  ExprASTNode *children;
  /// Next node: e.g. for 1 + 2 child nodes 1 and 2 are put into a single-linked
  /// list: `(+)->children` references only node 1, node 2 is in
  /// `(+)->children->next`.
  ExprASTNode *next;
  ParserPosition start;
  size_t len;
  union {
    struct {
      int name;  ///< Register name, may be -1 if name not present.
    } reg;  ///< For kExprNodeRegister.
    struct {
      /// Which nodes UnknownFigure can’t possibly represent.
      struct {
        /// True if UnknownFigure may actually represent dictionary literal.
        bool allow_dict;
        /// True if UnknownFigure may actually represent lambda.
        bool allow_lambda;
        /// True if UnknownFigure may actually be part of curly braces name.
        bool allow_ident;
      } type_guesses;
      /// Highlight chunk index, used for rehighlighting if needed
      size_t opening_hl_idx;
    } fig;  ///< For kExprNodeUnknownFigure.
    struct {
      ExprVarScope scope;  ///< Scope character or 0 if not present.
      /// Actual identifier without scope.
      ///
      /// Points to inside parser reader state.
      const char *ident;
      size_t ident_len;  ///< Actual identifier length.
    } var;  ///< For kExprNodePlainIdentifier and kExprNodePlainKey.
    struct {
      bool got_colon;  ///< True if colon was seen.
    } ter;  ///< For kExprNodeTernaryValue.
    struct {
      ExprComparisonType type;  ///< Comparison type.
      ExprCaseCompareStrategy ccs;  ///< Case comparison strategy.
      bool inv;  ///< True if comparison is to be inverted.
    } cmp;  ///< For kExprNodeComparison.
    struct {
      uvarnumber_T value;
    } num;  ///< For kExprNodeInteger.
    struct {
      float_T value;
    } flt;  ///< For kExprNodeFloat.
    struct {
      char *value;
      size_t size;
    } str;  ///< For kExprNodeSingleQuotedString and
            ///< kExprNodeDoubleQuotedString.
    struct {
      const char *ident;  ///< Option name start.
      size_t ident_len;  ///< Option name length.
      ExprOptScope scope;  ///< Option scope: &l:, &g: or not specified.
    } opt;  ///< For kExprNodeOption.
    struct {
      const char *ident;  ///< Environment variable name start.
      size_t ident_len;  ///< Environment variable name length.
    } env;  ///< For kExprNodeEnvironment.
    struct {
      ExprAssignmentType type;
    } ass;  ///< For kExprNodeAssignment
  } data;
};

enum ExprParserFlags {
  /// Allow multiple expressions in a row: e.g. for :echo
  ///
  /// Parser will still parse only one of them though.
  kExprFlagsMulti = (1 << 0),
  /// Allow NL, NUL and bar to be EOC
  ///
  /// When parsing expressions input by user bar is assumed to be a binary
  /// operator and other two are spacings.
  kExprFlagsDisallowEOC = (1 << 1),
  /// Parse :let argument
  ///
  /// That mean that top level node must be an assignment and first nodes
  /// belong to lvalues.
  kExprFlagsParseLet = (1 << 2),
  // XXX whenever you add a new flag, alter klee_assume() statement in
  //     viml_expressions_parser.c, nvim_parse_expression() flags parsing
  //     alongside with its documentation and flag sets in check_parsing()
  //     function in expressions parser functional and unit tests.
};

/// AST error definition
typedef struct {
  /// Error message. Must contain a single printf format atom: %.*s.
  const char *msg;
  /// Error message argument: points to the location of the error.
  const char *arg;
  /// Message argument length: length till the end of string.
  int arg_len;
} ExprASTError;

/// Structure representing complety AST for one expression
typedef struct {
  /// When AST is not correct this message will be printed.
  ///
  /// Uses `semsg(msg, arg_len, arg);`, `msg` is assumed to contain only `%.*s`.
  ExprASTError err;
  /// Root node of the AST.
  ExprASTNode *root;
} ExprAST;

/// Array mapping ExprASTNodeType to maximum amount of children node may have
extern const uint8_t node_maxchildren[];

/// Array mapping ExprASTNodeType values to their stringified versions
extern const char *const east_node_type_tab[];

/// Array mapping ExprComparisonType values to their stringified versions
extern const char *const eltkn_cmp_type_tab[];

/// Array mapping ExprCaseCompareStrategy values to their stringified versions
extern const char *const ccs_tab[];

/// Array mapping ExprAssignmentType values to their stringified versions
extern const char *const expr_asgn_type_tab[];

#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "viml/parser/expressions.h.generated.h"
#endif

#endif  // NVIM_VIML_PARSER_EXPRESSIONS_H