mirror of
https://github.com/neovim/neovim.git
synced 2025-09-13 06:48:17 +00:00
viml/parser/expressions: Add support for ternary operator
This commit is contained in:
@@ -46,6 +46,7 @@ typedef enum {
|
|||||||
kEOpLvlArrow,
|
kEOpLvlArrow,
|
||||||
kEOpLvlComma,
|
kEOpLvlComma,
|
||||||
kEOpLvlColon,
|
kEOpLvlColon,
|
||||||
|
kEOpLvlTernaryValue,
|
||||||
kEOpLvlTernary,
|
kEOpLvlTernary,
|
||||||
kEOpLvlOr,
|
kEOpLvlOr,
|
||||||
kEOpLvlAnd,
|
kEOpLvlAnd,
|
||||||
@@ -770,7 +771,8 @@ static inline void viml_pexpr_debug_print_token(
|
|||||||
// NVimUnaryOperator -> NVimOperator
|
// NVimUnaryOperator -> NVimOperator
|
||||||
// NVimBinaryOperator -> NVimOperator
|
// NVimBinaryOperator -> NVimOperator
|
||||||
// NVimComparisonOperator -> NVimOperator
|
// NVimComparisonOperator -> NVimOperator
|
||||||
// NVimTernaryOperator -> NVimOperator
|
// NVimTernary -> NVimOperator
|
||||||
|
// NVimTernaryColon -> NVimTernary
|
||||||
//
|
//
|
||||||
// NVimParenthesis -> Delimiter
|
// NVimParenthesis -> Delimiter
|
||||||
//
|
//
|
||||||
@@ -790,7 +792,8 @@ static inline void viml_pexpr_debug_print_token(
|
|||||||
//
|
//
|
||||||
// NVimInvalidComma -> NVimInvalidDelimiter
|
// NVimInvalidComma -> NVimInvalidDelimiter
|
||||||
// NVimInvalidSpacing -> NVimInvalid
|
// NVimInvalidSpacing -> NVimInvalid
|
||||||
// NVimInvalidTernaryOperator -> NVimInvalidOperator
|
// NVimInvalidTernary -> NVimInvalidOperator
|
||||||
|
// NVimInvalidTernaryColon -> NVimInvalidTernary
|
||||||
// NVimInvalidRegister -> NVimInvalidValue
|
// NVimInvalidRegister -> NVimInvalidValue
|
||||||
// NVimInvalidClosingBracket -> NVimInvalidDelimiter
|
// NVimInvalidClosingBracket -> NVimInvalidDelimiter
|
||||||
// NVimInvalidSpacing -> NVimInvalid
|
// NVimInvalidSpacing -> NVimInvalid
|
||||||
@@ -823,30 +826,6 @@ static inline ExprASTNode *viml_pexpr_new_node(const ExprASTNodeType type)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
kEOpLvlInvalid = 0,
|
|
||||||
kEOpLvlComplexIdentifier,
|
|
||||||
kEOpLvlParens,
|
|
||||||
kEOpLvlArrow,
|
|
||||||
kEOpLvlComma,
|
|
||||||
kEOpLvlColon,
|
|
||||||
kEOpLvlTernary,
|
|
||||||
kEOpLvlOr,
|
|
||||||
kEOpLvlAnd,
|
|
||||||
kEOpLvlComparison,
|
|
||||||
kEOpLvlAddition, ///< Addition, subtraction and concatenation.
|
|
||||||
kEOpLvlMultiplication, ///< Multiplication, division and modulo.
|
|
||||||
kEOpLvlUnary, ///< Unary operations: not, minus, plus.
|
|
||||||
kEOpLvlSubscript, ///< Subscripts.
|
|
||||||
kEOpLvlValue, ///< Values: literals, variables, nested expressions, …
|
|
||||||
} ExprOpLvl;
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
kEOpAssNo= 'n', ///< Not associative / not applicable.
|
|
||||||
kEOpAssLeft = 'l', ///< Left associativity.
|
|
||||||
kEOpAssRight = 'r', ///< Right associativity.
|
|
||||||
} ExprOpAssociativity;
|
|
||||||
|
|
||||||
static const ExprOpLvl node_type_to_op_lvl[] = {
|
static const ExprOpLvl node_type_to_op_lvl[] = {
|
||||||
[kExprNodeMissing] = kEOpLvlInvalid,
|
[kExprNodeMissing] = kEOpLvlInvalid,
|
||||||
[kExprNodeOpMissing] = kEOpLvlMultiplication,
|
[kExprNodeOpMissing] = kEOpLvlMultiplication,
|
||||||
@@ -868,6 +847,8 @@ static const ExprOpLvl node_type_to_op_lvl[] = {
|
|||||||
|
|
||||||
[kExprNodeTernary] = kEOpLvlTernary,
|
[kExprNodeTernary] = kEOpLvlTernary,
|
||||||
|
|
||||||
|
[kExprNodeTernaryValue] = kEOpLvlTernaryValue,
|
||||||
|
|
||||||
[kExprNodeBinaryPlus] = kEOpLvlAddition,
|
[kExprNodeBinaryPlus] = kEOpLvlAddition,
|
||||||
|
|
||||||
[kExprNodeUnaryPlus] = kEOpLvlUnary,
|
[kExprNodeUnaryPlus] = kEOpLvlUnary,
|
||||||
@@ -907,7 +888,9 @@ static const ExprOpAssociativity node_type_to_op_ass[] = {
|
|||||||
// about associativity, only about order of execution.
|
// about associativity, only about order of execution.
|
||||||
[kExprNodeComma] = kEOpAssRight,
|
[kExprNodeComma] = kEOpAssRight,
|
||||||
|
|
||||||
[kExprNodeTernary] = kEOpAssNo,
|
[kExprNodeTernary] = kEOpAssRight,
|
||||||
|
|
||||||
|
[kExprNodeTernaryValue] = kEOpAssRight,
|
||||||
|
|
||||||
[kExprNodeBinaryPlus] = kEOpAssLeft,
|
[kExprNodeBinaryPlus] = kEOpAssLeft,
|
||||||
|
|
||||||
@@ -1450,9 +1433,20 @@ viml_pexpr_parse_invalid_comma:
|
|||||||
const ExprOpLvl eastnode_lvl = node_lvl(**eastnode_p);
|
const ExprOpLvl eastnode_lvl = node_lvl(**eastnode_p);
|
||||||
STATIC_ASSERT(kEOpLvlTernary > kEOpLvlComma,
|
STATIC_ASSERT(kEOpLvlTernary > kEOpLvlComma,
|
||||||
"Unexpected operator priorities");
|
"Unexpected operator priorities");
|
||||||
if (can_be_ternary && eastnode_lvl == kEOpLvlTernary) {
|
if (can_be_ternary && eastnode_type == kExprNodeTernaryValue
|
||||||
assert(eastnode_type == kExprNodeTernary);
|
&& !(*eastnode_p)->data.ter.got_colon) {
|
||||||
|
kv_drop(ast_stack, i);
|
||||||
|
(*eastnode_p)->start = cur_token.start;
|
||||||
|
(*eastnode_p)->len = cur_token.len;
|
||||||
|
if (prev_token.type == kExprLexSpacing) {
|
||||||
|
(*eastnode_p)->start = prev_token.start;
|
||||||
|
(*eastnode_p)->len += prev_token.len;
|
||||||
|
}
|
||||||
is_ternary = true;
|
is_ternary = true;
|
||||||
|
(*eastnode_p)->data.ter.got_colon = true;
|
||||||
|
assert((*eastnode_p)->children != NULL);
|
||||||
|
assert((*eastnode_p)->children->next == NULL);
|
||||||
|
kvi_push(ast_stack, &(*eastnode_p)->children->next);
|
||||||
break;
|
break;
|
||||||
} else if (eastnode_type == kExprNodeUnknownFigure) {
|
} else if (eastnode_type == kExprNodeUnknownFigure) {
|
||||||
SELECT_FIGURE_BRACE_TYPE(*eastnode_p, DictLiteral, Dict);
|
SELECT_FIGURE_BRACE_TYPE(*eastnode_p, DictLiteral, Dict);
|
||||||
@@ -1460,7 +1454,7 @@ viml_pexpr_parse_invalid_comma:
|
|||||||
} else if (eastnode_type == kExprNodeDictLiteral
|
} else if (eastnode_type == kExprNodeDictLiteral
|
||||||
|| eastnode_type == kExprNodeComma) {
|
|| eastnode_type == kExprNodeComma) {
|
||||||
break;
|
break;
|
||||||
} else if (eastnode_lvl > kEOpLvlTernary) {
|
} else if (eastnode_lvl >= kEOpLvlTernaryValue) {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
} else if (eastnode_lvl > kEOpLvlComma) {
|
} else if (eastnode_lvl > kEOpLvlComma) {
|
||||||
can_be_ternary = false;
|
can_be_ternary = false;
|
||||||
@@ -1476,11 +1470,11 @@ viml_pexpr_parse_invalid_colon:
|
|||||||
goto viml_pexpr_parse_invalid_colon;
|
goto viml_pexpr_parse_invalid_colon;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeColon);
|
|
||||||
viml_pexpr_handle_bop(&ast_stack, cur_node, &want_node);
|
|
||||||
if (is_ternary) {
|
if (is_ternary) {
|
||||||
HL_CUR_TOKEN(TernaryColon);
|
HL_CUR_TOKEN(TernaryColon);
|
||||||
} else {
|
} else {
|
||||||
|
NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeColon);
|
||||||
|
viml_pexpr_handle_bop(&ast_stack, cur_node, &want_node);
|
||||||
HL_CUR_TOKEN(Colon);
|
HL_CUR_TOKEN(Colon);
|
||||||
}
|
}
|
||||||
want_node = kENodeValue;
|
want_node = kENodeValue;
|
||||||
@@ -1683,8 +1677,6 @@ viml_pexpr_parse_figure_brace_closing_error:
|
|||||||
cur_token.len - scope_shift,
|
cur_token.len - scope_shift,
|
||||||
HL(Identifier));
|
HL(Identifier));
|
||||||
}
|
}
|
||||||
// FIXME: Actually, g{foo}g:foo is valid: "1?g{foo}g:foo" is like
|
|
||||||
// "g{foo}g" and not an error.
|
|
||||||
} else {
|
} else {
|
||||||
if (cur_token.data.var.scope == 0) {
|
if (cur_token.data.var.scope == 0) {
|
||||||
ADD_IDENT(
|
ADD_IDENT(
|
||||||
@@ -1792,6 +1784,21 @@ viml_pexpr_parse_no_paren_closing_error: {}
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case kExprLexQuestion: {
|
||||||
|
ADD_VALUE_IF_MISSING(_("E15: Expected value, got question mark: %.*s"));
|
||||||
|
NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeTernary);
|
||||||
|
viml_pexpr_handle_bop(&ast_stack, cur_node, &want_node);
|
||||||
|
HL_CUR_TOKEN(Ternary);
|
||||||
|
ExprASTNode *ter_val_node;
|
||||||
|
NEW_NODE_WITH_CUR_POS(ter_val_node, kExprNodeTernaryValue);
|
||||||
|
ter_val_node->data.ter.got_colon = false;
|
||||||
|
assert(cur_node->children != NULL);
|
||||||
|
assert(cur_node->children->next == NULL);
|
||||||
|
assert(kv_last(ast_stack) == &cur_node->children->next);
|
||||||
|
*kv_last(ast_stack) = ter_val_node;
|
||||||
|
kvi_push(ast_stack, &ter_val_node->children);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
viml_pexpr_parse_cycle_end:
|
viml_pexpr_parse_cycle_end:
|
||||||
prev_token = cur_token;
|
prev_token = cur_token;
|
||||||
@@ -1815,6 +1822,7 @@ viml_pexpr_parse_end:
|
|||||||
const ExprASTNode *const cur_node = (*kv_pop(ast_stack));
|
const ExprASTNode *const cur_node = (*kv_pop(ast_stack));
|
||||||
// This should only happen when want_node == kENodeValue.
|
// This should only happen when want_node == kENodeValue.
|
||||||
assert(cur_node != NULL);
|
assert(cur_node != NULL);
|
||||||
|
// TODO(ZyX-I): Rehighlight as invalid?
|
||||||
switch (cur_node->type) {
|
switch (cur_node->type) {
|
||||||
case kExprNodeOpMissing:
|
case kExprNodeOpMissing:
|
||||||
case kExprNodeMissing: {
|
case kExprNodeMissing: {
|
||||||
@@ -1822,7 +1830,6 @@ viml_pexpr_parse_end:
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case kExprNodeCall: {
|
case kExprNodeCall: {
|
||||||
// TODO(ZyX-I): Rehighlight as invalid?
|
|
||||||
east_set_error(
|
east_set_error(
|
||||||
&ast, pstate,
|
&ast, pstate,
|
||||||
_("E116: Missing closing parenthesis for function call: %.*s"),
|
_("E116: Missing closing parenthesis for function call: %.*s"),
|
||||||
@@ -1830,7 +1837,6 @@ viml_pexpr_parse_end:
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case kExprNodeNested: {
|
case kExprNodeNested: {
|
||||||
// TODO(ZyX-I): Rehighlight as invalid?
|
|
||||||
east_set_error(
|
east_set_error(
|
||||||
&ast, pstate,
|
&ast, pstate,
|
||||||
_("E110: Missing closing parenthesis for nested expression"
|
_("E110: Missing closing parenthesis for nested expression"
|
||||||
@@ -1844,6 +1850,15 @@ viml_pexpr_parse_end:
|
|||||||
// It is OK to see these in the stack.
|
// It is OK to see these in the stack.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case kExprNodeTernaryValue: {
|
||||||
|
if (!cur_node->data.ter.got_colon) {
|
||||||
|
// Actually Vim throws E109 in more cases.
|
||||||
|
east_set_error(
|
||||||
|
&ast, pstate, _("E109: Missing ':' after '?': %.*s"),
|
||||||
|
cur_node->start);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
// TODO(ZyX-I): handle other values
|
// TODO(ZyX-I): handle other values
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -145,6 +145,7 @@ typedef enum {
|
|||||||
kExprNodeMissing = 'X',
|
kExprNodeMissing = 'X',
|
||||||
kExprNodeOpMissing = '_',
|
kExprNodeOpMissing = '_',
|
||||||
kExprNodeTernary = '?', ///< Ternary operator.
|
kExprNodeTernary = '?', ///< Ternary operator.
|
||||||
|
kExprNodeTernaryValue = 'C', ///< Ternary operator, colon.
|
||||||
kExprNodeRegister = '@', ///< Register.
|
kExprNodeRegister = '@', ///< Register.
|
||||||
kExprNodeSubscript = 's', ///< Subscript.
|
kExprNodeSubscript = 's', ///< Subscript.
|
||||||
kExprNodeListLiteral = 'l', ///< List literal.
|
kExprNodeListLiteral = 'l', ///< List literal.
|
||||||
@@ -209,7 +210,10 @@ struct expr_ast_node {
|
|||||||
/// Points to inside parser reader state.
|
/// Points to inside parser reader state.
|
||||||
const char *ident;
|
const char *ident;
|
||||||
size_t ident_len; ///< Actual identifier length.
|
size_t ident_len; ///< Actual identifier length.
|
||||||
} var;
|
} var; ///< For kExprNodePlainIdentifier.
|
||||||
|
struct {
|
||||||
|
bool got_colon; ///< True if colon was seen.
|
||||||
|
} ter; ///< For kExprNodeTernaryValue.
|
||||||
} data;
|
} data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -66,6 +66,7 @@ make_enum_conv_tab(lib, {
|
|||||||
'kExprNodeMissing',
|
'kExprNodeMissing',
|
||||||
'kExprNodeOpMissing',
|
'kExprNodeOpMissing',
|
||||||
'kExprNodeTernary',
|
'kExprNodeTernary',
|
||||||
|
'kExprNodeTernaryValue',
|
||||||
'kExprNodeRegister',
|
'kExprNodeRegister',
|
||||||
'kExprNodeSubscript',
|
'kExprNodeSubscript',
|
||||||
'kExprNodeListLiteral',
|
'kExprNodeListLiteral',
|
||||||
@@ -2489,6 +2490,652 @@ describe('Expressions parser', function()
|
|||||||
hl('Dict', '}'),
|
hl('Dict', '}'),
|
||||||
})
|
})
|
||||||
end)
|
end)
|
||||||
-- FIXME: Test sequence of arrows inside and outside lambdas.
|
itp('works with ternary operator', function()
|
||||||
-- FIXME: Test autoload character and scope in lambda arguments.
|
check_parsing('a ? b : c', 0, {
|
||||||
|
-- 012345678
|
||||||
|
ast = {
|
||||||
|
{
|
||||||
|
'Ternary:0:1: ?',
|
||||||
|
children = {
|
||||||
|
'PlainIdentifier(scope=0,ident=a):0:0:a',
|
||||||
|
{
|
||||||
|
'TernaryValue:0:5: :',
|
||||||
|
children = {
|
||||||
|
'PlainIdentifier(scope=0,ident=b):0:3: b',
|
||||||
|
'PlainIdentifier(scope=0,ident=c):0:7: c',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
hl('Identifier', 'a'),
|
||||||
|
hl('Ternary', '?', 1),
|
||||||
|
hl('Identifier', 'b', 1),
|
||||||
|
hl('TernaryColon', ':', 1),
|
||||||
|
hl('Identifier', 'c', 1),
|
||||||
|
})
|
||||||
|
check_parsing('@a?@b?@c:@d:@e', 0, {
|
||||||
|
-- 01234567890123
|
||||||
|
-- 0 1
|
||||||
|
ast = {
|
||||||
|
{
|
||||||
|
'Ternary:0:2:?',
|
||||||
|
children = {
|
||||||
|
'Register(name=a):0:0:@a',
|
||||||
|
{
|
||||||
|
'TernaryValue:0:11::',
|
||||||
|
children = {
|
||||||
|
{
|
||||||
|
'Ternary:0:5:?',
|
||||||
|
children = {
|
||||||
|
'Register(name=b):0:3:@b',
|
||||||
|
{
|
||||||
|
'TernaryValue:0:8::',
|
||||||
|
children = {
|
||||||
|
'Register(name=c):0:6:@c',
|
||||||
|
'Register(name=d):0:9:@d',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'Register(name=e):0:12:@e',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
hl('Register', '@a'),
|
||||||
|
hl('Ternary', '?'),
|
||||||
|
hl('Register', '@b'),
|
||||||
|
hl('Ternary', '?'),
|
||||||
|
hl('Register', '@c'),
|
||||||
|
hl('TernaryColon', ':'),
|
||||||
|
hl('Register', '@d'),
|
||||||
|
hl('TernaryColon', ':'),
|
||||||
|
hl('Register', '@e'),
|
||||||
|
})
|
||||||
|
check_parsing('@a?@b:@c?@d:@e', 0, {
|
||||||
|
-- 01234567890123
|
||||||
|
-- 0 1
|
||||||
|
ast = {
|
||||||
|
{
|
||||||
|
'Ternary:0:2:?',
|
||||||
|
children = {
|
||||||
|
'Register(name=a):0:0:@a',
|
||||||
|
{
|
||||||
|
'TernaryValue:0:5::',
|
||||||
|
children = {
|
||||||
|
'Register(name=b):0:3:@b',
|
||||||
|
{
|
||||||
|
'Ternary:0:8:?',
|
||||||
|
children = {
|
||||||
|
'Register(name=c):0:6:@c',
|
||||||
|
{
|
||||||
|
'TernaryValue:0:11::',
|
||||||
|
children = {
|
||||||
|
'Register(name=d):0:9:@d',
|
||||||
|
'Register(name=e):0:12:@e',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
hl('Register', '@a'),
|
||||||
|
hl('Ternary', '?'),
|
||||||
|
hl('Register', '@b'),
|
||||||
|
hl('TernaryColon', ':'),
|
||||||
|
hl('Register', '@c'),
|
||||||
|
hl('Ternary', '?'),
|
||||||
|
hl('Register', '@d'),
|
||||||
|
hl('TernaryColon', ':'),
|
||||||
|
hl('Register', '@e'),
|
||||||
|
})
|
||||||
|
check_parsing('@a?@b?@c?@d:@e?@f:@g:@h?@i:@j:@k', 0, {
|
||||||
|
-- 01234567890123456789012345678901
|
||||||
|
-- 0 1 2 3
|
||||||
|
ast = {
|
||||||
|
{
|
||||||
|
'Ternary:0:2:?',
|
||||||
|
children = {
|
||||||
|
'Register(name=a):0:0:@a',
|
||||||
|
{
|
||||||
|
'TernaryValue:0:29::',
|
||||||
|
children = {
|
||||||
|
{
|
||||||
|
'Ternary:0:5:?',
|
||||||
|
children = {
|
||||||
|
'Register(name=b):0:3:@b',
|
||||||
|
{
|
||||||
|
'TernaryValue:0:20::',
|
||||||
|
children = {
|
||||||
|
{
|
||||||
|
'Ternary:0:8:?',
|
||||||
|
children = {
|
||||||
|
'Register(name=c):0:6:@c',
|
||||||
|
{
|
||||||
|
'TernaryValue:0:11::',
|
||||||
|
children = {
|
||||||
|
'Register(name=d):0:9:@d',
|
||||||
|
{
|
||||||
|
'Ternary:0:14:?',
|
||||||
|
children = {
|
||||||
|
'Register(name=e):0:12:@e',
|
||||||
|
{
|
||||||
|
'TernaryValue:0:17::',
|
||||||
|
children = {
|
||||||
|
'Register(name=f):0:15:@f',
|
||||||
|
'Register(name=g):0:18:@g',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'Ternary:0:23:?',
|
||||||
|
children = {
|
||||||
|
'Register(name=h):0:21:@h',
|
||||||
|
{
|
||||||
|
'TernaryValue:0:26::',
|
||||||
|
children = {
|
||||||
|
'Register(name=i):0:24:@i',
|
||||||
|
'Register(name=j):0:27:@j',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'Register(name=k):0:30:@k',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
hl('Register', '@a'),
|
||||||
|
hl('Ternary', '?'),
|
||||||
|
hl('Register', '@b'),
|
||||||
|
hl('Ternary', '?'),
|
||||||
|
hl('Register', '@c'),
|
||||||
|
hl('Ternary', '?'),
|
||||||
|
hl('Register', '@d'),
|
||||||
|
hl('TernaryColon', ':'),
|
||||||
|
hl('Register', '@e'),
|
||||||
|
hl('Ternary', '?'),
|
||||||
|
hl('Register', '@f'),
|
||||||
|
hl('TernaryColon', ':'),
|
||||||
|
hl('Register', '@g'),
|
||||||
|
hl('TernaryColon', ':'),
|
||||||
|
hl('Register', '@h'),
|
||||||
|
hl('Ternary', '?'),
|
||||||
|
hl('Register', '@i'),
|
||||||
|
hl('TernaryColon', ':'),
|
||||||
|
hl('Register', '@j'),
|
||||||
|
hl('TernaryColon', ':'),
|
||||||
|
hl('Register', '@k'),
|
||||||
|
})
|
||||||
|
check_parsing('?', 0, {
|
||||||
|
-- 0
|
||||||
|
ast = {
|
||||||
|
{
|
||||||
|
'Ternary:0:0:?',
|
||||||
|
children = {
|
||||||
|
'Missing:0:0:',
|
||||||
|
'TernaryValue:0:0:?',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
err = {
|
||||||
|
arg = '?',
|
||||||
|
msg = 'E15: Expected value, got question mark: %.*s',
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
hl('InvalidTernary', '?'),
|
||||||
|
})
|
||||||
|
|
||||||
|
check_parsing('?:', 0, {
|
||||||
|
-- 01
|
||||||
|
ast = {
|
||||||
|
{
|
||||||
|
'Ternary:0:0:?',
|
||||||
|
children = {
|
||||||
|
'Missing:0:0:',
|
||||||
|
{
|
||||||
|
'TernaryValue:0:1::',
|
||||||
|
children = {
|
||||||
|
'Missing:0:1:',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
err = {
|
||||||
|
arg = '?:',
|
||||||
|
msg = 'E15: Expected value, got question mark: %.*s',
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
hl('InvalidTernary', '?'),
|
||||||
|
hl('InvalidTernaryColon', ':'),
|
||||||
|
})
|
||||||
|
|
||||||
|
check_parsing('?::', 0, {
|
||||||
|
-- 012
|
||||||
|
ast = {
|
||||||
|
{
|
||||||
|
'Colon:0:2::',
|
||||||
|
children = {
|
||||||
|
{
|
||||||
|
'Ternary:0:0:?',
|
||||||
|
children = {
|
||||||
|
'Missing:0:0:',
|
||||||
|
{
|
||||||
|
'TernaryValue:0:1::',
|
||||||
|
children = {
|
||||||
|
'Missing:0:1:',
|
||||||
|
'Missing:0:2:',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
err = {
|
||||||
|
arg = '?::',
|
||||||
|
msg = 'E15: Expected value, got question mark: %.*s',
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
hl('InvalidTernary', '?'),
|
||||||
|
hl('InvalidTernaryColon', ':'),
|
||||||
|
hl('InvalidColon', ':'),
|
||||||
|
})
|
||||||
|
|
||||||
|
check_parsing('a?b', 0, {
|
||||||
|
-- 012
|
||||||
|
ast = {
|
||||||
|
{
|
||||||
|
'Ternary:0:1:?',
|
||||||
|
children = {
|
||||||
|
'PlainIdentifier(scope=0,ident=a):0:0:a',
|
||||||
|
{
|
||||||
|
'TernaryValue:0:1:?',
|
||||||
|
children = {
|
||||||
|
'PlainIdentifier(scope=0,ident=b):0:2:b',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
err = {
|
||||||
|
arg = '?b',
|
||||||
|
msg = 'E109: Missing \':\' after \'?\': %.*s',
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
hl('Identifier', 'a'),
|
||||||
|
hl('Ternary', '?'),
|
||||||
|
hl('Identifier', 'b'),
|
||||||
|
})
|
||||||
|
check_parsing('a?b:', 0, {
|
||||||
|
-- 0123
|
||||||
|
ast = {
|
||||||
|
{
|
||||||
|
'Ternary:0:1:?',
|
||||||
|
children = {
|
||||||
|
'PlainIdentifier(scope=0,ident=a):0:0:a',
|
||||||
|
{
|
||||||
|
'TernaryValue:0:1:?',
|
||||||
|
children = {
|
||||||
|
'PlainIdentifier(scope=b,ident=):0:2:b:',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
err = {
|
||||||
|
arg = '?b:',
|
||||||
|
msg = 'E109: Missing \':\' after \'?\': %.*s',
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
hl('Identifier', 'a'),
|
||||||
|
hl('Ternary', '?'),
|
||||||
|
hl('IdentifierScope', 'b'),
|
||||||
|
hl('IdentifierScopeDelimiter', ':'),
|
||||||
|
})
|
||||||
|
|
||||||
|
check_parsing('a?b::c', 0, {
|
||||||
|
-- 012345
|
||||||
|
ast = {
|
||||||
|
{
|
||||||
|
'Ternary:0:1:?',
|
||||||
|
children = {
|
||||||
|
'PlainIdentifier(scope=0,ident=a):0:0:a',
|
||||||
|
{
|
||||||
|
'TernaryValue:0:4::',
|
||||||
|
children = {
|
||||||
|
'PlainIdentifier(scope=b,ident=):0:2:b:',
|
||||||
|
'PlainIdentifier(scope=0,ident=c):0:5:c',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
hl('Identifier', 'a'),
|
||||||
|
hl('Ternary', '?'),
|
||||||
|
hl('IdentifierScope', 'b'),
|
||||||
|
hl('IdentifierScopeDelimiter', ':'),
|
||||||
|
hl('TernaryColon', ':'),
|
||||||
|
hl('Identifier', 'c'),
|
||||||
|
})
|
||||||
|
|
||||||
|
check_parsing('a?b :', 0, {
|
||||||
|
-- 01234
|
||||||
|
ast = {
|
||||||
|
{
|
||||||
|
'Ternary:0:1:?',
|
||||||
|
children = {
|
||||||
|
'PlainIdentifier(scope=0,ident=a):0:0:a',
|
||||||
|
{
|
||||||
|
'TernaryValue:0:3: :',
|
||||||
|
children = {
|
||||||
|
'PlainIdentifier(scope=0,ident=b):0:2:b',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
err = {
|
||||||
|
arg = '',
|
||||||
|
msg = 'E15: Expected value, got EOC: %.*s',
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
hl('Identifier', 'a'),
|
||||||
|
hl('Ternary', '?'),
|
||||||
|
hl('Identifier', 'b'),
|
||||||
|
hl('TernaryColon', ':', 1),
|
||||||
|
})
|
||||||
|
|
||||||
|
check_parsing('(@a?@b:@c)?@d:@e', 0, {
|
||||||
|
-- 0123456789012345
|
||||||
|
-- 0 1
|
||||||
|
ast = {
|
||||||
|
{
|
||||||
|
'Ternary:0:10:?',
|
||||||
|
children = {
|
||||||
|
{
|
||||||
|
'Nested:0:0:(',
|
||||||
|
children = {
|
||||||
|
{
|
||||||
|
'Ternary:0:3:?',
|
||||||
|
children = {
|
||||||
|
'Register(name=a):0:1:@a',
|
||||||
|
{
|
||||||
|
'TernaryValue:0:6::',
|
||||||
|
children = {
|
||||||
|
'Register(name=b):0:4:@b',
|
||||||
|
'Register(name=c):0:7:@c',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'TernaryValue:0:13::',
|
||||||
|
children = {
|
||||||
|
'Register(name=d):0:11:@d',
|
||||||
|
'Register(name=e):0:14:@e',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
hl('NestingParenthesis', '('),
|
||||||
|
hl('Register', '@a'),
|
||||||
|
hl('Ternary', '?'),
|
||||||
|
hl('Register', '@b'),
|
||||||
|
hl('TernaryColon', ':'),
|
||||||
|
hl('Register', '@c'),
|
||||||
|
hl('NestingParenthesis', ')'),
|
||||||
|
hl('Ternary', '?'),
|
||||||
|
hl('Register', '@d'),
|
||||||
|
hl('TernaryColon', ':'),
|
||||||
|
hl('Register', '@e'),
|
||||||
|
})
|
||||||
|
|
||||||
|
check_parsing('(@a?@b:@c)?(@d?@e:@f):(@g?@h:@i)', 0, {
|
||||||
|
-- 01234567890123456789012345678901
|
||||||
|
-- 0 1 2 3
|
||||||
|
ast = {
|
||||||
|
{
|
||||||
|
'Ternary:0:10:?',
|
||||||
|
children = {
|
||||||
|
{
|
||||||
|
'Nested:0:0:(',
|
||||||
|
children = {
|
||||||
|
{
|
||||||
|
'Ternary:0:3:?',
|
||||||
|
children = {
|
||||||
|
'Register(name=a):0:1:@a',
|
||||||
|
{
|
||||||
|
'TernaryValue:0:6::',
|
||||||
|
children = {
|
||||||
|
'Register(name=b):0:4:@b',
|
||||||
|
'Register(name=c):0:7:@c',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'TernaryValue:0:21::',
|
||||||
|
children = {
|
||||||
|
{
|
||||||
|
'Nested:0:11:(',
|
||||||
|
children = {
|
||||||
|
{
|
||||||
|
'Ternary:0:14:?',
|
||||||
|
children = {
|
||||||
|
'Register(name=d):0:12:@d',
|
||||||
|
{
|
||||||
|
'TernaryValue:0:17::',
|
||||||
|
children = {
|
||||||
|
'Register(name=e):0:15:@e',
|
||||||
|
'Register(name=f):0:18:@f',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'Nested:0:22:(',
|
||||||
|
children = {
|
||||||
|
{
|
||||||
|
'Ternary:0:25:?',
|
||||||
|
children = {
|
||||||
|
'Register(name=g):0:23:@g',
|
||||||
|
{
|
||||||
|
'TernaryValue:0:28::',
|
||||||
|
children = {
|
||||||
|
'Register(name=h):0:26:@h',
|
||||||
|
'Register(name=i):0:29:@i',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
hl('NestingParenthesis', '('),
|
||||||
|
hl('Register', '@a'),
|
||||||
|
hl('Ternary', '?'),
|
||||||
|
hl('Register', '@b'),
|
||||||
|
hl('TernaryColon', ':'),
|
||||||
|
hl('Register', '@c'),
|
||||||
|
hl('NestingParenthesis', ')'),
|
||||||
|
hl('Ternary', '?'),
|
||||||
|
hl('NestingParenthesis', '('),
|
||||||
|
hl('Register', '@d'),
|
||||||
|
hl('Ternary', '?'),
|
||||||
|
hl('Register', '@e'),
|
||||||
|
hl('TernaryColon', ':'),
|
||||||
|
hl('Register', '@f'),
|
||||||
|
hl('NestingParenthesis', ')'),
|
||||||
|
hl('TernaryColon', ':'),
|
||||||
|
hl('NestingParenthesis', '('),
|
||||||
|
hl('Register', '@g'),
|
||||||
|
hl('Ternary', '?'),
|
||||||
|
hl('Register', '@h'),
|
||||||
|
hl('TernaryColon', ':'),
|
||||||
|
hl('Register', '@i'),
|
||||||
|
hl('NestingParenthesis', ')'),
|
||||||
|
})
|
||||||
|
|
||||||
|
check_parsing('(@a?@b:@c)?@d?@e:@f:@g?@h:@i', 0, {
|
||||||
|
-- 0123456789012345678901234567
|
||||||
|
-- 0 1 2
|
||||||
|
ast = {
|
||||||
|
{
|
||||||
|
'Ternary:0:10:?',
|
||||||
|
children = {
|
||||||
|
{
|
||||||
|
'Nested:0:0:(',
|
||||||
|
children = {
|
||||||
|
{
|
||||||
|
'Ternary:0:3:?',
|
||||||
|
children = {
|
||||||
|
'Register(name=a):0:1:@a',
|
||||||
|
{
|
||||||
|
'TernaryValue:0:6::',
|
||||||
|
children = {
|
||||||
|
'Register(name=b):0:4:@b',
|
||||||
|
'Register(name=c):0:7:@c',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'TernaryValue:0:19::',
|
||||||
|
children = {
|
||||||
|
{
|
||||||
|
'Ternary:0:13:?',
|
||||||
|
children = {
|
||||||
|
'Register(name=d):0:11:@d',
|
||||||
|
{
|
||||||
|
'TernaryValue:0:16::',
|
||||||
|
children = {
|
||||||
|
'Register(name=e):0:14:@e',
|
||||||
|
'Register(name=f):0:17:@f',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'Ternary:0:22:?',
|
||||||
|
children = {
|
||||||
|
'Register(name=g):0:20:@g',
|
||||||
|
{
|
||||||
|
'TernaryValue:0:25::',
|
||||||
|
children = {
|
||||||
|
'Register(name=h):0:23:@h',
|
||||||
|
'Register(name=i):0:26:@i',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
hl('NestingParenthesis', '('),
|
||||||
|
hl('Register', '@a'),
|
||||||
|
hl('Ternary', '?'),
|
||||||
|
hl('Register', '@b'),
|
||||||
|
hl('TernaryColon', ':'),
|
||||||
|
hl('Register', '@c'),
|
||||||
|
hl('NestingParenthesis', ')'),
|
||||||
|
hl('Ternary', '?'),
|
||||||
|
hl('Register', '@d'),
|
||||||
|
hl('Ternary', '?'),
|
||||||
|
hl('Register', '@e'),
|
||||||
|
hl('TernaryColon', ':'),
|
||||||
|
hl('Register', '@f'),
|
||||||
|
hl('TernaryColon', ':'),
|
||||||
|
hl('Register', '@g'),
|
||||||
|
hl('Ternary', '?'),
|
||||||
|
hl('Register', '@h'),
|
||||||
|
hl('TernaryColon', ':'),
|
||||||
|
hl('Register', '@i'),
|
||||||
|
})
|
||||||
|
check_parsing('a?b{cdef}g:h', 0, {
|
||||||
|
-- 012345678901
|
||||||
|
-- 0 1
|
||||||
|
ast = {
|
||||||
|
{
|
||||||
|
'Ternary:0:1:?',
|
||||||
|
children = {
|
||||||
|
'PlainIdentifier(scope=0,ident=a):0:0:a',
|
||||||
|
{
|
||||||
|
'TernaryValue:0:10::',
|
||||||
|
children = {
|
||||||
|
{
|
||||||
|
'ComplexIdentifier:0:3:',
|
||||||
|
children = {
|
||||||
|
'PlainIdentifier(scope=0,ident=b):0:2:b',
|
||||||
|
{
|
||||||
|
'ComplexIdentifier:0:9:',
|
||||||
|
children = {
|
||||||
|
{
|
||||||
|
'CurlyBracesIdentifier(--i):0:3:{',
|
||||||
|
children = {
|
||||||
|
'PlainIdentifier(scope=0,ident=cdef):0:4:cdef',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'PlainIdentifier(scope=0,ident=g):0:9:g',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'PlainIdentifier(scope=0,ident=h):0:11:h',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
hl('Identifier', 'a'),
|
||||||
|
hl('Ternary', '?'),
|
||||||
|
hl('Identifier', 'b'),
|
||||||
|
hl('Curly', '{'),
|
||||||
|
hl('Identifier', 'cdef'),
|
||||||
|
hl('Curly', '}'),
|
||||||
|
hl('Identifier', 'g'),
|
||||||
|
hl('TernaryColon', ':'),
|
||||||
|
hl('Identifier', 'h'),
|
||||||
|
})
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
Reference in New Issue
Block a user