#pragma once #include #include #include #include "nvim/eval/typval_defs.h" #include "nvim/types_defs.h" #include "nvim/viml/parser/parser.h" struct expr_ast_node; // 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 complete 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