aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/api/vim.c39
-rw-r--r--src/nvim/syntax.c20
-rw-r--r--src/nvim/viml/parser/expressions.c239
-rw-r--r--src/nvim/viml/parser/expressions.h40
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