mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
viml/parser/expressions: Add support for comparison operators
This commit is contained in:
parent
6791c57420
commit
6168e1127c
@ -102,7 +102,7 @@ LexExprToken viml_pexpr_next_token(ParserState *const pstate, const int flags)
|
|||||||
if (ret.len < pline.size \
|
if (ret.len < pline.size \
|
||||||
&& strchr("?#", pline.data[ret.len]) != NULL) { \
|
&& strchr("?#", pline.data[ret.len]) != NULL) { \
|
||||||
ret.data.cmp.ccs = \
|
ret.data.cmp.ccs = \
|
||||||
(CaseCompareStrategy)pline.data[ret.len]; \
|
(ExprCaseCompareStrategy)pline.data[ret.len]; \
|
||||||
ret.len++; \
|
ret.len++; \
|
||||||
} else { \
|
} else { \
|
||||||
ret.data.cmp.ccs = kCCStrategyUseOption; \
|
ret.data.cmp.ccs = kCCStrategyUseOption; \
|
||||||
@ -240,7 +240,7 @@ LexExprToken viml_pexpr_next_token(ParserState *const pstate, const int flags)
|
|||||||
&& ((ret.len == 2 && memcmp(pline.data, "is", 2) == 0)
|
&& ((ret.len == 2 && memcmp(pline.data, "is", 2) == 0)
|
||||||
|| (ret.len == 5 && memcmp(pline.data, "isnot", 5) == 0))) {
|
|| (ret.len == 5 && memcmp(pline.data, "isnot", 5) == 0))) {
|
||||||
ret.type = kExprLexComparison;
|
ret.type = kExprLexComparison;
|
||||||
ret.data.cmp.type = kExprLexCmpIdentical;
|
ret.data.cmp.type = kExprCmpIdentical;
|
||||||
ret.data.cmp.inv = (ret.len == 5);
|
ret.data.cmp.inv = (ret.len == 5);
|
||||||
GET_CCS(ret, pline);
|
GET_CCS(ret, pline);
|
||||||
// Scope: `s:`, etc.
|
// Scope: `s:`, etc.
|
||||||
@ -381,10 +381,10 @@ viml_pexpr_next_token_invalid_comparison:
|
|||||||
ret.type = kExprLexComparison;
|
ret.type = kExprLexComparison;
|
||||||
ret.data.cmp.inv = (schar == '!');
|
ret.data.cmp.inv = (schar == '!');
|
||||||
if (pline.data[1] == '=') {
|
if (pline.data[1] == '=') {
|
||||||
ret.data.cmp.type = kExprLexCmpEqual;
|
ret.data.cmp.type = kExprCmpEqual;
|
||||||
ret.len++;
|
ret.len++;
|
||||||
} else if (pline.data[1] == '~') {
|
} else if (pline.data[1] == '~') {
|
||||||
ret.data.cmp.type = kExprLexCmpMatches;
|
ret.data.cmp.type = kExprCmpMatches;
|
||||||
ret.len++;
|
ret.len++;
|
||||||
} else {
|
} else {
|
||||||
goto viml_pexpr_next_token_invalid_comparison;
|
goto viml_pexpr_next_token_invalid_comparison;
|
||||||
@ -404,8 +404,8 @@ viml_pexpr_next_token_invalid_comparison:
|
|||||||
GET_CCS(ret, pline);
|
GET_CCS(ret, pline);
|
||||||
ret.data.cmp.inv = (schar == '<');
|
ret.data.cmp.inv = (schar == '<');
|
||||||
ret.data.cmp.type = ((ret.data.cmp.inv ^ haseqsign)
|
ret.data.cmp.type = ((ret.data.cmp.inv ^ haseqsign)
|
||||||
? kExprLexCmpGreaterOrEqual
|
? kExprCmpGreaterOrEqual
|
||||||
: kExprLexCmpGreater);
|
: kExprCmpGreater);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -503,11 +503,11 @@ static const char *const eltkn_type_tab[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static const char *const eltkn_cmp_type_tab[] = {
|
static const char *const eltkn_cmp_type_tab[] = {
|
||||||
[kExprLexCmpEqual] = "Equal",
|
[kExprCmpEqual] = "Equal",
|
||||||
[kExprLexCmpMatches] = "Matches",
|
[kExprCmpMatches] = "Matches",
|
||||||
[kExprLexCmpGreater] = "Greater",
|
[kExprCmpGreater] = "Greater",
|
||||||
[kExprLexCmpGreaterOrEqual] = "GreaterOrEqual",
|
[kExprCmpGreaterOrEqual] = "GreaterOrEqual",
|
||||||
[kExprLexCmpIdentical] = "Identical",
|
[kExprCmpIdentical] = "Identical",
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *const ccs_tab[] = {
|
static const char *const ccs_tab[] = {
|
||||||
@ -770,7 +770,8 @@ static inline void viml_pexpr_debug_print_token(
|
|||||||
// NVimOperator -> Operator
|
// NVimOperator -> Operator
|
||||||
// NVimUnaryOperator -> NVimOperator
|
// NVimUnaryOperator -> NVimOperator
|
||||||
// NVimBinaryOperator -> NVimOperator
|
// NVimBinaryOperator -> NVimOperator
|
||||||
// NVimComparisonOperator -> NVimOperator
|
// NVimComparisonOperator -> NVimBinaryOperator
|
||||||
|
// NVimComparisonOperatorModifier -> NVimComparisonOperator
|
||||||
// NVimTernary -> NVimOperator
|
// NVimTernary -> NVimOperator
|
||||||
// NVimTernaryColon -> NVimTernary
|
// NVimTernaryColon -> NVimTernary
|
||||||
//
|
//
|
||||||
@ -805,6 +806,8 @@ static inline void viml_pexpr_debug_print_token(
|
|||||||
// NVimInvalidIdentifier -> NVimInvalidValue
|
// NVimInvalidIdentifier -> NVimInvalidValue
|
||||||
// NVimInvalidIdentifierScope -> NVimInvalidValue
|
// NVimInvalidIdentifierScope -> NVimInvalidValue
|
||||||
// NVimInvalidIdentifierScopeDelimiter -> NVimInvalidValue
|
// NVimInvalidIdentifierScopeDelimiter -> NVimInvalidValue
|
||||||
|
// NVimInvalidComparisonOperator -> NVimInvalidOperator
|
||||||
|
// NVimInvalidComparisonOperatorModifier -> NVimInvalidComparisonOperator
|
||||||
//
|
//
|
||||||
// NVimUnaryPlus -> NVimUnaryOperator
|
// NVimUnaryPlus -> NVimUnaryOperator
|
||||||
// NVimBinaryPlus -> NVimBinaryOperator
|
// NVimBinaryPlus -> NVimBinaryOperator
|
||||||
@ -849,6 +852,8 @@ static const ExprOpLvl node_type_to_op_lvl[] = {
|
|||||||
|
|
||||||
[kExprNodeTernaryValue] = kEOpLvlTernaryValue,
|
[kExprNodeTernaryValue] = kEOpLvlTernaryValue,
|
||||||
|
|
||||||
|
[kExprNodeComparison] = kEOpLvlComparison,
|
||||||
|
|
||||||
[kExprNodeBinaryPlus] = kEOpLvlAddition,
|
[kExprNodeBinaryPlus] = kEOpLvlAddition,
|
||||||
|
|
||||||
[kExprNodeUnaryPlus] = kEOpLvlUnary,
|
[kExprNodeUnaryPlus] = kEOpLvlUnary,
|
||||||
@ -892,6 +897,8 @@ static const ExprOpAssociativity node_type_to_op_ass[] = {
|
|||||||
|
|
||||||
[kExprNodeTernaryValue] = kEOpAssRight,
|
[kExprNodeTernaryValue] = kEOpAssRight,
|
||||||
|
|
||||||
|
[kExprNodeComparison] = kEOpAssRight,
|
||||||
|
|
||||||
[kExprNodeBinaryPlus] = kEOpAssLeft,
|
[kExprNodeBinaryPlus] = kEOpAssLeft,
|
||||||
|
|
||||||
[kExprNodeUnaryPlus] = kEOpAssNo,
|
[kExprNodeUnaryPlus] = kEOpAssNo,
|
||||||
@ -935,11 +942,23 @@ static inline ExprOpAssociativity node_ass(const ExprASTNode node)
|
|||||||
/// Handle binary operator
|
/// Handle binary operator
|
||||||
///
|
///
|
||||||
/// This function is responsible for handling priority levels as well.
|
/// This function is responsible for handling priority levels as well.
|
||||||
static void viml_pexpr_handle_bop(ExprASTStack *const ast_stack,
|
///
|
||||||
|
/// @param[in] pstate Parser state, used for error reporting.
|
||||||
|
/// @param ast_stack AST stack. May be popped of some values and will
|
||||||
|
/// definitely receive new ones.
|
||||||
|
/// @param bop_node New node to handle.
|
||||||
|
/// @param[out] want_node_p New value of want_node.
|
||||||
|
/// @param[out] ast_err Location where error is saved, if any.
|
||||||
|
///
|
||||||
|
/// @return True if no errors occurred, false otherwise.
|
||||||
|
static bool viml_pexpr_handle_bop(const ParserState *const pstate,
|
||||||
|
ExprASTStack *const ast_stack,
|
||||||
ExprASTNode *const bop_node,
|
ExprASTNode *const bop_node,
|
||||||
ExprASTWantedNode *const want_node_p)
|
ExprASTWantedNode *const want_node_p,
|
||||||
|
ExprASTError *const ast_err)
|
||||||
FUNC_ATTR_NONNULL_ALL
|
FUNC_ATTR_NONNULL_ALL
|
||||||
{
|
{
|
||||||
|
bool ret = true;
|
||||||
ExprASTNode **top_node_p = NULL;
|
ExprASTNode **top_node_p = NULL;
|
||||||
ExprASTNode *top_node;
|
ExprASTNode *top_node;
|
||||||
ExprOpLvl top_node_lvl;
|
ExprOpLvl top_node_lvl;
|
||||||
@ -977,7 +996,6 @@ static void viml_pexpr_handle_bop(ExprASTStack *const ast_stack,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} while (kv_size(*ast_stack));
|
} while (kv_size(*ast_stack));
|
||||||
// FIXME: Handle no associativity
|
|
||||||
if (top_node_ass == kEOpAssLeft || top_node_lvl != bop_node_lvl) {
|
if (top_node_ass == kEOpAssLeft || top_node_lvl != bop_node_lvl) {
|
||||||
// outer(op(x,y)) -> outer(new_op(op(x,y),*))
|
// outer(op(x,y)) -> outer(new_op(op(x,y),*))
|
||||||
//
|
//
|
||||||
@ -1008,10 +1026,18 @@ static void viml_pexpr_handle_bop(ExprASTStack *const ast_stack,
|
|||||||
kvi_push(*ast_stack, top_node_p);
|
kvi_push(*ast_stack, top_node_p);
|
||||||
kvi_push(*ast_stack, &top_node->children->next);
|
kvi_push(*ast_stack, &top_node->children->next);
|
||||||
kvi_push(*ast_stack, &bop_node->children->next);
|
kvi_push(*ast_stack, &bop_node->children->next);
|
||||||
|
// TODO(ZyX-I): Make this not error, but treat like Python does
|
||||||
|
if (bop_node->type == kExprNodeComparison) {
|
||||||
|
east_set_error(pstate, ast_err,
|
||||||
|
_("E15: Operator is not associative: %.*s"),
|
||||||
|
bop_node->start);
|
||||||
|
ret = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
*want_node_p = (*want_node_p == kENodeArgumentSeparator
|
*want_node_p = (*want_node_p == kENodeArgumentSeparator
|
||||||
? kENodeArgument
|
? kENodeArgument
|
||||||
: kENodeValue);
|
: kENodeValue);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ParserPosition literal based on ParserPosition pos with columns shifted
|
/// ParserPosition literal based on ParserPosition pos with columns shifted
|
||||||
@ -1074,6 +1100,13 @@ static inline ParserPosition shifted_pos(const ParserPosition pos,
|
|||||||
#define MAY_HAVE_NEXT_EXPR \
|
#define MAY_HAVE_NEXT_EXPR \
|
||||||
(kv_size(ast_stack) == 1)
|
(kv_size(ast_stack) == 1)
|
||||||
|
|
||||||
|
/// Add operator node
|
||||||
|
///
|
||||||
|
/// @param[in] cur_node Node to add.
|
||||||
|
#define ADD_OP_NODE(cur_node) \
|
||||||
|
is_invalid |= !viml_pexpr_handle_bop(pstate, &ast_stack, cur_node, \
|
||||||
|
&want_node, &ast.err)
|
||||||
|
|
||||||
/// Record missing operator: for things like
|
/// Record missing operator: for things like
|
||||||
///
|
///
|
||||||
/// :echo @a @a
|
/// :echo @a @a
|
||||||
@ -1094,7 +1127,7 @@ static inline ParserPosition shifted_pos(const ParserPosition pos,
|
|||||||
ERROR_FROM_TOKEN_AND_MSG(cur_token, _("E15: Missing operator: %.*s")); \
|
ERROR_FROM_TOKEN_AND_MSG(cur_token, _("E15: Missing operator: %.*s")); \
|
||||||
NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeOpMissing); \
|
NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeOpMissing); \
|
||||||
cur_node->len = 0; \
|
cur_node->len = 0; \
|
||||||
viml_pexpr_handle_bop(&ast_stack, cur_node, &want_node); \
|
ADD_OP_NODE(cur_node); \
|
||||||
goto viml_pexpr_parse_process_token; \
|
goto viml_pexpr_parse_process_token; \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
@ -1120,34 +1153,33 @@ static inline ParserPosition shifted_pos(const ParserPosition pos,
|
|||||||
/// @param[in] msg Error message, assumed to be already translated and
|
/// @param[in] msg Error message, assumed to be already translated and
|
||||||
/// containing a single %token "%.*s".
|
/// containing a single %token "%.*s".
|
||||||
/// @param[in] start Position at which error occurred.
|
/// @param[in] start Position at which error occurred.
|
||||||
static inline void east_set_error(ExprAST *const ret_ast,
|
static inline void east_set_error(const ParserState *const pstate,
|
||||||
const ParserState *const pstate,
|
ExprASTError *const ret_ast_err,
|
||||||
const char *const msg,
|
const char *const msg,
|
||||||
const ParserPosition start)
|
const ParserPosition start)
|
||||||
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_ALWAYS_INLINE
|
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_ALWAYS_INLINE
|
||||||
{
|
{
|
||||||
if (!ret_ast->correct) {
|
if (ret_ast_err->msg != NULL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const ParserLine pline = pstate->reader.lines.items[start.line];
|
const ParserLine pline = pstate->reader.lines.items[start.line];
|
||||||
ret_ast->correct = false;
|
ret_ast_err->msg = msg;
|
||||||
ret_ast->err.msg = msg;
|
ret_ast_err->arg_len = (int)(pline.size - start.col);
|
||||||
ret_ast->err.arg_len = (int)(pline.size - start.col);
|
ret_ast_err->arg = pline.data + start.col;
|
||||||
ret_ast->err.arg = pline.data + start.col;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set error from the given token and given message
|
/// Set error from the given token and given message
|
||||||
#define ERROR_FROM_TOKEN_AND_MSG(cur_token, msg) \
|
#define ERROR_FROM_TOKEN_AND_MSG(cur_token, msg) \
|
||||||
do { \
|
do { \
|
||||||
is_invalid = true; \
|
is_invalid = true; \
|
||||||
east_set_error(&ast, pstate, msg, cur_token.start); \
|
east_set_error(pstate, &ast.err, msg, cur_token.start); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
/// Like #ERROR_FROM_TOKEN_AND_MSG, but gets position from a node
|
/// Like #ERROR_FROM_TOKEN_AND_MSG, but gets position from a node
|
||||||
#define ERROR_FROM_NODE_AND_MSG(node, msg) \
|
#define ERROR_FROM_NODE_AND_MSG(node, msg) \
|
||||||
do { \
|
do { \
|
||||||
is_invalid = true; \
|
is_invalid = true; \
|
||||||
east_set_error(&ast, pstate, msg, node->start); \
|
east_set_error(pstate, &ast.err, msg, node->start); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
/// Set error from the given kExprLexInvalid token
|
/// Set error from the given kExprLexInvalid token
|
||||||
@ -1231,7 +1263,6 @@ ExprAST viml_pexpr_parse(ParserState *const pstate, const int flags)
|
|||||||
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
|
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
|
||||||
{
|
{
|
||||||
ExprAST ast = {
|
ExprAST ast = {
|
||||||
.correct = true,
|
|
||||||
.err = {
|
.err = {
|
||||||
.msg = NULL,
|
.msg = NULL,
|
||||||
.arg_len = 0,
|
.arg_len = 0,
|
||||||
@ -1359,12 +1390,38 @@ viml_pexpr_parse_process_token:
|
|||||||
HL_CUR_TOKEN(UnaryPlus);
|
HL_CUR_TOKEN(UnaryPlus);
|
||||||
} else {
|
} else {
|
||||||
NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeBinaryPlus);
|
NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeBinaryPlus);
|
||||||
viml_pexpr_handle_bop(&ast_stack, cur_node, &want_node);
|
ADD_OP_NODE(cur_node);
|
||||||
HL_CUR_TOKEN(BinaryPlus);
|
HL_CUR_TOKEN(BinaryPlus);
|
||||||
}
|
}
|
||||||
want_node = kENodeValue;
|
want_node = kENodeValue;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case kExprLexComparison: {
|
||||||
|
ADD_VALUE_IF_MISSING(
|
||||||
|
_("E15: Expected value, got comparison operator: %.*s"));
|
||||||
|
NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeComparison);
|
||||||
|
if (cur_token.type == kExprLexInvalid) {
|
||||||
|
cur_node->data.cmp.ccs = kCCStrategyUseOption;
|
||||||
|
cur_node->data.cmp.type = kExprCmpEqual;
|
||||||
|
cur_node->data.cmp.inv = false;
|
||||||
|
} else {
|
||||||
|
cur_node->data.cmp.ccs = cur_token.data.cmp.ccs;
|
||||||
|
cur_node->data.cmp.type = cur_token.data.cmp.type;
|
||||||
|
cur_node->data.cmp.inv = cur_token.data.cmp.inv;
|
||||||
|
}
|
||||||
|
ADD_OP_NODE(cur_node);
|
||||||
|
if (cur_token.data.cmp.ccs != kCCStrategyUseOption) {
|
||||||
|
viml_parser_highlight(pstate, cur_token.start, cur_token.len - 1,
|
||||||
|
HL(ComparisonOperator));
|
||||||
|
viml_parser_highlight(
|
||||||
|
pstate, shifted_pos(cur_token.start, cur_token.len - 1), 1,
|
||||||
|
HL(ComparisonOperatorModifier));
|
||||||
|
} else {
|
||||||
|
HL_CUR_TOKEN(ComparisonOperator);
|
||||||
|
}
|
||||||
|
want_node = kENodeValue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
case kExprLexComma: {
|
case kExprLexComma: {
|
||||||
assert(want_node != kENodeArgument);
|
assert(want_node != kENodeArgument);
|
||||||
if (want_node == kENodeValue) {
|
if (want_node == kENodeValue) {
|
||||||
@ -1415,7 +1472,7 @@ viml_pexpr_parse_invalid_comma:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeComma);
|
NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeComma);
|
||||||
viml_pexpr_handle_bop(&ast_stack, cur_node, &want_node);
|
ADD_OP_NODE(cur_node);
|
||||||
HL_CUR_TOKEN(Comma);
|
HL_CUR_TOKEN(Comma);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1474,7 +1531,7 @@ viml_pexpr_parse_invalid_colon:
|
|||||||
HL_CUR_TOKEN(TernaryColon);
|
HL_CUR_TOKEN(TernaryColon);
|
||||||
} else {
|
} else {
|
||||||
NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeColon);
|
NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeColon);
|
||||||
viml_pexpr_handle_bop(&ast_stack, cur_node, &want_node);
|
ADD_OP_NODE(cur_node);
|
||||||
HL_CUR_TOKEN(Colon);
|
HL_CUR_TOKEN(Colon);
|
||||||
}
|
}
|
||||||
want_node = kENodeValue;
|
want_node = kENodeValue;
|
||||||
@ -1646,7 +1703,7 @@ viml_pexpr_parse_figure_brace_closing_error:
|
|||||||
ERROR_FROM_TOKEN_AND_MSG(
|
ERROR_FROM_TOKEN_AND_MSG(
|
||||||
cur_token, _("E15: Arrow outside of lambda: %.*s"));
|
cur_token, _("E15: Arrow outside of lambda: %.*s"));
|
||||||
NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeArrow);
|
NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeArrow);
|
||||||
viml_pexpr_handle_bop(&ast_stack, cur_node, &want_node);
|
ADD_OP_NODE(cur_node);
|
||||||
}
|
}
|
||||||
want_node = kENodeValue;
|
want_node = kENodeValue;
|
||||||
HL_CUR_TOKEN(Arrow);
|
HL_CUR_TOKEN(Arrow);
|
||||||
@ -1775,7 +1832,7 @@ viml_pexpr_parse_no_paren_closing_error: {}
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeCall);
|
NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeCall);
|
||||||
viml_pexpr_handle_bop(&ast_stack, cur_node, &want_node);
|
ADD_OP_NODE(cur_node);
|
||||||
HL_CUR_TOKEN(CallingParenthesis);
|
HL_CUR_TOKEN(CallingParenthesis);
|
||||||
} else {
|
} else {
|
||||||
// Currently it is impossible to reach this.
|
// Currently it is impossible to reach this.
|
||||||
@ -1788,7 +1845,7 @@ viml_pexpr_parse_no_paren_closing_error: {}
|
|||||||
case kExprLexQuestion: {
|
case kExprLexQuestion: {
|
||||||
ADD_VALUE_IF_MISSING(_("E15: Expected value, got question mark: %.*s"));
|
ADD_VALUE_IF_MISSING(_("E15: Expected value, got question mark: %.*s"));
|
||||||
NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeTernary);
|
NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeTernary);
|
||||||
viml_pexpr_handle_bop(&ast_stack, cur_node, &want_node);
|
ADD_OP_NODE(cur_node);
|
||||||
HL_CUR_TOKEN(Ternary);
|
HL_CUR_TOKEN(Ternary);
|
||||||
ExprASTNode *ter_val_node;
|
ExprASTNode *ter_val_node;
|
||||||
NEW_NODE_WITH_CUR_POS(ter_val_node, kExprNodeTernaryValue);
|
NEW_NODE_WITH_CUR_POS(ter_val_node, kExprNodeTernaryValue);
|
||||||
@ -1808,7 +1865,7 @@ viml_pexpr_parse_cycle_end:
|
|||||||
} while (true);
|
} while (true);
|
||||||
viml_pexpr_parse_end:
|
viml_pexpr_parse_end:
|
||||||
if (want_node == kENodeValue) {
|
if (want_node == kENodeValue) {
|
||||||
east_set_error(&ast, pstate, _("E15: Expected value, got EOC: %.*s"),
|
east_set_error(pstate, &ast.err, _("E15: Expected value, got EOC: %.*s"),
|
||||||
pstate->pos);
|
pstate->pos);
|
||||||
} else if (kv_size(ast_stack) != 1) {
|
} else if (kv_size(ast_stack) != 1) {
|
||||||
// Something may be wrong, check whether it really is.
|
// Something may be wrong, check whether it really is.
|
||||||
@ -1819,7 +1876,7 @@ viml_pexpr_parse_end:
|
|||||||
// Topmost stack item must be a *finished* value, so it must not be
|
// Topmost stack item must be a *finished* value, so it must not be
|
||||||
// analyzed. E.g. it may contain an already finished nested expression.
|
// analyzed. E.g. it may contain an already finished nested expression.
|
||||||
kv_drop(ast_stack, 1);
|
kv_drop(ast_stack, 1);
|
||||||
while (ast.correct && kv_size(ast_stack)) {
|
while (ast.err.msg == NULL && kv_size(ast_stack)) {
|
||||||
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);
|
||||||
@ -1832,14 +1889,14 @@ viml_pexpr_parse_end:
|
|||||||
}
|
}
|
||||||
case kExprNodeCall: {
|
case kExprNodeCall: {
|
||||||
east_set_error(
|
east_set_error(
|
||||||
&ast, pstate,
|
pstate, &ast.err,
|
||||||
_("E116: Missing closing parenthesis for function call: %.*s"),
|
_("E116: Missing closing parenthesis for function call: %.*s"),
|
||||||
cur_node->start);
|
cur_node->start);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case kExprNodeNested: {
|
case kExprNodeNested: {
|
||||||
east_set_error(
|
east_set_error(
|
||||||
&ast, pstate,
|
pstate, &ast.err,
|
||||||
_("E110: Missing closing parenthesis for nested expression"
|
_("E110: Missing closing parenthesis for nested expression"
|
||||||
": %.*s"),
|
": %.*s"),
|
||||||
cur_node->start);
|
cur_node->start);
|
||||||
@ -1855,7 +1912,7 @@ viml_pexpr_parse_end:
|
|||||||
if (!cur_node->data.ter.got_colon) {
|
if (!cur_node->data.ter.got_colon) {
|
||||||
// Actually Vim throws E109 in more cases.
|
// Actually Vim throws E109 in more cases.
|
||||||
east_set_error(
|
east_set_error(
|
||||||
&ast, pstate, _("E109: Missing ':' after '?': %.*s"),
|
pstate, &ast.err, _("E109: Missing ':' after '?': %.*s"),
|
||||||
cur_node->start);
|
cur_node->start);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -16,7 +16,7 @@ typedef enum {
|
|||||||
kCCStrategyUseOption = 0, // 0 for xcalloc
|
kCCStrategyUseOption = 0, // 0 for xcalloc
|
||||||
kCCStrategyMatchCase = '#',
|
kCCStrategyMatchCase = '#',
|
||||||
kCCStrategyIgnoreCase = '?',
|
kCCStrategyIgnoreCase = '?',
|
||||||
} CaseCompareStrategy;
|
} ExprCaseCompareStrategy;
|
||||||
|
|
||||||
/// Lexer token type
|
/// Lexer token type
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@ -52,6 +52,14 @@ typedef enum {
|
|||||||
kExprLexArrow, ///< Arrow, like from lambda expressions.
|
kExprLexArrow, ///< Arrow, like from lambda expressions.
|
||||||
} LexExprTokenType;
|
} LexExprTokenType;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
kExprCmpEqual, ///< Equality, unequality.
|
||||||
|
kExprCmpMatches, ///< Matches regex, not matches regex.
|
||||||
|
kExprCmpGreater, ///< `>` or `<=`
|
||||||
|
kExprCmpGreaterOrEqual, ///< `>=` or `<`.
|
||||||
|
kExprCmpIdentical, ///< `is` or `isnot`
|
||||||
|
} ExprComparisonType;
|
||||||
|
|
||||||
/// Lexer token
|
/// Lexer token
|
||||||
typedef struct {
|
typedef struct {
|
||||||
ParserPosition start;
|
ParserPosition start;
|
||||||
@ -59,14 +67,8 @@ typedef struct {
|
|||||||
LexExprTokenType type;
|
LexExprTokenType type;
|
||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
enum {
|
ExprComparisonType type; ///< Comparison type.
|
||||||
kExprLexCmpEqual, ///< Equality, unequality.
|
ExprCaseCompareStrategy ccs; ///< Case comparison strategy.
|
||||||
kExprLexCmpMatches, ///< Matches regex, not matches regex.
|
|
||||||
kExprLexCmpGreater, ///< `>` or `<=`
|
|
||||||
kExprLexCmpGreaterOrEqual, ///< `>=` or `<`.
|
|
||||||
kExprLexCmpIdentical, ///< `is` or `isnot`
|
|
||||||
} type; ///< Comparison type.
|
|
||||||
CaseCompareStrategy ccs; ///< Case comparison strategy.
|
|
||||||
bool inv; ///< True if comparison is to be inverted.
|
bool inv; ///< True if comparison is to be inverted.
|
||||||
} cmp; ///< For kExprLexComparison.
|
} cmp; ///< For kExprLexComparison.
|
||||||
|
|
||||||
@ -171,6 +173,7 @@ typedef enum {
|
|||||||
kExprNodeComma = ',', ///< Comma “operator”.
|
kExprNodeComma = ',', ///< Comma “operator”.
|
||||||
kExprNodeColon = ':', ///< Colon “operator”.
|
kExprNodeColon = ':', ///< Colon “operator”.
|
||||||
kExprNodeArrow = '>', ///< Arrow “operator”.
|
kExprNodeArrow = '>', ///< Arrow “operator”.
|
||||||
|
kExprNodeComparison = '=', ///< Various comparison operators.
|
||||||
} ExprASTNodeType;
|
} ExprASTNodeType;
|
||||||
|
|
||||||
typedef struct expr_ast_node ExprASTNode;
|
typedef struct expr_ast_node ExprASTNode;
|
||||||
@ -214,6 +217,11 @@ struct expr_ast_node {
|
|||||||
struct {
|
struct {
|
||||||
bool got_colon; ///< True if colon was seen.
|
bool got_colon; ///< True if colon was seen.
|
||||||
} ter; ///< For kExprNodeTernaryValue.
|
} ter; ///< For kExprNodeTernaryValue.
|
||||||
|
struct {
|
||||||
|
ExprComparisonType type; ///< Comparison type.
|
||||||
|
ExprCaseCompareStrategy ccs; ///< Case comparison strategy.
|
||||||
|
bool inv; ///< True if comparison is to be inverted.
|
||||||
|
} cmp; ///< For kExprNodeComparison.
|
||||||
} data;
|
} data;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -235,19 +243,22 @@ enum {
|
|||||||
// viml_expressions_parser.c.
|
// viml_expressions_parser.c.
|
||||||
} ExprParserFlags;
|
} ExprParserFlags;
|
||||||
|
|
||||||
|
/// AST error definition
|
||||||
|
typedef struct {
|
||||||
|
/// Error message. Must contain a single printf format atom: %.*s.
|
||||||
|
const char *msg;
|
||||||
|
/// Error message argument: points to the location of the error.
|
||||||
|
const char *arg;
|
||||||
|
/// Message argument length: length till the end of string.
|
||||||
|
int arg_len;
|
||||||
|
} ExprASTError;
|
||||||
|
|
||||||
/// Structure representing complety AST for one expression
|
/// Structure representing complety AST for one expression
|
||||||
typedef struct {
|
typedef struct {
|
||||||
/// True if represented AST is correct and can be executed. Incorrect ones may
|
|
||||||
/// still be used for completion, or in linters.
|
|
||||||
bool correct;
|
|
||||||
/// When AST is not correct this message will be printed.
|
/// When AST is not correct this message will be printed.
|
||||||
///
|
///
|
||||||
/// Uses `emsgf(msg, arg_len, arg);`, `msg` is assumed to contain only `%.*s`.
|
/// Uses `emsgf(msg, arg_len, arg);`, `msg` is assumed to contain only `%.*s`.
|
||||||
struct {
|
ExprASTError err;
|
||||||
const char *msg;
|
|
||||||
int arg_len;
|
|
||||||
const char *arg;
|
|
||||||
} err;
|
|
||||||
/// Root node of the AST.
|
/// Root node of the AST.
|
||||||
ExprASTNode *root;
|
ExprASTNode *root;
|
||||||
} ExprAST;
|
} ExprAST;
|
||||||
|
@ -91,12 +91,6 @@ int main(const int argc, const char *const *const argv,
|
|||||||
const ExprAST ast = viml_pexpr_parse(&pstate, flags);
|
const ExprAST ast = viml_pexpr_parse(&pstate, flags);
|
||||||
assert(ast.root != NULL
|
assert(ast.root != NULL
|
||||||
|| plines[0].size == 0);
|
|| plines[0].size == 0);
|
||||||
assert(ast.root != NULL || !ast.correct);
|
assert(ast.root != NULL || ast.err.msg);
|
||||||
assert(ast.correct
|
|
||||||
|| (ast.err.msg != NULL
|
|
||||||
&& ast.err.arg != NULL
|
|
||||||
&& ast.err.arg >= plines[0].data
|
|
||||||
&& ((size_t)(ast.err.arg - plines[0].data) + ast.err.arg_len
|
|
||||||
<= plines[0].size)));
|
|
||||||
// FIXME: free memory and assert no memory leaks
|
// FIXME: free memory and assert no memory leaks
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
local helpers = require('test.unit.helpers')(after_each)
|
local helpers = require('test.unit.helpers')(after_each)
|
||||||
local viml_helpers = require('test.unit.viml.helpers')
|
|
||||||
local global_helpers = require('test.helpers')
|
local global_helpers = require('test.helpers')
|
||||||
local itp = helpers.gen_itp(it)
|
local itp = helpers.gen_itp(it)
|
||||||
|
local viml_helpers = require('test.unit.viml.helpers')
|
||||||
|
|
||||||
local child_call_once = helpers.child_call_once
|
local child_call_once = helpers.child_call_once
|
||||||
local conv_enum = helpers.conv_enum
|
local conv_enum = helpers.conv_enum
|
||||||
@ -9,17 +9,18 @@ local cimport = helpers.cimport
|
|||||||
local ffi = helpers.ffi
|
local ffi = helpers.ffi
|
||||||
local eq = helpers.eq
|
local eq = helpers.eq
|
||||||
|
|
||||||
|
local conv_ccs = viml_helpers.conv_ccs
|
||||||
local pline2lua = viml_helpers.pline2lua
|
local pline2lua = viml_helpers.pline2lua
|
||||||
local new_pstate = viml_helpers.new_pstate
|
local new_pstate = viml_helpers.new_pstate
|
||||||
local intchar2lua = viml_helpers.intchar2lua
|
local intchar2lua = viml_helpers.intchar2lua
|
||||||
|
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 shallowcopy = global_helpers.shallowcopy
|
local shallowcopy = global_helpers.shallowcopy
|
||||||
|
|
||||||
local lib = cimport('./src/nvim/viml/parser/expressions.h')
|
local lib = cimport('./src/nvim/viml/parser/expressions.h')
|
||||||
|
|
||||||
local eltkn_type_tab, eltkn_cmp_type_tab, ccs_tab, eltkn_mul_type_tab
|
local eltkn_type_tab, eltkn_mul_type_tab, eltkn_opt_scope_tab
|
||||||
local eltkn_opt_scope_tab
|
|
||||||
child_call_once(function()
|
child_call_once(function()
|
||||||
eltkn_type_tab = {
|
eltkn_type_tab = {
|
||||||
[tonumber(lib.kExprLexInvalid)] = 'Invalid',
|
[tonumber(lib.kExprLexInvalid)] = 'Invalid',
|
||||||
@ -54,20 +55,6 @@ child_call_once(function()
|
|||||||
[tonumber(lib.kExprLexArrow)] = 'Arrow',
|
[tonumber(lib.kExprLexArrow)] = 'Arrow',
|
||||||
}
|
}
|
||||||
|
|
||||||
eltkn_cmp_type_tab = {
|
|
||||||
[tonumber(lib.kExprLexCmpEqual)] = 'Equal',
|
|
||||||
[tonumber(lib.kExprLexCmpMatches)] = 'Matches',
|
|
||||||
[tonumber(lib.kExprLexCmpGreater)] = 'Greater',
|
|
||||||
[tonumber(lib.kExprLexCmpGreaterOrEqual)] = 'GreaterOrEqual',
|
|
||||||
[tonumber(lib.kExprLexCmpIdentical)] = 'Identical',
|
|
||||||
}
|
|
||||||
|
|
||||||
ccs_tab = {
|
|
||||||
[tonumber(lib.kCCStrategyUseOption)] = 'UseOption',
|
|
||||||
[tonumber(lib.kCCStrategyMatchCase)] = 'MatchCase',
|
|
||||||
[tonumber(lib.kCCStrategyIgnoreCase)] = 'IgnoreCase',
|
|
||||||
}
|
|
||||||
|
|
||||||
eltkn_mul_type_tab = {
|
eltkn_mul_type_tab = {
|
||||||
[tonumber(lib.kExprLexMulMul)] = 'Mul',
|
[tonumber(lib.kExprLexMulMul)] = 'Mul',
|
||||||
[tonumber(lib.kExprLexMulDiv)] = 'Div',
|
[tonumber(lib.kExprLexMulDiv)] = 'Div',
|
||||||
@ -101,8 +88,8 @@ local function eltkn2lua(pstate, tkn)
|
|||||||
end
|
end
|
||||||
if ret.type == 'Comparison' then
|
if ret.type == 'Comparison' then
|
||||||
ret.data = {
|
ret.data = {
|
||||||
type = conv_enum(eltkn_cmp_type_tab, tkn.data.cmp.type),
|
type = conv_cmp_type(tkn.data.cmp.type),
|
||||||
ccs = conv_enum(ccs_tab, tkn.data.cmp.ccs),
|
ccs = conv_ccs(tkn.data.cmp.ccs),
|
||||||
inv = (not not tkn.data.cmp.inv),
|
inv = (not not tkn.data.cmp.inv),
|
||||||
}
|
}
|
||||||
elseif ret.type == 'Multiplication' then
|
elseif ret.type == 'Multiplication' then
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
local helpers = require('test.unit.helpers')(after_each)
|
local helpers = require('test.unit.helpers')(after_each)
|
||||||
local viml_helpers = require('test.unit.viml.helpers')
|
|
||||||
local global_helpers = require('test.helpers')
|
local global_helpers = require('test.helpers')
|
||||||
local itp = helpers.gen_itp(it)
|
local itp = helpers.gen_itp(it)
|
||||||
|
local viml_helpers = require('test.unit.viml.helpers')
|
||||||
|
|
||||||
local make_enum_conv_tab = helpers.make_enum_conv_tab
|
local make_enum_conv_tab = helpers.make_enum_conv_tab
|
||||||
local child_call_once = helpers.child_call_once
|
local child_call_once = helpers.child_call_once
|
||||||
@ -11,9 +11,11 @@ local cimport = helpers.cimport
|
|||||||
local ffi = helpers.ffi
|
local ffi = helpers.ffi
|
||||||
local eq = helpers.eq
|
local eq = helpers.eq
|
||||||
|
|
||||||
|
local conv_ccs = viml_helpers.conv_ccs
|
||||||
local pline2lua = viml_helpers.pline2lua
|
local pline2lua = viml_helpers.pline2lua
|
||||||
local new_pstate = viml_helpers.new_pstate
|
local new_pstate = viml_helpers.new_pstate
|
||||||
local intchar2lua = viml_helpers.intchar2lua
|
local intchar2lua = viml_helpers.intchar2lua
|
||||||
|
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 format_string = global_helpers.format_string
|
local format_string = global_helpers.format_string
|
||||||
@ -83,6 +85,7 @@ make_enum_conv_tab(lib, {
|
|||||||
'kExprNodeComma',
|
'kExprNodeComma',
|
||||||
'kExprNodeColon',
|
'kExprNodeColon',
|
||||||
'kExprNodeArrow',
|
'kExprNodeArrow',
|
||||||
|
'kExprNodeComparison',
|
||||||
}, '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)
|
||||||
@ -121,6 +124,10 @@ local function eastnode2lua(pstate, eastnode, checked_nodes)
|
|||||||
(eastnode.data.fig.type_guesses.allow_lambda and '\\' or '-')
|
(eastnode.data.fig.type_guesses.allow_lambda and '\\' or '-')
|
||||||
.. (eastnode.data.fig.type_guesses.allow_dict and 'd' or '-')
|
.. (eastnode.data.fig.type_guesses.allow_dict and 'd' or '-')
|
||||||
.. (eastnode.data.fig.type_guesses.allow_ident and 'i' or '-'))
|
.. (eastnode.data.fig.type_guesses.allow_ident and 'i' or '-'))
|
||||||
|
elseif typ == 'Comparison' then
|
||||||
|
typ = typ .. ('(type=%s,inv=%u,ccs=%s)'):format(
|
||||||
|
conv_cmp_type(eastnode.data.cmp.type), eastnode.data.cmp.inv and 1 or 0,
|
||||||
|
conv_ccs(eastnode.data.cmp.ccs))
|
||||||
end
|
end
|
||||||
ret_str = typ .. ':' .. ret_str
|
ret_str = typ .. ':' .. ret_str
|
||||||
local can_simplify = true
|
local can_simplify = true
|
||||||
@ -150,7 +157,7 @@ end
|
|||||||
local function east2lua(pstate, east)
|
local function east2lua(pstate, east)
|
||||||
local checked_nodes = {}
|
local checked_nodes = {}
|
||||||
return {
|
return {
|
||||||
err = (not east.correct) and {
|
err = east.err.msg ~= nil and {
|
||||||
msg = ffi.string(east.err.msg),
|
msg = ffi.string(east.err.msg),
|
||||||
arg = ('%s'):format(
|
arg = ('%s'):format(
|
||||||
ffi.string(east.err.arg, east.err.arg_len)),
|
ffi.string(east.err.arg, east.err.arg_len)),
|
||||||
@ -3328,4 +3335,318 @@ describe('Expressions parser', function()
|
|||||||
hl('Identifier', 'h'),
|
hl('Identifier', 'h'),
|
||||||
})
|
})
|
||||||
end)
|
end)
|
||||||
|
itp('works with comparison operators', function()
|
||||||
|
check_parsing('a == b', 0, {
|
||||||
|
-- 012345
|
||||||
|
ast = {
|
||||||
|
{
|
||||||
|
'Comparison(type=Equal,inv=0,ccs=UseOption):0:1: ==',
|
||||||
|
children = {
|
||||||
|
'PlainIdentifier(scope=0,ident=a):0:0:a',
|
||||||
|
'PlainIdentifier(scope=0,ident=b):0:4: b',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
hl('Identifier', 'a'),
|
||||||
|
hl('ComparisonOperator', '==', 1),
|
||||||
|
hl('Identifier', 'b', 1),
|
||||||
|
})
|
||||||
|
|
||||||
|
check_parsing('a ==? b', 0, {
|
||||||
|
-- 0123456
|
||||||
|
ast = {
|
||||||
|
{
|
||||||
|
'Comparison(type=Equal,inv=0,ccs=IgnoreCase):0:1: ==?',
|
||||||
|
children = {
|
||||||
|
'PlainIdentifier(scope=0,ident=a):0:0:a',
|
||||||
|
'PlainIdentifier(scope=0,ident=b):0:5: b',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
hl('Identifier', 'a'),
|
||||||
|
hl('ComparisonOperator', '==', 1),
|
||||||
|
hl('ComparisonOperatorModifier', '?'),
|
||||||
|
hl('Identifier', 'b', 1),
|
||||||
|
})
|
||||||
|
|
||||||
|
check_parsing('a ==# b', 0, {
|
||||||
|
-- 0123456
|
||||||
|
ast = {
|
||||||
|
{
|
||||||
|
'Comparison(type=Equal,inv=0,ccs=MatchCase):0:1: ==#',
|
||||||
|
children = {
|
||||||
|
'PlainIdentifier(scope=0,ident=a):0:0:a',
|
||||||
|
'PlainIdentifier(scope=0,ident=b):0:5: b',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
hl('Identifier', 'a'),
|
||||||
|
hl('ComparisonOperator', '==', 1),
|
||||||
|
hl('ComparisonOperatorModifier', '#'),
|
||||||
|
hl('Identifier', 'b', 1),
|
||||||
|
})
|
||||||
|
|
||||||
|
check_parsing('a !=# b', 0, {
|
||||||
|
-- 0123456
|
||||||
|
ast = {
|
||||||
|
{
|
||||||
|
'Comparison(type=Equal,inv=1,ccs=MatchCase):0:1: !=#',
|
||||||
|
children = {
|
||||||
|
'PlainIdentifier(scope=0,ident=a):0:0:a',
|
||||||
|
'PlainIdentifier(scope=0,ident=b):0:5: b',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
hl('Identifier', 'a'),
|
||||||
|
hl('ComparisonOperator', '!=', 1),
|
||||||
|
hl('ComparisonOperatorModifier', '#'),
|
||||||
|
hl('Identifier', 'b', 1),
|
||||||
|
})
|
||||||
|
|
||||||
|
check_parsing('a <=# b', 0, {
|
||||||
|
-- 0123456
|
||||||
|
ast = {
|
||||||
|
{
|
||||||
|
'Comparison(type=Greater,inv=1,ccs=MatchCase):0:1: <=#',
|
||||||
|
children = {
|
||||||
|
'PlainIdentifier(scope=0,ident=a):0:0:a',
|
||||||
|
'PlainIdentifier(scope=0,ident=b):0:5: b',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
hl('Identifier', 'a'),
|
||||||
|
hl('ComparisonOperator', '<=', 1),
|
||||||
|
hl('ComparisonOperatorModifier', '#'),
|
||||||
|
hl('Identifier', 'b', 1),
|
||||||
|
})
|
||||||
|
|
||||||
|
check_parsing('a >=# b', 0, {
|
||||||
|
-- 0123456
|
||||||
|
ast = {
|
||||||
|
{
|
||||||
|
'Comparison(type=GreaterOrEqual,inv=0,ccs=MatchCase):0:1: >=#',
|
||||||
|
children = {
|
||||||
|
'PlainIdentifier(scope=0,ident=a):0:0:a',
|
||||||
|
'PlainIdentifier(scope=0,ident=b):0:5: b',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
hl('Identifier', 'a'),
|
||||||
|
hl('ComparisonOperator', '>=', 1),
|
||||||
|
hl('ComparisonOperatorModifier', '#'),
|
||||||
|
hl('Identifier', 'b', 1),
|
||||||
|
})
|
||||||
|
|
||||||
|
check_parsing('a ># b', 0, {
|
||||||
|
-- 012345
|
||||||
|
ast = {
|
||||||
|
{
|
||||||
|
'Comparison(type=Greater,inv=0,ccs=MatchCase):0:1: >#',
|
||||||
|
children = {
|
||||||
|
'PlainIdentifier(scope=0,ident=a):0:0:a',
|
||||||
|
'PlainIdentifier(scope=0,ident=b):0:4: b',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
hl('Identifier', 'a'),
|
||||||
|
hl('ComparisonOperator', '>', 1),
|
||||||
|
hl('ComparisonOperatorModifier', '#'),
|
||||||
|
hl('Identifier', 'b', 1),
|
||||||
|
})
|
||||||
|
|
||||||
|
check_parsing('a <# b', 0, {
|
||||||
|
-- 012345
|
||||||
|
ast = {
|
||||||
|
{
|
||||||
|
'Comparison(type=GreaterOrEqual,inv=1,ccs=MatchCase):0:1: <#',
|
||||||
|
children = {
|
||||||
|
'PlainIdentifier(scope=0,ident=a):0:0:a',
|
||||||
|
'PlainIdentifier(scope=0,ident=b):0:4: b',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
hl('Identifier', 'a'),
|
||||||
|
hl('ComparisonOperator', '<', 1),
|
||||||
|
hl('ComparisonOperatorModifier', '#'),
|
||||||
|
hl('Identifier', 'b', 1),
|
||||||
|
})
|
||||||
|
|
||||||
|
check_parsing('a is#b', 0, {
|
||||||
|
-- 012345
|
||||||
|
ast = {
|
||||||
|
{
|
||||||
|
'Comparison(type=Identical,inv=0,ccs=MatchCase):0:1: is#',
|
||||||
|
children = {
|
||||||
|
'PlainIdentifier(scope=0,ident=a):0:0:a',
|
||||||
|
'PlainIdentifier(scope=0,ident=b):0:5:b',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
hl('Identifier', 'a'),
|
||||||
|
hl('ComparisonOperator', 'is', 1),
|
||||||
|
hl('ComparisonOperatorModifier', '#'),
|
||||||
|
hl('Identifier', 'b'),
|
||||||
|
})
|
||||||
|
|
||||||
|
check_parsing('a is?b', 0, {
|
||||||
|
-- 012345
|
||||||
|
ast = {
|
||||||
|
{
|
||||||
|
'Comparison(type=Identical,inv=0,ccs=IgnoreCase):0:1: is?',
|
||||||
|
children = {
|
||||||
|
'PlainIdentifier(scope=0,ident=a):0:0:a',
|
||||||
|
'PlainIdentifier(scope=0,ident=b):0:5:b',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
hl('Identifier', 'a'),
|
||||||
|
hl('ComparisonOperator', 'is', 1),
|
||||||
|
hl('ComparisonOperatorModifier', '?'),
|
||||||
|
hl('Identifier', 'b'),
|
||||||
|
})
|
||||||
|
|
||||||
|
check_parsing('a isnot b', 0, {
|
||||||
|
-- 012345678
|
||||||
|
ast = {
|
||||||
|
{
|
||||||
|
'Comparison(type=Identical,inv=1,ccs=UseOption):0:1: isnot',
|
||||||
|
children = {
|
||||||
|
'PlainIdentifier(scope=0,ident=a):0:0:a',
|
||||||
|
'PlainIdentifier(scope=0,ident=b):0:7: b',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
hl('Identifier', 'a'),
|
||||||
|
hl('ComparisonOperator', 'isnot', 1),
|
||||||
|
hl('Identifier', 'b', 1),
|
||||||
|
})
|
||||||
|
|
||||||
|
check_parsing('a < b < c', 0, {
|
||||||
|
-- 012345678
|
||||||
|
ast = {
|
||||||
|
{
|
||||||
|
'Comparison(type=GreaterOrEqual,inv=1,ccs=UseOption):0:1: <',
|
||||||
|
children = {
|
||||||
|
'PlainIdentifier(scope=0,ident=a):0:0:a',
|
||||||
|
{
|
||||||
|
'Comparison(type=GreaterOrEqual,inv=1,ccs=UseOption):0:5: <',
|
||||||
|
children = {
|
||||||
|
'PlainIdentifier(scope=0,ident=b):0:3: b',
|
||||||
|
'PlainIdentifier(scope=0,ident=c):0:7: c',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
err = {
|
||||||
|
arg = ' < c',
|
||||||
|
msg = 'E15: Operator is not associative: %.*s',
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
hl('Identifier', 'a'),
|
||||||
|
hl('ComparisonOperator', '<', 1),
|
||||||
|
hl('Identifier', 'b', 1),
|
||||||
|
hl('InvalidComparisonOperator', '<', 1),
|
||||||
|
hl('Identifier', 'c', 1),
|
||||||
|
})
|
||||||
|
check_parsing('a += b', 0, {
|
||||||
|
-- 012345
|
||||||
|
ast = {
|
||||||
|
{
|
||||||
|
'Comparison(type=Equal,inv=0,ccs=UseOption):0:3:=',
|
||||||
|
children = {
|
||||||
|
{
|
||||||
|
'BinaryPlus:0:1: +',
|
||||||
|
children = {
|
||||||
|
'PlainIdentifier(scope=0,ident=a):0:0:a',
|
||||||
|
'Missing:0:3:',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'PlainIdentifier(scope=0,ident=b):0:4: b',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
err = {
|
||||||
|
arg = '= b',
|
||||||
|
msg = 'E15: Expected == or =~: %.*s',
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
hl('Identifier', 'a'),
|
||||||
|
hl('BinaryPlus', '+', 1),
|
||||||
|
hl('InvalidComparisonOperator', '='),
|
||||||
|
hl('Identifier', 'b', 1),
|
||||||
|
})
|
||||||
|
check_parsing('a + b == c + d', 0, {
|
||||||
|
-- 01234567890123
|
||||||
|
-- 0 1
|
||||||
|
ast = {
|
||||||
|
{
|
||||||
|
'Comparison(type=Equal,inv=0,ccs=UseOption):0:5: ==',
|
||||||
|
children = {
|
||||||
|
{
|
||||||
|
'BinaryPlus:0:1: +',
|
||||||
|
children = {
|
||||||
|
'PlainIdentifier(scope=0,ident=a):0:0:a',
|
||||||
|
'PlainIdentifier(scope=0,ident=b):0:3: b',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'BinaryPlus:0:10: +',
|
||||||
|
children = {
|
||||||
|
'PlainIdentifier(scope=0,ident=c):0:8: c',
|
||||||
|
'PlainIdentifier(scope=0,ident=d):0:12: d',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
hl('Identifier', 'a'),
|
||||||
|
hl('BinaryPlus', '+', 1),
|
||||||
|
hl('Identifier', 'b', 1),
|
||||||
|
hl('ComparisonOperator', '==', 1),
|
||||||
|
hl('Identifier', 'c', 1),
|
||||||
|
hl('BinaryPlus', '+', 1),
|
||||||
|
hl('Identifier', 'd', 1),
|
||||||
|
})
|
||||||
|
check_parsing('+ a == + b', 0, {
|
||||||
|
-- 0123456789
|
||||||
|
ast = {
|
||||||
|
{
|
||||||
|
'Comparison(type=Equal,inv=0,ccs=UseOption):0:3: ==',
|
||||||
|
children = {
|
||||||
|
{
|
||||||
|
'UnaryPlus:0:0:+',
|
||||||
|
children = {
|
||||||
|
'PlainIdentifier(scope=0,ident=a):0:1: a',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'UnaryPlus:0:6: +',
|
||||||
|
children = {
|
||||||
|
'PlainIdentifier(scope=0,ident=b):0:8: b',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
hl('UnaryPlus', '+'),
|
||||||
|
hl('Identifier', 'a', 1),
|
||||||
|
hl('ComparisonOperator', '==', 1),
|
||||||
|
hl('UnaryPlus', '+', 1),
|
||||||
|
hl('Identifier', 'b', 1),
|
||||||
|
})
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
@ -1,8 +1,13 @@
|
|||||||
local helpers = require('test.unit.helpers')(nil)
|
local helpers = require('test.unit.helpers')(nil)
|
||||||
|
|
||||||
local ffi = helpers.ffi
|
local ffi = helpers.ffi
|
||||||
|
local cimport = helpers.cimport
|
||||||
local kvi_new = helpers.kvi_new
|
local kvi_new = helpers.kvi_new
|
||||||
local kvi_init = helpers.kvi_init
|
local kvi_init = helpers.kvi_init
|
||||||
|
local conv_enum = helpers.conv_enum
|
||||||
|
local make_enum_conv_tab = helpers.make_enum_conv_tab
|
||||||
|
|
||||||
|
local lib = cimport('./src/nvim/viml/parser/expressions.h')
|
||||||
|
|
||||||
local function new_pstate(strings)
|
local function new_pstate(strings)
|
||||||
local strings_idx = 0
|
local strings_idx = 0
|
||||||
@ -88,10 +93,36 @@ local function pstate_set_str(pstate, start, len, ret)
|
|||||||
return ret
|
return ret
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local eltkn_cmp_type_tab
|
||||||
|
make_enum_conv_tab(lib, {
|
||||||
|
'kExprCmpEqual',
|
||||||
|
'kExprCmpMatches',
|
||||||
|
'kExprCmpGreater',
|
||||||
|
'kExprCmpGreaterOrEqual',
|
||||||
|
'kExprCmpIdentical',
|
||||||
|
}, 'kExprCmp', function(ret) eltkn_cmp_type_tab = ret end)
|
||||||
|
|
||||||
|
local function conv_cmp_type(typ)
|
||||||
|
return conv_enum(eltkn_cmp_type_tab, typ)
|
||||||
|
end
|
||||||
|
|
||||||
|
local ccs_tab
|
||||||
|
make_enum_conv_tab(lib, {
|
||||||
|
'kCCStrategyUseOption',
|
||||||
|
'kCCStrategyMatchCase',
|
||||||
|
'kCCStrategyIgnoreCase',
|
||||||
|
}, 'kCCStrategy', function(ret) ccs_tab = ret end)
|
||||||
|
|
||||||
|
local function conv_ccs(ccs)
|
||||||
|
return conv_enum(ccs_tab, ccs)
|
||||||
|
end
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
conv_ccs = conv_ccs,
|
||||||
pline2lua = pline2lua,
|
pline2lua = pline2lua,
|
||||||
pstate_str = pstate_str,
|
pstate_str = pstate_str,
|
||||||
new_pstate = new_pstate,
|
new_pstate = new_pstate,
|
||||||
intchar2lua = intchar2lua,
|
intchar2lua = intchar2lua,
|
||||||
|
conv_cmp_type = conv_cmp_type,
|
||||||
pstate_set_str = pstate_set_str,
|
pstate_set_str = pstate_set_str,
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user