mirror of
https://github.com/neovim/neovim.git
synced 2025-09-18 09:18:19 +00:00
viml/parser/expressions: Add support for parsing assignments
This commit is contained in:
@@ -900,14 +900,21 @@ typedef kvec_withinit_t(ExprASTConvStackItem, 16) ExprASTConvStack;
|
|||||||
/// Parse a VimL expression
|
/// Parse a VimL expression
|
||||||
///
|
///
|
||||||
/// @param[in] expr Expression to parse. Is always treated as a single line.
|
/// @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
|
/// @param[in] flags Flags:
|
||||||
/// (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
|
/// - "m" if multiple expressions in a row are allowed (only
|
||||||
/// parse like for ":echo", empty string for ":let".
|
/// 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"
|
/// @param[in] highlight If true, return value will also include "highlight"
|
||||||
/// key containing array of 4-tuples (arrays) (Integer,
|
/// key containing array of 4-tuples (arrays) (Integer,
|
||||||
/// Integer, Integer, String), where first three numbers
|
/// Integer, Integer, String), where first three numbers
|
||||||
@@ -964,6 +971,9 @@ typedef kvec_withinit_t(ExprASTConvStackItem, 16) ExprASTConvStack;
|
|||||||
/// value names from ExprCaseCompareStrategy,
|
/// value names from ExprCaseCompareStrategy,
|
||||||
/// stringified without "kCCStrategy" prefix. Only
|
/// stringified without "kCCStrategy" prefix. Only
|
||||||
/// present for "Comparison" nodes.
|
/// 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
|
/// "invert": Boolean, true if result of comparison needs to be
|
||||||
/// inverted. Only present for "Comparison" nodes.
|
/// inverted. Only present for "Comparison" nodes.
|
||||||
/// "ivalue": Integer, integer value for "Integer" 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]) {
|
switch (flags.data[i]) {
|
||||||
case 'm': { pflags |= kExprFlagsMulti; break; }
|
case 'm': { pflags |= kExprFlagsMulti; break; }
|
||||||
case 'E': { pflags |= kExprFlagsDisallowEOC; break; }
|
case 'E': { pflags |= kExprFlagsDisallowEOC; break; }
|
||||||
|
case 'l': { pflags |= kExprFlagsParseLet; break; }
|
||||||
case NUL: {
|
case NUL: {
|
||||||
api_set_error(err, kErrorTypeValidation, "Invalid flag: '\\0' (%u)",
|
api_set_error(err, kErrorTypeValidation, "Invalid flag: '\\0' (%u)",
|
||||||
(unsigned)flags.data[i]);
|
(unsigned)flags.data[i]);
|
||||||
@@ -1114,6 +1125,7 @@ Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight,
|
|||||||
+ (node->type == kExprNodeFloat) // "fvalue"
|
+ (node->type == kExprNodeFloat) // "fvalue"
|
||||||
+ (node->type == kExprNodeDoubleQuotedString
|
+ (node->type == kExprNodeDoubleQuotedString
|
||||||
|| node->type == kExprNodeSingleQuotedString) // "svalue"
|
|| node->type == kExprNodeSingleQuotedString) // "svalue"
|
||||||
|
+ (node->type == kExprNodeAssignment) // "augmentation"
|
||||||
+ 0);
|
+ 0);
|
||||||
Dictionary ret_node = {
|
Dictionary ret_node = {
|
||||||
.items = xmalloc(ret_node_items_size * sizeof(ret_node.items[0])),
|
.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;
|
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 kExprNodeMissing:
|
||||||
case kExprNodeOpMissing:
|
case kExprNodeOpMissing:
|
||||||
case kExprNodeTernary:
|
case kExprNodeTernary:
|
||||||
|
@@ -6021,11 +6021,21 @@ static const char *highlight_init_dark[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static const char *highlight_init_cmdline[] = {
|
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 should appear only when highlighter has a bug.
|
||||||
"NVimInternalError ctermfg=Red ctermbg=Red guifg=Red guibg=Red",
|
"NVimInternalError ctermfg=Red ctermbg=Red guifg=Red guibg=Red",
|
||||||
|
|
||||||
// Highlight groups (links) used by parser:
|
// 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 NVimOperator Operator",
|
||||||
|
|
||||||
"default link NVimUnaryOperator NVimOperator",
|
"default link NVimUnaryOperator NVimOperator",
|
||||||
@@ -6113,6 +6123,16 @@ static const char *highlight_init_cmdline[] = {
|
|||||||
|
|
||||||
"default link NVimInvalid Error",
|
"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 NVimInvalidOperator NVimInvalid",
|
||||||
|
|
||||||
"default link NVimInvalidUnaryOperator NVimInvalidOperator",
|
"default link NVimInvalidUnaryOperator NVimInvalidOperator",
|
||||||
|
@@ -84,6 +84,10 @@ typedef enum {
|
|||||||
/// Just like parsing function arguments, but it is valid to be ended with an
|
/// Just like parsing function arguments, but it is valid to be ended with an
|
||||||
/// arrow only.
|
/// arrow only.
|
||||||
kEPTLambdaArguments,
|
kEPTLambdaArguments,
|
||||||
|
/// Assignment: parsing for :let
|
||||||
|
kEPTAssignment,
|
||||||
|
/// Single assignment: used when lists are not allowed (i.e. when nesting)
|
||||||
|
kEPTSingleAssignment,
|
||||||
} ExprASTParseType;
|
} ExprASTParseType;
|
||||||
|
|
||||||
typedef kvec_withinit_t(ExprASTParseType, 4) ExprASTParseTypeStack;
|
typedef kvec_withinit_t(ExprASTParseType, 4) ExprASTParseTypeStack;
|
||||||
@@ -93,6 +97,7 @@ typedef enum {
|
|||||||
kEOpLvlInvalid = 0,
|
kEOpLvlInvalid = 0,
|
||||||
kEOpLvlComplexIdentifier,
|
kEOpLvlComplexIdentifier,
|
||||||
kEOpLvlParens,
|
kEOpLvlParens,
|
||||||
|
kEOpLvlAssignment,
|
||||||
kEOpLvlArrow,
|
kEOpLvlArrow,
|
||||||
kEOpLvlComma,
|
kEOpLvlComma,
|
||||||
kEOpLvlColon,
|
kEOpLvlColon,
|
||||||
@@ -217,8 +222,6 @@ LexExprToken viml_pexpr_next_token(ParserState *const pstate, const int flags)
|
|||||||
}
|
}
|
||||||
CHAR(kExprLexQuestion, '?')
|
CHAR(kExprLexQuestion, '?')
|
||||||
CHAR(kExprLexColon, ':')
|
CHAR(kExprLexColon, ':')
|
||||||
CHAR(kExprLexDot, '.')
|
|
||||||
CHAR(kExprLexPlus, '+')
|
|
||||||
CHAR(kExprLexComma, ',')
|
CHAR(kExprLexComma, ',')
|
||||||
#undef CHAR
|
#undef CHAR
|
||||||
|
|
||||||
@@ -532,12 +535,8 @@ LexExprToken viml_pexpr_next_token(ParserState *const pstate, const int flags)
|
|||||||
case '!':
|
case '!':
|
||||||
case '=': {
|
case '=': {
|
||||||
if (pline.size == 1) {
|
if (pline.size == 1) {
|
||||||
viml_pexpr_next_token_invalid_comparison:
|
ret.type = (schar == '!' ? kExprLexNot : kExprLexAssignment);
|
||||||
ret.type = (schar == '!' ? kExprLexNot : kExprLexInvalid);
|
ret.data.ass.type = kExprAsgnPlain;
|
||||||
if (ret.type == kExprLexInvalid) {
|
|
||||||
ret.data.err.msg = _("E15: Expected == or =~: %.*s");
|
|
||||||
ret.data.err.type = kExprLexComparison;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ret.type = kExprLexComparison;
|
ret.type = kExprLexComparison;
|
||||||
@@ -548,8 +547,11 @@ viml_pexpr_next_token_invalid_comparison:
|
|||||||
} else if (pline.data[1] == '~') {
|
} else if (pline.data[1] == '~') {
|
||||||
ret.data.cmp.type = kExprCmpMatches;
|
ret.data.cmp.type = kExprCmpMatches;
|
||||||
ret.len++;
|
ret.len++;
|
||||||
|
} else if (schar == '!') {
|
||||||
|
ret.type = kExprLexNot;
|
||||||
} else {
|
} else {
|
||||||
goto viml_pexpr_next_token_invalid_comparison;
|
ret.type = kExprLexAssignment;
|
||||||
|
ret.data.ass.type = kExprAsgnPlain;
|
||||||
}
|
}
|
||||||
GET_CCS(ret, pline);
|
GET_CCS(ret, pline);
|
||||||
break;
|
break;
|
||||||
@@ -571,17 +573,37 @@ viml_pexpr_next_token_invalid_comparison:
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Minus sign or arrow from lambdas.
|
// Minus sign, arrow from lambdas or augmented assignment.
|
||||||
case '-': {
|
case '-': {
|
||||||
if (pline.size > 1 && pline.data[1] == '>') {
|
if (pline.size > 1 && pline.data[1] == '>') {
|
||||||
ret.len++;
|
ret.len++;
|
||||||
ret.type = kExprLexArrow;
|
ret.type = kExprLexArrow;
|
||||||
|
} else if (pline.size > 1 && pline.data[1] == '=') {
|
||||||
|
ret.len++;
|
||||||
|
ret.type = kExprLexAssignment;
|
||||||
|
ret.data.ass.type = kExprAsgnSubtract;
|
||||||
} else {
|
} else {
|
||||||
ret.type = kExprLexMinus;
|
ret.type = kExprLexMinus;
|
||||||
}
|
}
|
||||||
break;
|
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.
|
// Expression end because Ex command ended.
|
||||||
case NUL:
|
case NUL:
|
||||||
case NL: {
|
case NL: {
|
||||||
@@ -661,6 +683,7 @@ static const char *const eltkn_type_tab[] = {
|
|||||||
[kExprLexParenthesis] = "Parenthesis",
|
[kExprLexParenthesis] = "Parenthesis",
|
||||||
[kExprLexComma] = "Comma",
|
[kExprLexComma] = "Comma",
|
||||||
[kExprLexArrow] = "Arrow",
|
[kExprLexArrow] = "Arrow",
|
||||||
|
[kExprLexAssignment] = "Assignment",
|
||||||
};
|
};
|
||||||
|
|
||||||
const char *const eltkn_cmp_type_tab[] = {
|
const char *const eltkn_cmp_type_tab[] = {
|
||||||
@@ -671,6 +694,13 @@ const char *const eltkn_cmp_type_tab[] = {
|
|||||||
[kExprCmpIdentical] = "Identical",
|
[kExprCmpIdentical] = "Identical",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const char *const expr_asgn_type_tab[] = {
|
||||||
|
[kExprAsgnPlain] = "Plain",
|
||||||
|
[kExprAsgnAdd] = "Add",
|
||||||
|
[kExprAsgnSubtract] = "Subtract",
|
||||||
|
[kExprAsgnConcat] = "Concat",
|
||||||
|
};
|
||||||
|
|
||||||
const char *const ccs_tab[] = {
|
const char *const ccs_tab[] = {
|
||||||
[kCCStrategyUseOption] = "UseOption",
|
[kCCStrategyUseOption] = "UseOption",
|
||||||
[kCCStrategyMatchCase] = "MatchCase",
|
[kCCStrategyMatchCase] = "MatchCase",
|
||||||
@@ -732,6 +762,8 @@ const char *viml_pexpr_repr_token(const ParserState *const pstate,
|
|||||||
(int)token.data.cmp.inv)
|
(int)token.data.cmp.inv)
|
||||||
TKNARGS(kExprLexMultiplication, "(type=%s)",
|
TKNARGS(kExprLexMultiplication, "(type=%s)",
|
||||||
eltkn_mul_type_tab[token.data.mul.type])
|
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))
|
TKNARGS(kExprLexRegister, "(name=%s)", intchar2str(token.data.reg.name))
|
||||||
case kExprLexDoubleQuotedString:
|
case kExprLexDoubleQuotedString:
|
||||||
TKNARGS(kExprLexSingleQuotedString, "(closed=%i)",
|
TKNARGS(kExprLexSingleQuotedString, "(closed=%i)",
|
||||||
@@ -811,6 +843,7 @@ const char *const east_node_type_tab[] = {
|
|||||||
[kExprNodeMod] = "Mod",
|
[kExprNodeMod] = "Mod",
|
||||||
[kExprNodeOption] = "Option",
|
[kExprNodeOption] = "Option",
|
||||||
[kExprNodeEnvironment] = "Environment",
|
[kExprNodeEnvironment] = "Environment",
|
||||||
|
[kExprNodeAssignment] = "Assignment",
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Represent `int` character as a string
|
/// Represent `int` character as a string
|
||||||
@@ -933,6 +966,7 @@ const uint8_t node_maxchildren[] = {
|
|||||||
[kExprNodeMod] = 2,
|
[kExprNodeMod] = 2,
|
||||||
[kExprNodeOption] = 0,
|
[kExprNodeOption] = 0,
|
||||||
[kExprNodeEnvironment] = 0,
|
[kExprNodeEnvironment] = 0,
|
||||||
|
[kExprNodeAssignment] = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Free memory occupied by AST
|
/// Free memory occupied by AST
|
||||||
@@ -993,6 +1027,7 @@ void viml_pexpr_free_ast(ExprAST ast)
|
|||||||
case kExprNodeLambda:
|
case kExprNodeLambda:
|
||||||
case kExprNodeDictLiteral:
|
case kExprNodeDictLiteral:
|
||||||
case kExprNodeCurlyBracesIdentifier:
|
case kExprNodeCurlyBracesIdentifier:
|
||||||
|
case kExprNodeAssignment:
|
||||||
case kExprNodeComma:
|
case kExprNodeComma:
|
||||||
case kExprNodeColon:
|
case kExprNodeColon:
|
||||||
case kExprNodeArrow:
|
case kExprNodeArrow:
|
||||||
@@ -1111,6 +1146,8 @@ static struct {
|
|||||||
|
|
||||||
[kExprNodeCurlyBracesIdentifier] = { kEOpLvlComplexIdentifier, kEOpAssLeft },
|
[kExprNodeCurlyBracesIdentifier] = { kEOpLvlComplexIdentifier, kEOpAssLeft },
|
||||||
|
|
||||||
|
[kExprNodeAssignment] = { kEOpLvlAssignment, kEOpAssLeft },
|
||||||
|
|
||||||
[kExprNodeComplexIdentifier] = { kEOpLvlValue, kEOpAssLeft },
|
[kExprNodeComplexIdentifier] = { kEOpLvlValue, kEOpAssLeft },
|
||||||
|
|
||||||
[kExprNodePlainIdentifier] = { kEOpLvlValue, kEOpAssNo },
|
[kExprNodePlainIdentifier] = { kEOpLvlValue, kEOpAssNo },
|
||||||
@@ -1478,6 +1515,17 @@ static inline void east_set_error(const ParserState *const pstate,
|
|||||||
} \
|
} \
|
||||||
} while (0)
|
} 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
|
/// Structure used to define “string shifts” necessary to map string
|
||||||
/// highlighting to actual strings.
|
/// highlighting to actual strings.
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -1839,6 +1887,9 @@ ExprAST viml_pexpr_parse(ParserState *const pstate, const int flags)
|
|||||||
ExprASTParseTypeStack pt_stack;
|
ExprASTParseTypeStack pt_stack;
|
||||||
kvi_init(pt_stack);
|
kvi_init(pt_stack);
|
||||||
kvi_push(pt_stack, kEPTExpr);
|
kvi_push(pt_stack, kEPTExpr);
|
||||||
|
if (flags & kExprFlagsParseLet) {
|
||||||
|
kvi_push(pt_stack, kEPTAssignment);
|
||||||
|
}
|
||||||
LexExprToken prev_token = { .type = kExprLexMissing };
|
LexExprToken prev_token = { .type = kExprLexMissing };
|
||||||
bool highlighted_prev_spacing = false;
|
bool highlighted_prev_spacing = false;
|
||||||
// Lambda node, valid when parsing lambda arguments only.
|
// Lambda node, valid when parsing lambda arguments only.
|
||||||
@@ -1938,15 +1989,21 @@ viml_pexpr_parse_process_token:
|
|||||||
// circumstances, and in any case runtime and not parse time errors.
|
// circumstances, and in any case runtime and not parse time errors.
|
||||||
(*kv_Z(ast_stack, 1))->type = kExprNodeConcat;
|
(*kv_Z(ast_stack, 1))->type = kExprNodeConcat;
|
||||||
}
|
}
|
||||||
if (kv_last(pt_stack) == kEPTLambdaArguments
|
// Pop some stack pt_stack items in case of misplaced nodes.
|
||||||
&& ((want_node == kENodeOperator
|
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 != kExprLexComma
|
||||||
&& tok_type != kExprLexArrow)
|
&& tok_type != kExprLexArrow)
|
||||||
|| (want_node == kENodeValue
|
|| (want_node == kENodeValue
|
||||||
&& !(cur_token.type == kExprLexPlainIdentifier
|
&& !(cur_token.type == kExprLexPlainIdentifier
|
||||||
&& cur_token.data.var.scope == kExprVarScopeMissing
|
&& cur_token.data.var.scope == kExprVarScopeMissing
|
||||||
&& !cur_token.data.var.autoload)
|
&& !cur_token.data.var.autoload)
|
||||||
&& tok_type != kExprLexArrow))) {
|
&& tok_type != kExprLexArrow)) {
|
||||||
lambda_node->data.fig.type_guesses.allow_lambda = false;
|
lambda_node->data.fig.type_guesses.allow_lambda = false;
|
||||||
if (lambda_node->children != NULL
|
if (lambda_node->children != NULL
|
||||||
&& lambda_node->children->type == kExprNodeComma) {
|
&& lambda_node->children->type == kExprNodeComma) {
|
||||||
@@ -1965,6 +2022,50 @@ viml_pexpr_parse_process_token:
|
|||||||
kv_drop(pt_stack, 1);
|
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);
|
const ExprASTParseType cur_pt = kv_last(pt_stack);
|
||||||
assert(lambda_node == NULL || cur_pt == kEPTLambdaArguments);
|
assert(lambda_node == NULL || cur_pt == kEPTLambdaArguments);
|
||||||
switch (tok_type) {
|
switch (tok_type) {
|
||||||
@@ -2339,21 +2440,41 @@ viml_pexpr_parse_bracket_closing_error:
|
|||||||
}
|
}
|
||||||
kvi_push(ast_stack, new_top_node_p);
|
kvi_push(ast_stack, new_top_node_p);
|
||||||
want_node = kENodeOperator;
|
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 {
|
} else {
|
||||||
if (want_node == kENodeValue) {
|
if (want_node == kENodeValue) {
|
||||||
// Value means list literal.
|
// Value means list literal or list assignment.
|
||||||
HL_CUR_TOKEN(List);
|
HL_CUR_TOKEN(List);
|
||||||
NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeListLiteral);
|
NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeListLiteral);
|
||||||
*top_node_p = cur_node;
|
*top_node_p = cur_node;
|
||||||
kvi_push(ast_stack, &cur_node->children);
|
kvi_push(ast_stack, &cur_node->children);
|
||||||
want_node = kENodeValue;
|
want_node = kENodeValue;
|
||||||
|
if (cur_pt == kEPTAssignment) {
|
||||||
|
// Additional assignment parse type allows to easily forbid nested
|
||||||
|
// lists.
|
||||||
|
kvi_push(pt_stack, kEPTSingleAssignment);
|
||||||
|
}
|
||||||
} else {
|
} 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) {
|
if (prev_token.type == kExprLexSpacing) {
|
||||||
OP_MISSING;
|
OP_MISSING;
|
||||||
}
|
}
|
||||||
NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeSubscript);
|
NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeSubscript);
|
||||||
ADD_OP_NODE(cur_node);
|
ADD_OP_NODE(cur_node);
|
||||||
HL_CUR_TOKEN(SubscriptBracket);
|
HL_CUR_TOKEN(SubscriptBracket);
|
||||||
|
if (pt_is_assignment(cur_pt)) {
|
||||||
|
kvi_push(pt_stack, kEPTExpr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -2458,15 +2579,31 @@ viml_pexpr_parse_figure_brace_closing_error:
|
|||||||
}
|
}
|
||||||
kvi_push(ast_stack, new_top_node_p);
|
kvi_push(ast_stack, new_top_node_p);
|
||||||
want_node = kENodeOperator;
|
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 {
|
} else {
|
||||||
if (want_node == kENodeValue) {
|
if (want_node == kENodeValue) {
|
||||||
HL_CUR_TOKEN(FigureBrace);
|
HL_CUR_TOKEN(FigureBrace);
|
||||||
// Value: may be any of lambda, dictionary literal and curly braces
|
// Value: may be any of lambda, dictionary literal and curly braces
|
||||||
// name.
|
// name.
|
||||||
|
|
||||||
|
// 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);
|
NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeUnknownFigure);
|
||||||
cur_node->data.fig.type_guesses.allow_lambda = true;
|
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_dict = true;
|
||||||
cur_node->data.fig.type_guesses.allow_ident = true;
|
cur_node->data.fig.type_guesses.allow_ident = true;
|
||||||
|
}
|
||||||
if (pstate->colors) {
|
if (pstate->colors) {
|
||||||
cur_node->data.fig.opening_hl_idx = kv_size(*pstate->colors) - 1;
|
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_dict = false;
|
||||||
cur_node->data.fig.type_guesses.allow_ident = true;
|
cur_node->data.fig.type_guesses.allow_ident = true;
|
||||||
kvi_push(ast_stack, &cur_node->children);
|
kvi_push(ast_stack, &cur_node->children);
|
||||||
|
if (pt_is_assignment(cur_pt)) {
|
||||||
|
kvi_push(pt_stack, kEPTExpr);
|
||||||
|
}
|
||||||
want_node = kENodeValue;
|
want_node = kENodeValue;
|
||||||
} while (0),
|
} while (0),
|
||||||
Curly);
|
Curly);
|
||||||
@@ -2746,6 +2886,36 @@ viml_pexpr_parse_no_paren_closing_error: {}
|
|||||||
want_node = kENodeOperator;
|
want_node = kENodeOperator;
|
||||||
break;
|
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:
|
viml_pexpr_parse_cycle_end:
|
||||||
prev_token = cur_token;
|
prev_token = cur_token;
|
||||||
@@ -2862,6 +3032,7 @@ viml_pexpr_parse_end:
|
|||||||
// FIXME: Investigate whether above are OK to be present in the stack.
|
// FIXME: Investigate whether above are OK to be present in the stack.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case kExprNodeAssignment:
|
||||||
case kExprNodeMod:
|
case kExprNodeMod:
|
||||||
case kExprNodeDivision:
|
case kExprNodeDivision:
|
||||||
case kExprNodeMultiplication:
|
case kExprNodeMultiplication:
|
||||||
|
@@ -51,6 +51,9 @@ typedef enum {
|
|||||||
kExprLexParenthesis, ///< Parenthesis, either opening or closing.
|
kExprLexParenthesis, ///< Parenthesis, either opening or closing.
|
||||||
kExprLexComma, ///< Comma.
|
kExprLexComma, ///< Comma.
|
||||||
kExprLexArrow, ///< Arrow, like from lambda expressions.
|
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;
|
} LexExprTokenType;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@@ -68,6 +71,14 @@ typedef enum {
|
|||||||
kExprOptScopeLocal = 'l',
|
kExprOptScopeLocal = 'l',
|
||||||
} ExprOptScope;
|
} 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 \
|
#define EXPR_OPT_SCOPE_LIST \
|
||||||
((char[]){ kExprOptScopeGlobal, kExprOptScopeLocal })
|
((char[]){ kExprOptScopeGlobal, kExprOptScopeLocal })
|
||||||
|
|
||||||
@@ -147,6 +158,10 @@ typedef struct {
|
|||||||
uint8_t base; ///< Base: 2, 8, 10 or 16.
|
uint8_t base; ///< Base: 2, 8, 10 or 16.
|
||||||
bool is_float; ///< True if number is a floating-point.
|
bool is_float; ///< True if number is a floating-point.
|
||||||
} num; ///< For kExprLexNumber
|
} num; ///< For kExprLexNumber
|
||||||
|
|
||||||
|
struct {
|
||||||
|
ExprAssignmentType type;
|
||||||
|
} ass; ///< For kExprLexAssignment
|
||||||
} data; ///< Additional data, if needed.
|
} data; ///< Additional data, if needed.
|
||||||
} LexExprToken;
|
} LexExprToken;
|
||||||
|
|
||||||
@@ -170,7 +185,7 @@ typedef enum {
|
|||||||
/// “EOC” is something like "|". It is fine with emitting EOC at the end of
|
/// “EOC” is something like "|". It is fine with emitting EOC at the end of
|
||||||
/// string still, with or without this flag set.
|
/// string still, with or without this flag set.
|
||||||
kELFlagForbidEOC = (1 << 4),
|
kELFlagForbidEOC = (1 << 4),
|
||||||
// WARNING: whenever you add a new flag, alter klee_assume() statement in
|
// XXX Whenever you add a new flag, alter klee_assume() statement in
|
||||||
// viml_expressions_lexer.c.
|
// viml_expressions_lexer.c.
|
||||||
} LexExprFlags;
|
} LexExprFlags;
|
||||||
|
|
||||||
@@ -233,6 +248,10 @@ typedef enum {
|
|||||||
kExprNodeMod,
|
kExprNodeMod,
|
||||||
kExprNodeOption,
|
kExprNodeOption,
|
||||||
kExprNodeEnvironment,
|
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;
|
} ExprASTNodeType;
|
||||||
|
|
||||||
typedef struct expr_ast_node ExprASTNode;
|
typedef struct expr_ast_node ExprASTNode;
|
||||||
@@ -301,6 +320,9 @@ struct expr_ast_node {
|
|||||||
const char *ident; ///< Environment variable name start.
|
const char *ident; ///< Environment variable name start.
|
||||||
size_t ident_len; ///< Environment variable name length.
|
size_t ident_len; ///< Environment variable name length.
|
||||||
} env; ///< For kExprNodeEnvironment.
|
} env; ///< For kExprNodeEnvironment.
|
||||||
|
struct {
|
||||||
|
ExprAssignmentType type;
|
||||||
|
} ass; ///< For kExprNodeAssignment
|
||||||
} data;
|
} data;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -314,8 +336,15 @@ enum {
|
|||||||
/// When parsing expressions input by user bar is assumed to be a binary
|
/// When parsing expressions input by user bar is assumed to be a binary
|
||||||
/// operator and other two are spacings.
|
/// operator and other two are spacings.
|
||||||
kExprFlagsDisallowEOC = (1 << 1),
|
kExprFlagsDisallowEOC = (1 << 1),
|
||||||
// WARNING: whenever you add a new flag, alter klee_assume() statement in
|
/// Parse :let argument
|
||||||
// viml_expressions_parser.c.
|
///
|
||||||
|
/// 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;
|
} ExprParserFlags;
|
||||||
|
|
||||||
/// AST error definition
|
/// AST error definition
|
||||||
@@ -350,6 +379,9 @@ extern const char *const eltkn_cmp_type_tab[];
|
|||||||
/// Array mapping ExprCaseCompareStrategy values to their stringified versions
|
/// Array mapping ExprCaseCompareStrategy values to their stringified versions
|
||||||
extern const char *const ccs_tab[];
|
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
|
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||||
# include "viml/parser/expressions.h.generated.h"
|
# include "viml/parser/expressions.h.generated.h"
|
||||||
#endif
|
#endif
|
||||||
|
@@ -48,7 +48,8 @@ int main(const int argc, const char *const *const argv,
|
|||||||
klee_make_symbolic(&shift, sizeof(shift), "shift");
|
klee_make_symbolic(&shift, sizeof(shift), "shift");
|
||||||
klee_make_symbolic(&flags, sizeof(flags), "flags");
|
klee_make_symbolic(&flags, sizeof(flags), "flags");
|
||||||
klee_assume(shift < INPUT_SIZE);
|
klee_assume(shift < INPUT_SIZE);
|
||||||
klee_assume(flags <= (kExprFlagsMulti|kExprFlagsDisallowEOC));
|
klee_assume(
|
||||||
|
flags <= (kExprFlagsMulti|kExprFlagsDisallowEOC|kExprFlagsParseLet));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ParserLine plines[] = {
|
ParserLine plines[] = {
|
||||||
|
@@ -13,6 +13,7 @@ local conv_ccs = viml_helpers.conv_ccs
|
|||||||
local new_pstate = viml_helpers.new_pstate
|
local new_pstate = viml_helpers.new_pstate
|
||||||
local conv_cmp_type = viml_helpers.conv_cmp_type
|
local conv_cmp_type = viml_helpers.conv_cmp_type
|
||||||
local pstate_set_str = viml_helpers.pstate_set_str
|
local pstate_set_str = viml_helpers.pstate_set_str
|
||||||
|
local conv_expr_asgn_type = viml_helpers.conv_expr_asgn_type
|
||||||
|
|
||||||
local shallowcopy = global_helpers.shallowcopy
|
local shallowcopy = global_helpers.shallowcopy
|
||||||
local intchar2lua = global_helpers.intchar2lua
|
local intchar2lua = global_helpers.intchar2lua
|
||||||
@@ -52,6 +53,8 @@ child_call_once(function()
|
|||||||
[tonumber(lib.kExprLexParenthesis)] = 'Parenthesis',
|
[tonumber(lib.kExprLexParenthesis)] = 'Parenthesis',
|
||||||
[tonumber(lib.kExprLexComma)] = 'Comma',
|
[tonumber(lib.kExprLexComma)] = 'Comma',
|
||||||
[tonumber(lib.kExprLexArrow)] = 'Arrow',
|
[tonumber(lib.kExprLexArrow)] = 'Arrow',
|
||||||
|
|
||||||
|
[tonumber(lib.kExprLexAssignment)] = 'Assignment',
|
||||||
}
|
}
|
||||||
|
|
||||||
eltkn_mul_type_tab = {
|
eltkn_mul_type_tab = {
|
||||||
@@ -118,6 +121,8 @@ local function eltkn2lua(pstate, tkn)
|
|||||||
ret.data.val = tonumber(tkn.data.num.is_float
|
ret.data.val = tonumber(tkn.data.num.is_float
|
||||||
and tkn.data.num.val.floating
|
and tkn.data.num.val.floating
|
||||||
or tkn.data.num.val.integer)
|
or tkn.data.num.val.integer)
|
||||||
|
elseif ret.type == 'Assignment' then
|
||||||
|
ret.data = { type = conv_expr_asgn_type(tkn.data.ass.type) }
|
||||||
elseif ret.type == 'Invalid' then
|
elseif ret.type == 'Invalid' then
|
||||||
ret.data = { error = ffi.string(tkn.data.err.msg) }
|
ret.data = { error = ffi.string(tkn.data.err.msg) }
|
||||||
end
|
end
|
||||||
@@ -198,7 +203,9 @@ describe('Expressions lexer', function()
|
|||||||
singl_eltkn_test('Question', '?')
|
singl_eltkn_test('Question', '?')
|
||||||
singl_eltkn_test('Colon', ':')
|
singl_eltkn_test('Colon', ':')
|
||||||
singl_eltkn_test('Dot', '.')
|
singl_eltkn_test('Dot', '.')
|
||||||
|
singl_eltkn_test('Assignment', '.=', {type='Concat'})
|
||||||
singl_eltkn_test('Plus', '+')
|
singl_eltkn_test('Plus', '+')
|
||||||
|
singl_eltkn_test('Assignment', '+=', {type='Add'})
|
||||||
singl_eltkn_test('Comma', ',')
|
singl_eltkn_test('Comma', ',')
|
||||||
singl_eltkn_test('Multiplication', '*', {type='Mul'})
|
singl_eltkn_test('Multiplication', '*', {type='Mul'})
|
||||||
singl_eltkn_test('Multiplication', '/', {type='Div'})
|
singl_eltkn_test('Multiplication', '/', {type='Div'})
|
||||||
@@ -266,12 +273,13 @@ describe('Expressions lexer', function()
|
|||||||
singl_eltkn_test('DoubleQuotedString', '"x\\"', {closed=false})
|
singl_eltkn_test('DoubleQuotedString', '"x\\"', {closed=false})
|
||||||
singl_eltkn_test('DoubleQuotedString', '"\\"x', {closed=false})
|
singl_eltkn_test('DoubleQuotedString', '"\\"x', {closed=false})
|
||||||
singl_eltkn_test('Not', '!')
|
singl_eltkn_test('Not', '!')
|
||||||
singl_eltkn_test('Invalid', '=', {error='E15: Expected == or =~: %.*s'})
|
singl_eltkn_test('Assignment', '=', {type='Plain'})
|
||||||
comparison_test('==', '!=', 'Equal')
|
comparison_test('==', '!=', 'Equal')
|
||||||
comparison_test('=~', '!~', 'Matches')
|
comparison_test('=~', '!~', 'Matches')
|
||||||
comparison_test('>', '<=', 'Greater')
|
comparison_test('>', '<=', 'Greater')
|
||||||
comparison_test('>=', '<', 'GreaterOrEqual')
|
comparison_test('>=', '<', 'GreaterOrEqual')
|
||||||
singl_eltkn_test('Minus', '-')
|
singl_eltkn_test('Minus', '-')
|
||||||
|
singl_eltkn_test('Assignment', '-=', {type='Subtract'})
|
||||||
singl_eltkn_test('Arrow', '->')
|
singl_eltkn_test('Arrow', '->')
|
||||||
singl_eltkn_test('Invalid', '~', {error='E15: Unidentified character: %.*s'})
|
singl_eltkn_test('Invalid', '~', {error='E15: Unidentified character: %.*s'})
|
||||||
simple_test({{data=nil, size=0}}, 'EOC', 0, {error='start.col >= #pstr'})
|
simple_test({{data=nil, size=0}}, 'EOC', 0, {error='start.col >= #pstr'})
|
||||||
|
@@ -18,6 +18,7 @@ local conv_ccs = viml_helpers.conv_ccs
|
|||||||
local new_pstate = viml_helpers.new_pstate
|
local new_pstate = viml_helpers.new_pstate
|
||||||
local conv_cmp_type = viml_helpers.conv_cmp_type
|
local conv_cmp_type = viml_helpers.conv_cmp_type
|
||||||
local pstate_set_str = viml_helpers.pstate_set_str
|
local pstate_set_str = viml_helpers.pstate_set_str
|
||||||
|
local conv_expr_asgn_type = viml_helpers.conv_expr_asgn_type
|
||||||
|
|
||||||
local mergedicts_copy = global_helpers.mergedicts_copy
|
local mergedicts_copy = global_helpers.mergedicts_copy
|
||||||
local format_string = global_helpers.format_string
|
local format_string = global_helpers.format_string
|
||||||
@@ -109,6 +110,7 @@ make_enum_conv_tab(lib, {
|
|||||||
'kExprNodeMod',
|
'kExprNodeMod',
|
||||||
'kExprNodeOption',
|
'kExprNodeOption',
|
||||||
'kExprNodeEnvironment',
|
'kExprNodeEnvironment',
|
||||||
|
'kExprNodeAssignment',
|
||||||
}, 'kExprNode', function(ret) east_node_type_tab = ret end)
|
}, 'kExprNode', function(ret) east_node_type_tab = ret end)
|
||||||
|
|
||||||
local function conv_east_node_type(typ)
|
local function conv_east_node_type(typ)
|
||||||
@@ -174,6 +176,8 @@ local function eastnode2lua(pstate, eastnode, checked_nodes)
|
|||||||
typ = ('%s(ident=%s)'):format(
|
typ = ('%s(ident=%s)'):format(
|
||||||
typ,
|
typ,
|
||||||
ffi.string(eastnode.data.env.ident, eastnode.data.env.ident_len))
|
ffi.string(eastnode.data.env.ident, eastnode.data.env.ident_len))
|
||||||
|
elseif typ == 'Assignment' then
|
||||||
|
typ = ('%s(%s)'):format(typ, conv_expr_asgn_type(eastnode.data.ass.type))
|
||||||
end
|
end
|
||||||
ret_str = typ .. ':' .. ret_str
|
ret_str = typ .. ':' .. ret_str
|
||||||
local can_simplify = not ret.children
|
local can_simplify = not ret.children
|
||||||
@@ -3976,27 +3980,20 @@ describe('Expressions parser', function()
|
|||||||
-- 012345
|
-- 012345
|
||||||
ast = {
|
ast = {
|
||||||
{
|
{
|
||||||
'Comparison(type=Equal,inv=0,ccs=UseOption):0:3:=',
|
'Assignment(Add):0:1: +=',
|
||||||
children = {
|
|
||||||
{
|
|
||||||
'BinaryPlus:0:1: +',
|
|
||||||
children = {
|
children = {
|
||||||
'PlainIdentifier(scope=0,ident=a):0:0:a',
|
'PlainIdentifier(scope=0,ident=a):0:0:a',
|
||||||
'Missing:0:3:',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'PlainIdentifier(scope=0,ident=b):0:4: b',
|
'PlainIdentifier(scope=0,ident=b):0:4: b',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
err = {
|
err = {
|
||||||
arg = '= b',
|
arg = '+= b',
|
||||||
msg = 'E15: Expected == or =~: %.*s',
|
msg = 'E15: Misplaced assignment: %.*s',
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
hl('IdentifierName', 'a'),
|
hl('IdentifierName', 'a'),
|
||||||
hl('BinaryPlus', '+', 1),
|
hl('InvalidAssignmentWithAddition', '+=', 1),
|
||||||
hl('InvalidComparison', '='),
|
|
||||||
hl('IdentifierName', 'b', 1),
|
hl('IdentifierName', 'b', 1),
|
||||||
})
|
})
|
||||||
check_parsing('a + b == c + d', {
|
check_parsing('a + b == c + d', {
|
||||||
@@ -7347,4 +7344,6 @@ describe('Expressions parser', function()
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
end)
|
end)
|
||||||
|
-- FIXME: Test assignments thoroughly
|
||||||
|
-- FIXME: Test that parsing assignments can be used for `:for` pre-`in` part.
|
||||||
end)
|
end)
|
||||||
|
@@ -107,6 +107,18 @@ local function conv_ccs(ccs)
|
|||||||
return conv_enum(ccs_tab, ccs)
|
return conv_enum(ccs_tab, ccs)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local expr_asgn_type_tab
|
||||||
|
make_enum_conv_tab(lib, {
|
||||||
|
'kExprAsgnPlain',
|
||||||
|
'kExprAsgnAdd',
|
||||||
|
'kExprAsgnSubtract',
|
||||||
|
'kExprAsgnConcat',
|
||||||
|
}, 'kExprAsgn', function(ret) expr_asgn_type_tab = ret end)
|
||||||
|
|
||||||
|
local function conv_expr_asgn_type(expr_asgn_type)
|
||||||
|
return conv_enum(expr_asgn_type_tab, expr_asgn_type)
|
||||||
|
end
|
||||||
|
|
||||||
return {
|
return {
|
||||||
conv_ccs = conv_ccs,
|
conv_ccs = conv_ccs,
|
||||||
pline2lua = pline2lua,
|
pline2lua = pline2lua,
|
||||||
@@ -114,4 +126,5 @@ return {
|
|||||||
new_pstate = new_pstate,
|
new_pstate = new_pstate,
|
||||||
conv_cmp_type = conv_cmp_type,
|
conv_cmp_type = conv_cmp_type,
|
||||||
pstate_set_str = pstate_set_str,
|
pstate_set_str = pstate_set_str,
|
||||||
|
conv_expr_asgn_type = conv_expr_asgn_type,
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user