diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/api/vim.c | 39 | ||||
-rw-r--r-- | src/nvim/syntax.c | 20 | ||||
-rw-r--r-- | src/nvim/viml/parser/expressions.c | 239 | ||||
-rw-r--r-- | src/nvim/viml/parser/expressions.h | 40 |
4 files changed, 292 insertions, 46 deletions
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index bac19ef363..ce554d351c 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -900,14 +900,21 @@ typedef kvec_withinit_t(ExprASTConvStackItem, 16) ExprASTConvStack; /// Parse a VimL expression /// /// @param[in] expr Expression to parse. Is always treated as a single line. -/// @param[in] flags Flags: "m" if multiple expressions in a row are allowed -/// (only the first one will be parsed), "E" if EOC tokens -/// are not allowed (determines whether they will stop -/// parsing process or be recognized as an operator/space, -/// though also yielding an error). -/// -/// Use only "m" to parse like for "<C-r>=", only "E" to -/// parse like for ":echo", empty string for ":let". +/// @param[in] flags Flags: +/// +/// - "m" if multiple expressions in a row are allowed (only +/// the first one will be parsed), +/// - "E" if EOC tokens are not allowed (determines whether +/// they will stop parsing process or be recognized as an +/// operator/space, though also yielding an error). +/// - "l" when needing to start parsing with lvalues for +/// ":let" or ":for". +/// +/// Common flag sets: +/// - "m" to parse like for ":echo". +/// - "E" to parse like for "<C-r>=". +/// - empty string for ":call". +/// - "lm" to parse for ":let". /// @param[in] highlight If true, return value will also include "highlight" /// key containing array of 4-tuples (arrays) (Integer, /// Integer, Integer, String), where first three numbers @@ -964,6 +971,9 @@ typedef kvec_withinit_t(ExprASTConvStackItem, 16) ExprASTConvStack; /// value names from ExprCaseCompareStrategy, /// stringified without "kCCStrategy" prefix. Only /// present for "Comparison" nodes. +/// "augmentation": String, augmentation type for "Assignment" nodes. +/// Is either an empty string, "Add", "Subtract" or +/// "Concat" for "=", "+=", "-=" or ".=" respectively. /// "invert": Boolean, true if result of comparison needs to be /// inverted. Only present for "Comparison" nodes. /// "ivalue": Integer, integer value for "Integer" nodes. @@ -979,6 +989,7 @@ Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight, switch (flags.data[i]) { case 'm': { pflags |= kExprFlagsMulti; break; } case 'E': { pflags |= kExprFlagsDisallowEOC; break; } + case 'l': { pflags |= kExprFlagsParseLet; break; } case NUL: { api_set_error(err, kErrorTypeValidation, "Invalid flag: '\\0' (%u)", (unsigned)flags.data[i]); @@ -1114,6 +1125,7 @@ Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight, + (node->type == kExprNodeFloat) // "fvalue" + (node->type == kExprNodeDoubleQuotedString || node->type == kExprNodeSingleQuotedString) // "svalue" + + (node->type == kExprNodeAssignment) // "augmentation" + 0); Dictionary ret_node = { .items = xmalloc(ret_node_items_size * sizeof(ret_node.items[0])), @@ -1272,6 +1284,17 @@ Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight, }; break; } + case kExprNodeAssignment: { + const ExprAssignmentType asgn_type = node->data.ass.type; + ret_node->items[ret_node->size++] = (KeyValuePair) { + .key = STATIC_CSTR_TO_STRING("augmentation"), + .value = STRING_OBJ( + asgn_type == kExprAsgnPlain + ? (String)STRING_INIT + : cstr_to_string(expr_asgn_type_tab[asgn_type])), + }; + break; + } case kExprNodeMissing: case kExprNodeOpMissing: case kExprNodeTernary: diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index e0bf74567d..9f98b26905 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -6021,11 +6021,21 @@ static const char *highlight_init_dark[] = { }; static const char *highlight_init_cmdline[] = { + // XXX When modifying a list modify it in both valid and invalid halfs. + // TODO(ZyX-I): merge valid and invalid groups via a macros. + // NVimInternalError should appear only when highlighter has a bug. "NVimInternalError ctermfg=Red ctermbg=Red guifg=Red guibg=Red", // Highlight groups (links) used by parser: + "default link NVimAssignment Operator", + "default link NVimPlainAssignment NVimAssignment", + "default link NVimAugmentedAssignment NVimAssignment", + "default link NVimAssignmentWithAddition NVimAugmentedAssignment", + "default link NVimAssignmentWithSubtraction NVimAugmentedAssignment", + "default link NVimAssignmentWithConcatenation NVimAugmentedAssignment", + "default link NVimOperator Operator", "default link NVimUnaryOperator NVimOperator", @@ -6113,6 +6123,16 @@ static const char *highlight_init_cmdline[] = { "default link NVimInvalid Error", + "default link NVimInvalidAssignment NVimInvalid", + "default link NVimInvalidPlainAssignment NVimInvalidAssignment", + "default link NVimInvalidAugmentedAssignment NVimInvalidAssignment", + "default link NVimInvalidAssignmentWithAddition " + "NVimInvalidAugmentedAssignment", + "default link NVimInvalidAssignmentWithSubtraction " + "NVimInvalidAugmentedAssignment", + "default link NVimInvalidAssignmentWithConcatenation " + "NVimInvalidAugmentedAssignment", + "default link NVimInvalidOperator NVimInvalid", "default link NVimInvalidUnaryOperator NVimInvalidOperator", diff --git a/src/nvim/viml/parser/expressions.c b/src/nvim/viml/parser/expressions.c index e23c58bfd1..13f7131744 100644 --- a/src/nvim/viml/parser/expressions.c +++ b/src/nvim/viml/parser/expressions.c @@ -84,6 +84,10 @@ typedef enum { /// Just like parsing function arguments, but it is valid to be ended with an /// arrow only. kEPTLambdaArguments, + /// Assignment: parsing for :let + kEPTAssignment, + /// Single assignment: used when lists are not allowed (i.e. when nesting) + kEPTSingleAssignment, } ExprASTParseType; typedef kvec_withinit_t(ExprASTParseType, 4) ExprASTParseTypeStack; @@ -93,6 +97,7 @@ typedef enum { kEOpLvlInvalid = 0, kEOpLvlComplexIdentifier, kEOpLvlParens, + kEOpLvlAssignment, kEOpLvlArrow, kEOpLvlComma, kEOpLvlColon, @@ -217,8 +222,6 @@ LexExprToken viml_pexpr_next_token(ParserState *const pstate, const int flags) } CHAR(kExprLexQuestion, '?') CHAR(kExprLexColon, ':') - CHAR(kExprLexDot, '.') - CHAR(kExprLexPlus, '+') CHAR(kExprLexComma, ',') #undef CHAR @@ -532,12 +535,8 @@ LexExprToken viml_pexpr_next_token(ParserState *const pstate, const int flags) case '!': case '=': { if (pline.size == 1) { -viml_pexpr_next_token_invalid_comparison: - ret.type = (schar == '!' ? kExprLexNot : kExprLexInvalid); - if (ret.type == kExprLexInvalid) { - ret.data.err.msg = _("E15: Expected == or =~: %.*s"); - ret.data.err.type = kExprLexComparison; - } + ret.type = (schar == '!' ? kExprLexNot : kExprLexAssignment); + ret.data.ass.type = kExprAsgnPlain; break; } ret.type = kExprLexComparison; @@ -548,8 +547,11 @@ viml_pexpr_next_token_invalid_comparison: } else if (pline.data[1] == '~') { ret.data.cmp.type = kExprCmpMatches; ret.len++; + } else if (schar == '!') { + ret.type = kExprLexNot; } else { - goto viml_pexpr_next_token_invalid_comparison; + ret.type = kExprLexAssignment; + ret.data.ass.type = kExprAsgnPlain; } GET_CCS(ret, pline); break; @@ -571,17 +573,37 @@ viml_pexpr_next_token_invalid_comparison: break; } - // Minus sign or arrow from lambdas. + // Minus sign, arrow from lambdas or augmented assignment. case '-': { if (pline.size > 1 && pline.data[1] == '>') { ret.len++; ret.type = kExprLexArrow; + } else if (pline.size > 1 && pline.data[1] == '=') { + ret.len++; + ret.type = kExprLexAssignment; + ret.data.ass.type = kExprAsgnSubtract; } else { ret.type = kExprLexMinus; } break; } + // Sign or augmented assignment. +#define CHAR_OR_ASSIGN(ch, ch_type, ass_type) \ + case ch: { \ + if (pline.size > 1 && pline.data[1] == '=') { \ + ret.len++; \ + ret.type = kExprLexAssignment; \ + ret.data.ass.type = ass_type; \ + } else { \ + ret.type = ch_type; \ + } \ + break; \ + } + CHAR_OR_ASSIGN('+', kExprLexPlus, kExprAsgnAdd) + CHAR_OR_ASSIGN('.', kExprLexDot, kExprAsgnConcat) +#undef CHAR_OR_ASSIGN + // Expression end because Ex command ended. case NUL: case NL: { @@ -661,6 +683,7 @@ static const char *const eltkn_type_tab[] = { [kExprLexParenthesis] = "Parenthesis", [kExprLexComma] = "Comma", [kExprLexArrow] = "Arrow", + [kExprLexAssignment] = "Assignment", }; const char *const eltkn_cmp_type_tab[] = { @@ -671,6 +694,13 @@ const char *const eltkn_cmp_type_tab[] = { [kExprCmpIdentical] = "Identical", }; +const char *const expr_asgn_type_tab[] = { + [kExprAsgnPlain] = "Plain", + [kExprAsgnAdd] = "Add", + [kExprAsgnSubtract] = "Subtract", + [kExprAsgnConcat] = "Concat", +}; + const char *const ccs_tab[] = { [kCCStrategyUseOption] = "UseOption", [kCCStrategyMatchCase] = "MatchCase", @@ -732,6 +762,8 @@ const char *viml_pexpr_repr_token(const ParserState *const pstate, (int)token.data.cmp.inv) TKNARGS(kExprLexMultiplication, "(type=%s)", eltkn_mul_type_tab[token.data.mul.type]) + TKNARGS(kExprLexAssignment, "(type=%s)", + expr_asgn_type_tab[token.data.ass.type]) TKNARGS(kExprLexRegister, "(name=%s)", intchar2str(token.data.reg.name)) case kExprLexDoubleQuotedString: TKNARGS(kExprLexSingleQuotedString, "(closed=%i)", @@ -811,6 +843,7 @@ const char *const east_node_type_tab[] = { [kExprNodeMod] = "Mod", [kExprNodeOption] = "Option", [kExprNodeEnvironment] = "Environment", + [kExprNodeAssignment] = "Assignment", }; /// Represent `int` character as a string @@ -933,6 +966,7 @@ const uint8_t node_maxchildren[] = { [kExprNodeMod] = 2, [kExprNodeOption] = 0, [kExprNodeEnvironment] = 0, + [kExprNodeAssignment] = 2, }; /// Free memory occupied by AST @@ -993,6 +1027,7 @@ void viml_pexpr_free_ast(ExprAST ast) case kExprNodeLambda: case kExprNodeDictLiteral: case kExprNodeCurlyBracesIdentifier: + case kExprNodeAssignment: case kExprNodeComma: case kExprNodeColon: case kExprNodeArrow: @@ -1111,6 +1146,8 @@ static struct { [kExprNodeCurlyBracesIdentifier] = { kEOpLvlComplexIdentifier, kEOpAssLeft }, + [kExprNodeAssignment] = { kEOpLvlAssignment, kEOpAssLeft }, + [kExprNodeComplexIdentifier] = { kEOpLvlValue, kEOpAssLeft }, [kExprNodePlainIdentifier] = { kEOpLvlValue, kEOpAssNo }, @@ -1478,6 +1515,17 @@ static inline void east_set_error(const ParserState *const pstate, } \ } while (0) +/// Determine whether given parse type is an assignment +/// +/// @param[in] pt Checked parse type. +/// +/// @return true if parsing an assignment, false otherwise. +static inline bool pt_is_assignment(const ExprASTParseType pt) + FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT +{ + return (pt == kEPTAssignment || pt == kEPTSingleAssignment); +} + /// Structure used to define “string shifts” necessary to map string /// highlighting to actual strings. typedef struct { @@ -1839,6 +1887,9 @@ ExprAST viml_pexpr_parse(ParserState *const pstate, const int flags) ExprASTParseTypeStack pt_stack; kvi_init(pt_stack); kvi_push(pt_stack, kEPTExpr); + if (flags & kExprFlagsParseLet) { + kvi_push(pt_stack, kEPTAssignment); + } LexExprToken prev_token = { .type = kExprLexMissing }; bool highlighted_prev_spacing = false; // Lambda node, valid when parsing lambda arguments only. @@ -1938,33 +1989,83 @@ viml_pexpr_parse_process_token: // circumstances, and in any case runtime and not parse time errors. (*kv_Z(ast_stack, 1))->type = kExprNodeConcat; } - if (kv_last(pt_stack) == kEPTLambdaArguments - && ((want_node == kENodeOperator + // Pop some stack pt_stack items in case of misplaced nodes. + const bool is_single_assignment = kv_last(pt_stack) == kEPTSingleAssignment; + switch (kv_last(pt_stack)) { + case kEPTExpr: { + break; + } + case kEPTLambdaArguments: { + if ((want_node == kENodeOperator && tok_type != kExprLexComma && tok_type != kExprLexArrow) || (want_node == kENodeValue && !(cur_token.type == kExprLexPlainIdentifier && cur_token.data.var.scope == kExprVarScopeMissing && !cur_token.data.var.autoload) - && tok_type != kExprLexArrow))) { - lambda_node->data.fig.type_guesses.allow_lambda = false; - if (lambda_node->children != NULL - && lambda_node->children->type == kExprNodeComma) { - // If lambda has comma child this means that parser has already seen at - // least "{arg1,", so node cannot possibly be anything, but lambda. - - // Vim may give E121 or E720 in this case, but it does not look right to - // have either because both are results of reevaluation possibly-lambda - // node as a dictionary and here this is not going to happen. - ERROR_FROM_TOKEN_AND_MSG( - cur_token, _("E15: Expected lambda arguments list or arrow: %.*s")); - } else { - // Else it may appear that possibly-lambda node is actually a dictionary - // or curly-braces-name identifier. - lambda_node = NULL; - kv_drop(pt_stack, 1); + && tok_type != kExprLexArrow)) { + lambda_node->data.fig.type_guesses.allow_lambda = false; + if (lambda_node->children != NULL + && lambda_node->children->type == kExprNodeComma) { + // If lambda has comma child this means that parser has already seen at + // least "{arg1,", so node cannot possibly be anything, but lambda. + + // Vim may give E121 or E720 in this case, but it does not look right to + // have either because both are results of reevaluation possibly-lambda + // node as a dictionary and here this is not going to happen. + ERROR_FROM_TOKEN_AND_MSG( + cur_token, _("E15: Expected lambda arguments list or arrow: %.*s")); + } else { + // Else it may appear that possibly-lambda node is actually a dictionary + // or curly-braces-name identifier. + lambda_node = NULL; + kv_drop(pt_stack, 1); + } + } + break; + } + case kEPTSingleAssignment: { + if (tok_type == kExprLexBracket && !cur_token.data.brc.closing) { + ERROR_FROM_TOKEN_AND_MSG( + cur_token, + _("E475: Nested lists not allowed when assigning: %.*s")); + kv_drop(pt_stack, 2); + assert(kv_size(pt_stack)); + assert(kv_last(pt_stack) == kEPTExpr); + break; + } + FALLTHROUGH; + } + case kEPTAssignment: { + if (want_node == kENodeValue + && tok_type != kExprLexBracket + && tok_type != kExprLexPlainIdentifier + && (tok_type != kExprLexFigureBrace || cur_token.data.brc.closing) + && !(node_is_key && tok_type == kExprLexNumber) + && tok_type != kExprLexEnv + && tok_type != kExprLexOption + && tok_type != kExprLexRegister) { + ERROR_FROM_TOKEN_AND_MSG( + cur_token, + _("E15: Expected value part of assignment lvalue: %.*s")); + kv_drop(pt_stack, 1); + } else if (want_node == kENodeOperator + && tok_type != kExprLexBracket + && (tok_type != kExprLexFigureBrace + || cur_token.data.brc.closing) + && tok_type != kExprLexDot + && (tok_type != kExprLexComma || !is_single_assignment) + && tok_type != kExprLexAssignment) { + ERROR_FROM_TOKEN_AND_MSG( + cur_token, + _("E15: Expected assignment operator or subscript: %.*s")); + kv_drop(pt_stack, 1); + } + assert(kv_size(pt_stack)); + break; } } + assert(kv_size(pt_stack)); const ExprASTParseType cur_pt = kv_last(pt_stack); assert(lambda_node == NULL || cur_pt == kEPTLambdaArguments); switch (tok_type) { @@ -2339,21 +2440,41 @@ viml_pexpr_parse_bracket_closing_error: } kvi_push(ast_stack, new_top_node_p); want_node = kENodeOperator; + if (cur_pt == kEPTSingleAssignment) { + kv_drop(pt_stack, 1); + } else if (cur_pt == kEPTAssignment) { + assert(ast.err.msg); + } else if (cur_pt == kEPTExpr + && kv_size(pt_stack) > 1 + && pt_is_assignment(kv_Z(pt_stack, 1))) { + kv_drop(pt_stack, 1); + } } else { if (want_node == kENodeValue) { - // Value means list literal. + // Value means list literal or list assignment. HL_CUR_TOKEN(List); NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeListLiteral); *top_node_p = cur_node; kvi_push(ast_stack, &cur_node->children); want_node = kENodeValue; + if (cur_pt == kEPTAssignment) { + // Additional assignment parse type allows to easily forbid nested + // lists. + kvi_push(pt_stack, kEPTSingleAssignment); + } } else { + // Operator means subscript, also in assignment. But in assignment + // subscript may be pretty much any expression, so need to push + // kEPTExpr. if (prev_token.type == kExprLexSpacing) { OP_MISSING; } NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeSubscript); ADD_OP_NODE(cur_node); HL_CUR_TOKEN(SubscriptBracket); + if (pt_is_assignment(cur_pt)) { + kvi_push(pt_stack, kEPTExpr); + } } } break; @@ -2458,15 +2579,31 @@ viml_pexpr_parse_figure_brace_closing_error: } kvi_push(ast_stack, new_top_node_p); want_node = kENodeOperator; + if (cur_pt == kEPTExpr + && kv_size(pt_stack) > 1 + && pt_is_assignment(kv_Z(pt_stack, 1))) { + kv_drop(pt_stack, 1); + } } else { if (want_node == kENodeValue) { HL_CUR_TOKEN(FigureBrace); // Value: may be any of lambda, dictionary literal and curly braces // name. - NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeUnknownFigure); - cur_node->data.fig.type_guesses.allow_lambda = true; - cur_node->data.fig.type_guesses.allow_dict = true; - cur_node->data.fig.type_guesses.allow_ident = true; + + // Though if we are in an assignment this may only be a curly braces + // name. + if (pt_is_assignment(cur_pt)) { + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeCurlyBracesIdentifier); + cur_node->data.fig.type_guesses.allow_lambda = false; + cur_node->data.fig.type_guesses.allow_dict = false; + cur_node->data.fig.type_guesses.allow_ident = true; + kvi_push(pt_stack, kEPTExpr); + } else { + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeUnknownFigure); + cur_node->data.fig.type_guesses.allow_lambda = true; + cur_node->data.fig.type_guesses.allow_dict = true; + cur_node->data.fig.type_guesses.allow_ident = true; + } if (pstate->colors) { cur_node->data.fig.opening_hl_idx = kv_size(*pstate->colors) - 1; } @@ -2484,6 +2621,9 @@ viml_pexpr_parse_figure_brace_closing_error: cur_node->data.fig.type_guesses.allow_dict = false; cur_node->data.fig.type_guesses.allow_ident = true; kvi_push(ast_stack, &cur_node->children); + if (pt_is_assignment(cur_pt)) { + kvi_push(pt_stack, kEPTExpr); + } want_node = kENodeValue; } while (0), Curly); @@ -2746,6 +2886,36 @@ viml_pexpr_parse_no_paren_closing_error: {} want_node = kENodeOperator; break; } + case kExprLexAssignment: { + if (cur_pt == kEPTAssignment) { + kv_drop(pt_stack, 1); + } else if (cur_pt == kEPTSingleAssignment) { + kv_drop(pt_stack, 2); + ERROR_FROM_TOKEN_AND_MSG( + cur_token, + _("E475: Expected closing bracket to end list assignment " + "lvalue: %.*s")); + } else { + ERROR_FROM_TOKEN_AND_MSG( + cur_token, _("E15: Misplaced assignment: %.*s")); + } + assert(kv_size(pt_stack)); + assert(kv_last(pt_stack) == kEPTExpr); + ADD_VALUE_IF_MISSING(_("E15: Unexpected assignment: %.*s")); + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeAssignment); + cur_node->data.ass.type = cur_token.data.ass.type; + switch (cur_token.data.ass.type) { +#define HL_ASGN(asgn, hl) \ + case kExprAsgn##asgn: { HL_CUR_TOKEN(hl); break; } + HL_ASGN(Plain, PlainAssignment) + HL_ASGN(Add, AssignmentWithAddition) + HL_ASGN(Subtract, AssignmentWithSubtraction) + HL_ASGN(Concat, AssignmentWithConcatenation) +#undef HL_ASGN + } + ADD_OP_NODE(cur_node); + break; + } } viml_pexpr_parse_cycle_end: prev_token = cur_token; @@ -2862,6 +3032,7 @@ viml_pexpr_parse_end: // FIXME: Investigate whether above are OK to be present in the stack. break; } + case kExprNodeAssignment: case kExprNodeMod: case kExprNodeDivision: case kExprNodeMultiplication: diff --git a/src/nvim/viml/parser/expressions.h b/src/nvim/viml/parser/expressions.h index 648f8cbc1f..23e172da75 100644 --- a/src/nvim/viml/parser/expressions.h +++ b/src/nvim/viml/parser/expressions.h @@ -51,6 +51,9 @@ typedef enum { 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 { @@ -68,6 +71,14 @@ typedef enum { 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 }) @@ -147,6 +158,10 @@ typedef struct { 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; @@ -170,8 +185,8 @@ typedef enum { /// “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), - // WARNING: whenever you add a new flag, alter klee_assume() statement in - // viml_expressions_lexer.c. + // XXX Whenever you add a new flag, alter klee_assume() statement in + // viml_expressions_lexer.c. } LexExprFlags; /// Expression AST node type @@ -233,6 +248,10 @@ typedef enum { 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; @@ -301,6 +320,9 @@ struct expr_ast_node { 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; }; @@ -314,8 +336,15 @@ enum { /// When parsing expressions input by user bar is assumed to be a binary /// operator and other two are spacings. kExprFlagsDisallowEOC = (1 << 1), - // WARNING: whenever you add a new flag, alter klee_assume() statement in - // viml_expressions_parser.c. + /// 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. } ExprParserFlags; /// AST error definition @@ -350,6 +379,9 @@ 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 |