diff options
author | ZyX <kp-pav@yandex.ru> | 2017-10-09 02:55:56 +0300 |
---|---|---|
committer | ZyX <kp-pav@yandex.ru> | 2017-10-15 19:13:52 +0300 |
commit | fa3cfc0dd54df125a1dbabccda47a5f45dc483ae (patch) | |
tree | be4c707c2bdfda3fab439d48b5ed9a5a93eb4856 | |
parent | af38cea133f5ebb67208cedd289e408cd1dad15a (diff) | |
download | rneovim-fa3cfc0dd54df125a1dbabccda47a5f45dc483ae.tar.gz rneovim-fa3cfc0dd54df125a1dbabccda47a5f45dc483ae.tar.bz2 rneovim-fa3cfc0dd54df125a1dbabccda47a5f45dc483ae.zip |
viml/parser/expressions: Finish parser
Note: formatc.lua was unable to swallow some newer additions to ExprASTNodeType
(specifically `kExprNodeOr = '|'` and probably something else), so all `= …`
were dropped: in any case they only were there in order to not bother updating
viml_pexpr_debug_print_ast_node and since it is now known all nodes which will
be present it is not much of an issue.
-rw-r--r-- | src/nvim/viml/parser/expressions.c | 369 | ||||
-rw-r--r-- | src/nvim/viml/parser/expressions.h | 115 | ||||
-rw-r--r-- | src/nvim/viml/parser/parser.h | 2 | ||||
-rw-r--r-- | test/symbolic/klee/viml_expressions_lexer.c | 1 | ||||
-rw-r--r-- | test/unit/viml/expressions/lexer_spec.lua | 6 | ||||
-rw-r--r-- | test/unit/viml/expressions/parser_spec.lua | 19 | ||||
-rw-r--r-- | test/unit/viml/helpers.lua | 1 |
7 files changed, 355 insertions, 158 deletions
diff --git a/src/nvim/viml/parser/expressions.c b/src/nvim/viml/parser/expressions.c index 3f30fe2a0e..75fcb17bf6 100644 --- a/src/nvim/viml/parser/expressions.c +++ b/src/nvim/viml/parser/expressions.c @@ -361,11 +361,12 @@ LexExprToken viml_pexpr_next_token(ParserState *const pstate, const int flags) // Scope: `s:`, etc. } else if (ret.len == 1 && pline.size > 1 - && strchr("sgvbwtla", schar) != NULL + && memchr(EXPR_VAR_SCOPE_LIST, schar, + sizeof(EXPR_VAR_SCOPE_LIST)) != NULL && pline.data[ret.len] == ':' && !(flags & kELFlagForbidScope)) { ret.len++; - ret.data.var.scope = schar; + ret.data.var.scope = (ExprVarScope)schar; CHARREG(kExprLexPlainIdentifier, ISWORD_OR_AUTOLOAD); ret.data.var.autoload = ( memchr(pline.data + 2, AUTOLOAD_CHAR, ret.len - 2) @@ -408,14 +409,13 @@ LexExprToken viml_pexpr_next_token(ParserState *const pstate, const int flags) ret.type = kExprLexOption; if (pline.size > 2 && pline.data[2] == ':' - && strchr("gl", pline.data[1]) != NULL) { + && memchr(EXPR_OPT_SCOPE_LIST, pline.data[1], + sizeof(EXPR_OPT_SCOPE_LIST)) != NULL) { ret.len += 2; - ret.data.opt.scope = (pline.data[1] == 'g' - ? kExprLexOptGlobal - : kExprLexOptLocal); + ret.data.opt.scope = (ExprOptScope)pline.data[1]; ret.data.opt.name = pline.data + 3; } else { - ret.data.opt.scope = kExprLexOptUnspecified; + ret.data.opt.scope = kExprOptScopeUnspecified; ret.data.opt.name = pline.data + 1; } const char *p = ret.data.opt.name; @@ -637,9 +637,9 @@ static const char *const eltkn_mul_type_tab[] = { }; static const char *const eltkn_opt_scope_tab[] = { - [kExprLexOptUnspecified] = "Unspecified", - [kExprLexOptGlobal] = "Global", - [kExprLexOptLocal] = "Local", + [kExprOptScopeUnspecified] = "Unspecified", + [kExprOptScopeGlobal] = "Global", + [kExprOptScopeLocal] = "Local", }; /// Represent `int` character as a string @@ -990,67 +990,25 @@ static inline ExprASTNode *viml_pexpr_new_node(const ExprASTNodeType type) return ret; } -static const ExprOpLvl node_type_to_op_lvl[] = { - [kExprNodeMissing] = kEOpLvlInvalid, - [kExprNodeOpMissing] = kEOpLvlMultiplication, +static struct { + ExprOpLvl lvl; + ExprOpAssociativity ass; +} node_type_to_node_props[] = { + [kExprNodeMissing] = { kEOpLvlInvalid, kEOpAssNo, }, + [kExprNodeOpMissing] = { kEOpLvlMultiplication, kEOpAssNo }, - [kExprNodeNested] = kEOpLvlParens, + [kExprNodeNested] = { kEOpLvlParens, kEOpAssNo }, // Note: below nodes are kEOpLvlSubscript for “binary operator” itself, but // kEOpLvlParens when it comes to inside the parenthesis. - [kExprNodeCall] = kEOpLvlParens, - [kExprNodeSubscript] = kEOpLvlParens, + [kExprNodeCall] = { kEOpLvlParens, kEOpAssNo }, + [kExprNodeSubscript] = { kEOpLvlParens, kEOpAssNo }, - [kExprNodeUnknownFigure] = kEOpLvlParens, - [kExprNodeLambda] = kEOpLvlParens, - [kExprNodeDictLiteral] = kEOpLvlParens, - [kExprNodeListLiteral] = kEOpLvlParens, + [kExprNodeUnknownFigure] = { kEOpLvlParens, kEOpAssLeft }, + [kExprNodeLambda] = { kEOpLvlParens, kEOpAssNo }, + [kExprNodeDictLiteral] = { kEOpLvlParens, kEOpAssNo }, + [kExprNodeListLiteral] = { kEOpLvlParens, kEOpAssNo }, - [kExprNodeArrow] = kEOpLvlArrow, - - [kExprNodeComma] = kEOpLvlComma, - - [kExprNodeColon] = kEOpLvlColon, - - [kExprNodeTernary] = kEOpLvlTernary, - - [kExprNodeTernaryValue] = kEOpLvlTernaryValue, - - [kExprNodeComparison] = kEOpLvlComparison, - - [kExprNodeBinaryPlus] = kEOpLvlAddition, - [kExprNodeConcat] = kEOpLvlAddition, - - [kExprNodeUnaryPlus] = kEOpLvlUnary, - - [kExprNodeConcatOrSubscript] = kEOpLvlSubscript, - - [kExprNodeCurlyBracesIdentifier] = kEOpLvlComplexIdentifier, - - [kExprNodeComplexIdentifier] = kEOpLvlValue, - [kExprNodePlainIdentifier] = kEOpLvlValue, - [kExprNodePlainKey] = kEOpLvlValue, - [kExprNodeRegister] = kEOpLvlValue, - [kExprNodeInteger] = kEOpLvlValue, - [kExprNodeFloat] = kEOpLvlValue, -}; - -static const ExprOpAssociativity node_type_to_op_ass[] = { - [kExprNodeMissing] = kEOpAssNo, - [kExprNodeOpMissing] = kEOpAssNo, - - [kExprNodeNested] = kEOpAssNo, - [kExprNodeCall] = kEOpAssNo, - [kExprNodeSubscript] = kEOpAssNo, - - [kExprNodeUnknownFigure] = kEOpAssLeft, - [kExprNodeLambda] = kEOpAssNo, - [kExprNodeDictLiteral] = kEOpAssNo, - [kExprNodeListLiteral] = kEOpAssNo, - - // Does not really matter. - [kExprNodeArrow] = kEOpAssNo, - - [kExprNodeColon] = kEOpAssNo, + [kExprNodeArrow] = { kEOpLvlArrow, kEOpAssNo }, // Right associativity for comma because this means easier access to arguments // list, etc: for "[a, b, c, d]" you can access "a" in one step if it is @@ -1059,29 +1017,48 @@ static const ExprOpAssociativity node_type_to_op_ass[] = { // traverse all three comma() structures. And with comma operator (including // actual comma operator from C which is not present in VimL) nobody cares // about associativity, only about order of execution. - [kExprNodeComma] = kEOpAssRight, + [kExprNodeComma] = { kEOpLvlComma, kEOpAssRight }, + + // Colons are not eligible for chaining, so nobody cares about associativity. + [kExprNodeColon] = { kEOpLvlColon, kEOpAssNo }, + + [kExprNodeTernary] = { kEOpLvlTernary, kEOpAssRight }, - [kExprNodeTernary] = kEOpAssRight, + [kExprNodeOr] = { kEOpLvlOr, kEOpAssLeft }, - [kExprNodeTernaryValue] = kEOpAssRight, + [kExprNodeAnd] = { kEOpLvlAnd, kEOpAssLeft }, - [kExprNodeComparison] = kEOpAssRight, + [kExprNodeTernaryValue] = { kEOpLvlTernaryValue, kEOpAssRight }, - [kExprNodeBinaryPlus] = kEOpAssLeft, - [kExprNodeConcat] = kEOpAssLeft, + [kExprNodeComparison] = { kEOpLvlComparison, kEOpAssRight }, - [kExprNodeUnaryPlus] = kEOpAssNo, + [kExprNodeBinaryPlus] = { kEOpLvlAddition, kEOpAssLeft }, + [kExprNodeBinaryMinus] = { kEOpLvlAddition, kEOpAssLeft }, + [kExprNodeConcat] = { kEOpLvlAddition, kEOpAssLeft }, - [kExprNodeConcatOrSubscript] = kEOpAssLeft, + [kExprNodeMultiplication] = { kEOpLvlMultiplication, kEOpAssLeft }, + [kExprNodeDivision] = { kEOpLvlMultiplication, kEOpAssLeft }, + [kExprNodeMod] = { kEOpLvlMultiplication, kEOpAssLeft }, - [kExprNodeCurlyBracesIdentifier] = kEOpAssLeft, + [kExprNodeUnaryPlus] = { kEOpLvlUnary, kEOpAssNo }, + [kExprNodeUnaryMinus] = { kEOpLvlUnary, kEOpAssNo }, + [kExprNodeNot] = { kEOpLvlUnary, kEOpAssNo }, - [kExprNodeComplexIdentifier] = kEOpAssLeft, - [kExprNodePlainIdentifier] = kEOpAssNo, - [kExprNodePlainKey] = kEOpAssNo, - [kExprNodeRegister] = kEOpAssNo, - [kExprNodeInteger] = kEOpAssNo, - [kExprNodeFloat] = kEOpAssNo, + [kExprNodeConcatOrSubscript] = { kEOpLvlSubscript, kEOpAssLeft }, + + [kExprNodeCurlyBracesIdentifier] = { kEOpLvlComplexIdentifier, kEOpAssLeft }, + + [kExprNodeComplexIdentifier] = { kEOpLvlValue, kEOpAssLeft }, + + [kExprNodePlainIdentifier] = { kEOpLvlValue, kEOpAssNo }, + [kExprNodePlainKey] = { kEOpLvlValue, kEOpAssNo }, + [kExprNodeRegister] = { kEOpLvlValue, kEOpAssNo }, + [kExprNodeInteger] = { kEOpLvlValue, kEOpAssNo }, + [kExprNodeFloat] = { kEOpLvlValue, kEOpAssNo }, + [kExprNodeDoubleQuotedString] = { kEOpLvlValue, kEOpAssNo }, + [kExprNodeSingleQuotedString] = { kEOpLvlValue, kEOpAssNo }, + [kExprNodeOption] = { kEOpLvlValue, kEOpAssNo }, + [kExprNodeEnvironment] = { kEOpLvlValue, kEOpAssNo }, }; /// Get AST node priority level @@ -1094,7 +1071,7 @@ static const ExprOpAssociativity node_type_to_op_ass[] = { static inline ExprOpLvl node_lvl(const ExprASTNode node) FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT { - return node_type_to_op_lvl[node.type]; + return node_type_to_node_props[node.type].lvl; } /// Get AST node associativity, to be used for operator nodes primary @@ -1107,7 +1084,7 @@ static inline ExprOpLvl node_lvl(const ExprASTNode node) static inline ExprOpAssociativity node_ass(const ExprASTNode node) FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT { - return node_type_to_op_ass[node.type]; + return node_type_to_node_props[node.type].ass; } /// Handle binary operator @@ -1837,7 +1814,7 @@ viml_pexpr_parse_process_token: is_concat_or_subscript && (cur_token.type == kExprLexPlainIdentifier ? (!cur_token.data.var.autoload - && cur_token.data.var.scope == 0) + && cur_token.data.var.scope == kExprVarScopeMissing) : (cur_token.type == kExprLexNumber)) && prev_token.type != kExprLexSpacing); if (is_concat_or_subscript && !node_is_key) { @@ -1856,7 +1833,7 @@ viml_pexpr_parse_process_token: && tok_type != kExprLexArrow) || (want_node == kENodeArgument && !(cur_token.type == kExprLexPlainIdentifier - && cur_token.data.var.scope == 0 + && cur_token.data.var.scope == kExprVarScopeMissing && !cur_token.data.var.autoload) && tok_type != kExprLexArrow)) { lambda_node->data.fig.type_guesses.allow_lambda = false; @@ -1885,6 +1862,8 @@ viml_pexpr_parse_process_token: || want_node == kENodeArgumentSeparator || want_node == kENodeArgument); switch (tok_type) { + case kExprLexMissing: + case kExprLexSpacing: case kExprLexEOC: { assert(false); } @@ -1894,31 +1873,111 @@ viml_pexpr_parse_process_token: goto viml_pexpr_parse_process_token; } case kExprLexRegister: { - if (want_node == kENodeValue) { - NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeRegister); - cur_node->data.reg.name = cur_token.data.reg.name; - *top_node_p = cur_node; - want_node = kENodeOperator; - HL_CUR_TOKEN(Register); - } else { + if (want_node == kENodeOperator) { // Register in operator position: e.g. @a @a OP_MISSING; } + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeRegister); + cur_node->data.reg.name = cur_token.data.reg.name; + *top_node_p = cur_node; + want_node = kENodeOperator; + HL_CUR_TOKEN(Register); break; } - case kExprLexPlus: { - if (want_node == kENodeValue) { - // Value level: assume unary plus - NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeUnaryPlus); - *top_node_p = cur_node; - kvi_push(ast_stack, &cur_node->children); - HL_CUR_TOKEN(UnaryPlus); - } else { - NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeBinaryPlus); - ADD_OP_NODE(cur_node); - HL_CUR_TOKEN(BinaryPlus); +#define SIMPLE_UB_OP(op) \ + case kExprLex##op: { \ + if (want_node == kENodeValue) { \ + /* Value level: assume unary operator. */ \ + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeUnary##op); \ + *top_node_p = cur_node; \ + kvi_push(ast_stack, &cur_node->children); \ + HL_CUR_TOKEN(Unary##op); \ + } else { \ + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeBinary##op); \ + ADD_OP_NODE(cur_node); \ + HL_CUR_TOKEN(Binary##op); \ + } \ + want_node = kENodeValue; \ + break; \ + } + SIMPLE_UB_OP(Plus) + SIMPLE_UB_OP(Minus) +#undef SIMPLE_UB_OP +#define SIMPLE_B_OP(op, msg) \ + case kExprLex##op: { \ + ADD_VALUE_IF_MISSING(_("E15: Unexpected " msg ": %.*s")); \ + NEW_NODE_WITH_CUR_POS(cur_node, kExprNode##op); \ + HL_CUR_TOKEN(op); \ + ADD_OP_NODE(cur_node); \ + break; \ + } + SIMPLE_B_OP(Or, "or operator") + SIMPLE_B_OP(And, "and operator") +#undef SIMPLE_B_OP + case kExprLexMultiplication: { + ADD_VALUE_IF_MISSING( + _("E15: Unexpected multiplication-like operator: %.*s")); + switch (cur_token.data.mul.type) { +#define MUL_OP(lex_op_tail, node_op_tail) \ + case kExprLexMul##lex_op_tail: { \ + NEW_NODE_WITH_CUR_POS(cur_node, kExprNode##node_op_tail); \ + HL_CUR_TOKEN(node_op_tail); \ + break; \ + } + MUL_OP(Mul, Multiplication) + MUL_OP(Div, Division) + MUL_OP(Mod, Mod) +#undef MUL_OP } - want_node = kENodeValue; + ADD_OP_NODE(cur_node); + break; + } + case kExprLexOption: { + if (want_node == kENodeOperator) { + OP_MISSING; + } + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeOption); + cur_node->data.opt.ident = cur_token.data.opt.name; + cur_node->data.opt.ident_len = cur_token.data.opt.len; + cur_node->data.opt.scope = cur_token.data.opt.scope; + *top_node_p = cur_node; + want_node = kENodeOperator; + viml_parser_highlight(pstate, cur_token.start, 1, HL(OptionSigil)); + const size_t scope_shift = ( + cur_token.data.opt.scope == kExprOptScopeUnspecified ? 0 : 2); + if (scope_shift) { + viml_parser_highlight(pstate, shifted_pos(cur_token.start, 1), 1, + HL(OptionScope)); + viml_parser_highlight(pstate, shifted_pos(cur_token.start, 2), 1, + HL(OptionScopeDelimiter)); + } + viml_parser_highlight( + pstate, shifted_pos(cur_token.start, scope_shift + 1), + cur_token.len - scope_shift + 1, HL(Option)); + break; + } + case kExprLexEnv: { + if (want_node == kENodeOperator) { + OP_MISSING; + } + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeEnvironment); + cur_node->data.env.ident = pline.data + cur_token.start.col + 1; + cur_node->data.env.ident_len = cur_token.len - 1; + *top_node_p = cur_node; + want_node = kENodeOperator; + viml_parser_highlight(pstate, cur_token.start, 1, HL(EnvironmentSigil)); + viml_parser_highlight(pstate, shifted_pos(cur_token.start, 1), + cur_token.len - 1, HL(Environment)); + break; + } + case kExprLexNot: { + if (want_node == kENodeOperator) { + OP_MISSING; + } + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeNot); + *top_node_p = cur_node; + kvi_push(ast_stack, &cur_node->children); + HL_CUR_TOKEN(Not); break; } case kExprLexComparison: { @@ -2359,9 +2418,8 @@ viml_pexpr_parse_figure_brace_closing_error: ? kExprNodePlainKey : kExprNodePlainIdentifier)); cur_node->data.var.scope = cur_token.data.var.scope; - const size_t scope_shift = (cur_token.data.var.scope == 0 - ? 0 - : 2); + const size_t scope_shift = ( + cur_token.data.var.scope == kExprVarScopeMissing ? 0 : 2); cur_node->data.var.ident = (pline.data + cur_token.start.col + scope_shift); cur_node->data.var.ident_len = cur_token.len - scope_shift; @@ -2373,16 +2431,14 @@ viml_pexpr_parse_figure_brace_closing_error: viml_parser_highlight(pstate, shifted_pos(cur_token.start, 1), 1, HL(IdentifierScopeDelimiter)); } - if (scope_shift < cur_token.len) { - viml_parser_highlight(pstate, shifted_pos(cur_token.start, - scope_shift), - cur_token.len - scope_shift, - (node_is_key - ? HL(IdentifierKey) - : HL(Identifier))); - } + viml_parser_highlight(pstate, shifted_pos(cur_token.start, + scope_shift), + cur_token.len - scope_shift, + (node_is_key + ? HL(IdentifierKey) + : HL(Identifier))); } else { - if (cur_token.data.var.scope == 0) { + if (cur_token.data.var.scope == kExprVarScopeMissing) { ADD_IDENT( do { NEW_NODE_WITH_CUR_POS(cur_node, kExprNodePlainIdentifier); @@ -2606,9 +2662,85 @@ viml_pexpr_parse_end: cur_node->start); break; } - case kExprNodeBinaryPlus: + case kExprNodeListLiteral: { + // For whatever reason "[1" yields "E696: Missing comma in list" error + // in Vim while "[1," yields E697. + east_set_error( + pstate, &ast.err, + _("E697: Missing end of List ']': %.*s"), + cur_node->start); + break; + } + case kExprNodeDictLiteral: { + // Same problem like with list literal with E722 (missing comma) vs + // E723, but additionally just "{" yields only E15. + east_set_error( + pstate, &ast.err, + _("E723: Missing end of Dictionary '}': %.*s"), + cur_node->start); + break; + } + case kExprNodeUnknownFigure: { + east_set_error( + pstate, &ast.err, + _("E15: Missing closing figure brace: %.*s"), + cur_node->start); + break; + } + case kExprNodeLambda: { + east_set_error( + pstate, &ast.err, + _("E15: Missing closing figure brace for lambda: %.*s"), + cur_node->start); + break; + } + case kExprNodeCurlyBracesIdentifier: { + // Until trailing "}" it is impossible to distinguish curly braces + // identifier and dictionary, so it must not appear in the stack like + // this. + assert(false); + } + case kExprNodeInteger: + case kExprNodeFloat: + case kExprNodeSingleQuotedString: + case kExprNodeDoubleQuotedString: + case kExprNodeOption: + case kExprNodeEnvironment: + case kExprNodeRegister: + case kExprNodePlainIdentifier: + case kExprNodePlainKey: { + // These are plain values and not containers, for them it should only + // be possible to show up in the topmost stack element, but it was + // unconditionally popped at the start. + assert(false); + } + case kExprNodeComma: + case kExprNodeColon: + case kExprNodeArrow: { + // It is actually only valid inside something else, but everything + // where one of the above is valid requires to be closed and thus is + // to be caught later. + break; + } + case kExprNodeConcatOrSubscript: + case kExprNodeComplexIdentifier: + case kExprNodeSubscript: { + // FIXME: Investigate whether above are OK to be present in the stack. + break; + } + case kExprNodeMod: + case kExprNodeDivision: + case kExprNodeMultiplication: + case kExprNodeNot: + case kExprNodeAnd: + case kExprNodeOr: + case kExprNodeConcat: + case kExprNodeComparison: + case kExprNodeUnaryMinus: case kExprNodeUnaryPlus: - case kExprNodeRegister: { + case kExprNodeBinaryMinus: + case kExprNodeTernary: + case kExprNodeBinaryPlus: { // It is OK to see these in the stack. break; } @@ -2621,7 +2753,6 @@ viml_pexpr_parse_end: } break; } - // TODO(ZyX-I): handle other values } } } diff --git a/src/nvim/viml/parser/expressions.h b/src/nvim/viml/parser/expressions.h index a09cdde4c0..0198852bed 100644 --- a/src/nvim/viml/parser/expressions.h +++ b/src/nvim/viml/parser/expressions.h @@ -61,6 +61,36 @@ typedef enum { kExprCmpIdentical, ///< `is` or `isnot` } ExprComparisonType; +/// All possible option scopes +typedef enum { + kExprOptScopeUnspecified = 0, + kExprOptScopeGlobal = 'g', + kExprOptScopeLocal = 'l', +} ExprOptScope; + +#define EXPR_OPT_SCOPE_LIST \ + ((char *)(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; @@ -96,15 +126,11 @@ typedef struct { struct { const char *name; ///< Option name start. size_t len; ///< Option name length. - enum { - kExprLexOptUnspecified = 0, - kExprLexOptGlobal = 1, - kExprLexOptLocal = 2, - } scope; ///< Option scope: &l:, &g: or not specified. + ExprOptScope scope; ///< Option scope: &l:, &g: or not specified. } opt; ///< Option properties. struct { - int scope; ///< Scope character or 0 if not present. + ExprVarScope scope; ///< Scope character or 0 if not present. bool autoload; ///< Has autoload characters. } var; ///< For kExprLexPlainIdentifier @@ -150,53 +176,63 @@ typedef enum { /// Expression AST node type typedef enum { - kExprNodeMissing = 'X', - kExprNodeOpMissing = '_', - kExprNodeTernary = '?', ///< Ternary operator. - kExprNodeTernaryValue = 'C', ///< Ternary operator, colon. - kExprNodeRegister = '@', ///< Register. - kExprNodeSubscript = 's', ///< Subscript. - kExprNodeListLiteral = 'l', ///< List literal. - kExprNodeUnaryPlus = 'p', - kExprNodeBinaryPlus = '+', - kExprNodeNested = 'e', ///< Nested parenthesised expression. - kExprNodeCall = 'c', ///< Function call. + 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 = 'i', + kExprNodePlainIdentifier, /// Plain dictionary key, for use with kExprNodeConcatOrSubscript - kExprNodePlainKey = 'k', + kExprNodePlainKey, /// Complex identifier: variable/function name with curly braces - kExprNodeComplexIdentifier = 'I', + kExprNodeComplexIdentifier, /// Figure brace expression which is not yet known /// /// May resolve to any of kExprNodeDictLiteral, kExprNodeLambda or /// kExprNodeCurlyBracesIdentifier. - kExprNodeUnknownFigure = '{', - kExprNodeLambda = '\\', ///< Lambda. - kExprNodeDictLiteral = 'd', ///< Dictionary literal. - kExprNodeCurlyBracesIdentifier= '}', ///< Part of the curly braces name. - kExprNodeComma = ',', ///< Comma “operator”. - kExprNodeColon = ':', ///< Colon “operator”. - kExprNodeArrow = '>', ///< Arrow “operator”. - kExprNodeComparison = '=', ///< Various comparison operators. + 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 = '.', + 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 = 'S', - kExprNodeInteger = '0', ///< Integral number. - kExprNodeFloat = '1', ///< Floating-point number. - kExprNodeSingleQuotedString = '\'', - kExprNodeDoubleQuotedString = '"', + kExprNodeConcatOrSubscript, + kExprNodeInteger, ///< Integral number. + kExprNodeFloat, ///< Floating-point number. + kExprNodeSingleQuotedString, + kExprNodeDoubleQuotedString, + kExprNodeOr, + kExprNodeAnd, + kExprNodeUnaryMinus, + kExprNodeBinaryMinus, + kExprNodeNot, + kExprNodeMultiplication, + kExprNodeDivision, + kExprNodeMod, + kExprNodeOption, + kExprNodeEnvironment, } ExprASTNodeType; typedef struct expr_ast_node ExprASTNode; @@ -230,7 +266,7 @@ struct expr_ast_node { size_t opening_hl_idx; } fig; ///< For kExprNodeUnknownFigure. struct { - int scope; ///< Scope character or 0 if not present. + ExprVarScope scope; ///< Scope character or 0 if not present. /// Actual identifier without scope. /// /// Points to inside parser reader state. @@ -256,6 +292,15 @@ struct expr_ast_node { 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. } data; }; diff --git a/src/nvim/viml/parser/parser.h b/src/nvim/viml/parser/parser.h index a17edac403..10ced57977 100644 --- a/src/nvim/viml/parser/parser.h +++ b/src/nvim/viml/parser/parser.h @@ -173,7 +173,7 @@ static inline void viml_parser_highlight(ParserState *const pstate, const size_t len, const char *const group) { - if (pstate->colors == NULL) { + if (pstate->colors == NULL || len == 0) { return; } // TODO(ZyX-I): May do some assert() sanitizing here. diff --git a/test/symbolic/klee/viml_expressions_lexer.c b/test/symbolic/klee/viml_expressions_lexer.c index cddc1cb2f1..ee7dc312e9 100644 --- a/test/symbolic/klee/viml_expressions_lexer.c +++ b/test/symbolic/klee/viml_expressions_lexer.c @@ -17,6 +17,7 @@ #include "nvim/charset.c" #include "nvim/garray.c" #include "nvim/gettext.c" +#include "nvim/keymap.c" #include "nvim/viml/parser/expressions.c" #define INPUT_SIZE 7 diff --git a/test/unit/viml/expressions/lexer_spec.lua b/test/unit/viml/expressions/lexer_spec.lua index f180d8ceff..674b1b37db 100644 --- a/test/unit/viml/expressions/lexer_spec.lua +++ b/test/unit/viml/expressions/lexer_spec.lua @@ -62,9 +62,9 @@ child_call_once(function() } eltkn_opt_scope_tab = { - [tonumber(lib.kExprLexOptUnspecified)] = 'Unspecified', - [tonumber(lib.kExprLexOptGlobal)] = 'Global', - [tonumber(lib.kExprLexOptLocal)] = 'Local', + [tonumber(lib.kExprOptScopeUnspecified)] = 'Unspecified', + [tonumber(lib.kExprOptScopeGlobal)] = 'Global', + [tonumber(lib.kExprOptScopeLocal)] = 'Local', } end) diff --git a/test/unit/viml/expressions/parser_spec.lua b/test/unit/viml/expressions/parser_spec.lua index ed77a7cba4..5041708a3e 100644 --- a/test/unit/viml/expressions/parser_spec.lua +++ b/test/unit/viml/expressions/parser_spec.lua @@ -93,6 +93,16 @@ make_enum_conv_tab(lib, { 'kExprNodeFloat', 'kExprNodeSingleQuotedString', 'kExprNodeDoubleQuotedString', + 'kExprNodeOr', + 'kExprNodeAnd', + 'kExprNodeUnaryMinus', + 'kExprNodeBinaryMinus', + 'kExprNodeNot', + 'kExprNodeMultiplication', + 'kExprNodeDivision', + 'kExprNodeMod', + 'kExprNodeOption', + 'kExprNodeEnvironment', }, 'kExprNode', function(ret) east_node_type_tab = ret end) local function conv_east_node_type(typ) @@ -149,6 +159,15 @@ local function eastnode2lua(pstate, eastnode, checked_nodes) local s = ffi.string(eastnode.data.str.value, eastnode.data.str.size) typ = format_string('%s(val=%q)', typ, s) end + elseif typ == 'Option' then + typ = ('%s(scope=%s,ident=%s)'):format( + typ, + tostring(intchar2lua(eastnode.data.opt.scope)), + ffi.string(eastnode.data.opt.ident, eastnode.data.opt.ident_len)) + elseif typ == 'Environment' then + typ = ('%s(ident=%s)'):format( + typ, + ffi.string(eastnode.data.env.ident, eastnode.data.env.ident_len)) end ret_str = typ .. ':' .. ret_str local can_simplify = true diff --git a/test/unit/viml/helpers.lua b/test/unit/viml/helpers.lua index 0b92be2654..c965cacb29 100644 --- a/test/unit/viml/helpers.lua +++ b/test/unit/viml/helpers.lua @@ -5,6 +5,7 @@ local cimport = helpers.cimport local kvi_new = helpers.kvi_new local kvi_init = helpers.kvi_init local conv_enum = helpers.conv_enum +local child_call_once = helpers.child_call_once local make_enum_conv_tab = helpers.make_enum_conv_tab local lib = cimport('./src/nvim/viml/parser/expressions.h') |