mirror of
https://github.com/neovim/neovim.git
synced 2025-09-17 00:38:17 +00:00
viml/parser/expressions: Add support for figure braces (three kinds)
This commit is contained in:
@@ -20,10 +20,22 @@
|
|||||||
|
|
||||||
typedef kvec_withinit_t(ExprASTNode **, 16) ExprASTStack;
|
typedef kvec_withinit_t(ExprASTNode **, 16) ExprASTStack;
|
||||||
|
|
||||||
|
/// Which nodes may be wanted
|
||||||
typedef enum {
|
typedef enum {
|
||||||
kELvlOperator, ///< Operators: function call, subscripts, binary operators, …
|
/// Operators: function call, subscripts, binary operators, …
|
||||||
kELvlValue, ///< Actual value: literals, variables, nested expressions.
|
///
|
||||||
} ExprASTLevel;
|
/// For unrestricted expressions.
|
||||||
|
kENodeOperator,
|
||||||
|
/// Values: literals, variables, nested expressions, unary operators.
|
||||||
|
///
|
||||||
|
/// For unrestricted expressions as well, implies that top item in AST stack
|
||||||
|
/// points to NULL.
|
||||||
|
kENodeValue,
|
||||||
|
/// Argument: only allows simple argument names.
|
||||||
|
kENodeArgument,
|
||||||
|
/// Argument separator: only allows commas.
|
||||||
|
kENodeArgumentSeparator,
|
||||||
|
} ExprASTWantedNode;
|
||||||
|
|
||||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||||
# include "viml/parser/expressions.c.generated.h"
|
# include "viml/parser/expressions.c.generated.h"
|
||||||
@@ -456,6 +468,8 @@ viml_pexpr_next_token_adv_return:
|
|||||||
//
|
//
|
||||||
// Used highlighting groups and assumed linkage:
|
// Used highlighting groups and assumed linkage:
|
||||||
//
|
//
|
||||||
|
// NVimInternalError -> highlight as fg:red/bg:red
|
||||||
|
//
|
||||||
// NVimInvalid -> Error
|
// NVimInvalid -> Error
|
||||||
// NVimInvalidValue -> NVimInvalid
|
// NVimInvalidValue -> NVimInvalid
|
||||||
// NVimInvalidOperator -> NVimInvalid
|
// NVimInvalidOperator -> NVimInvalid
|
||||||
@@ -469,11 +483,33 @@ viml_pexpr_next_token_adv_return:
|
|||||||
//
|
//
|
||||||
// NVimParenthesis -> Delimiter
|
// NVimParenthesis -> Delimiter
|
||||||
//
|
//
|
||||||
|
// NVimComma -> Delimiter
|
||||||
|
// NVimArrow -> Delimiter
|
||||||
|
//
|
||||||
|
// NVimLambda -> Delimiter
|
||||||
|
// NVimDict -> Delimiter
|
||||||
|
// NVimCurly -> Delimiter
|
||||||
|
//
|
||||||
|
// NVimIdentifier -> Identifier
|
||||||
|
// NVimIdentifierScope -> NVimIdentifier
|
||||||
|
// NVimIdentifierScopeDelimiter -> NVimIdentifier
|
||||||
|
//
|
||||||
|
// NVimFigureBrace -> NVimInternalError
|
||||||
|
//
|
||||||
|
// NVimInvalidComma -> NVimInvalidDelimiter
|
||||||
// NVimInvalidSpacing -> NVimInvalid
|
// NVimInvalidSpacing -> NVimInvalid
|
||||||
// NVimInvalidTernaryOperator -> NVimInvalidOperator
|
// NVimInvalidTernaryOperator -> NVimInvalidOperator
|
||||||
// NVimInvalidRegister -> NVimInvalidValue
|
// NVimInvalidRegister -> NVimInvalidValue
|
||||||
// NVimInvalidClosingBracket -> NVimInvalidDelimiter
|
// NVimInvalidClosingBracket -> NVimInvalidDelimiter
|
||||||
// NVimInvalidSpacing -> NVimInvalid
|
// NVimInvalidSpacing -> NVimInvalid
|
||||||
|
// NVimInvalidArrow -> NVimInvalidDelimiter
|
||||||
|
// NVimInvalidLambda -> NVimInvalidDelimiter
|
||||||
|
// NVimInvalidDict -> NVimInvalidDelimiter
|
||||||
|
// NVimInvalidCurly -> NVimInvalidDelimiter
|
||||||
|
// NVimInvalidFigureBrace -> NVimInvalidDelimiter
|
||||||
|
// NVimInvalidIdentifier -> NVimInvalidValue
|
||||||
|
// NVimInvalidIdentifierScope -> NVimInvalidValue
|
||||||
|
// NVimInvalidIdentifierScopeDelimiter -> NVimInvalidValue
|
||||||
//
|
//
|
||||||
// NVimUnaryPlus -> NVimUnaryOperator
|
// NVimUnaryPlus -> NVimUnaryOperator
|
||||||
// NVimBinaryPlus -> NVimBinaryOperator
|
// NVimBinaryPlus -> NVimBinaryOperator
|
||||||
@@ -498,6 +534,9 @@ static inline ExprASTNode *viml_pexpr_new_node(const ExprASTNodeType type)
|
|||||||
typedef enum {
|
typedef enum {
|
||||||
kEOpLvlInvalid = 0,
|
kEOpLvlInvalid = 0,
|
||||||
kEOpLvlParens,
|
kEOpLvlParens,
|
||||||
|
kEOpLvlArrow,
|
||||||
|
kEOpLvlComma,
|
||||||
|
kEOpLvlColon,
|
||||||
kEOpLvlTernary,
|
kEOpLvlTernary,
|
||||||
kEOpLvlOr,
|
kEOpLvlOr,
|
||||||
kEOpLvlAnd,
|
kEOpLvlAnd,
|
||||||
@@ -506,6 +545,7 @@ typedef enum {
|
|||||||
kEOpLvlMultiplication, ///< Multiplication, division and modulo.
|
kEOpLvlMultiplication, ///< Multiplication, division and modulo.
|
||||||
kEOpLvlUnary, ///< Unary operations: not, minus, plus.
|
kEOpLvlUnary, ///< Unary operations: not, minus, plus.
|
||||||
kEOpLvlSubscript, ///< Subscripts.
|
kEOpLvlSubscript, ///< Subscripts.
|
||||||
|
kEOpLvlComplexIdentifier, ///< Plain identifier, curly braces name.
|
||||||
kEOpLvlValue, ///< Values: literals, variables, nested expressions, …
|
kEOpLvlValue, ///< Values: literals, variables, nested expressions, …
|
||||||
} ExprOpLvl;
|
} ExprOpLvl;
|
||||||
|
|
||||||
@@ -520,7 +560,16 @@ static const ExprOpLvl node_type_to_op_lvl[] = {
|
|||||||
[kExprNodeOpMissing] = kEOpLvlMultiplication,
|
[kExprNodeOpMissing] = kEOpLvlMultiplication,
|
||||||
|
|
||||||
[kExprNodeNested] = kEOpLvlParens,
|
[kExprNodeNested] = kEOpLvlParens,
|
||||||
[kExprNodeComplexIdentifier] = kEOpLvlParens,
|
|
||||||
|
[kExprNodeUnknownFigure] = kEOpLvlParens,
|
||||||
|
[kExprNodeLambda] = kEOpLvlParens,
|
||||||
|
[kExprNodeDictLiteral] = kEOpLvlParens,
|
||||||
|
|
||||||
|
[kExprNodeArrow] = kEOpLvlArrow,
|
||||||
|
|
||||||
|
[kExprNodeComma] = kEOpLvlComma,
|
||||||
|
|
||||||
|
[kExprNodeColon] = kEOpLvlColon,
|
||||||
|
|
||||||
[kExprNodeTernary] = kEOpLvlTernary,
|
[kExprNodeTernary] = kEOpLvlTernary,
|
||||||
|
|
||||||
@@ -531,9 +580,12 @@ static const ExprOpLvl node_type_to_op_lvl[] = {
|
|||||||
[kExprNodeSubscript] = kEOpLvlSubscript,
|
[kExprNodeSubscript] = kEOpLvlSubscript,
|
||||||
[kExprNodeCall] = kEOpLvlSubscript,
|
[kExprNodeCall] = kEOpLvlSubscript,
|
||||||
|
|
||||||
|
[kExprNodeComplexIdentifier] = kEOpLvlComplexIdentifier,
|
||||||
|
[kExprNodePlainIdentifier] = kEOpLvlComplexIdentifier,
|
||||||
|
[kExprNodeCurlyBracesIdentifier] = kEOpLvlComplexIdentifier,
|
||||||
|
|
||||||
[kExprNodeRegister] = kEOpLvlValue,
|
[kExprNodeRegister] = kEOpLvlValue,
|
||||||
[kExprNodeListLiteral] = kEOpLvlValue,
|
[kExprNodeListLiteral] = kEOpLvlValue,
|
||||||
[kExprNodePlainIdentifier] = kEOpLvlValue,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const ExprOpAssociativity node_type_to_op_ass[] = {
|
static const ExprOpAssociativity node_type_to_op_ass[] = {
|
||||||
@@ -541,7 +593,24 @@ static const ExprOpAssociativity node_type_to_op_ass[] = {
|
|||||||
[kExprNodeOpMissing] = kEOpAssNo,
|
[kExprNodeOpMissing] = kEOpAssNo,
|
||||||
|
|
||||||
[kExprNodeNested] = kEOpAssNo,
|
[kExprNodeNested] = kEOpAssNo,
|
||||||
[kExprNodeComplexIdentifier] = kEOpAssLeft,
|
|
||||||
|
[kExprNodeUnknownFigure] = kEOpAssLeft,
|
||||||
|
[kExprNodeLambda] = kEOpAssNo,
|
||||||
|
[kExprNodeDictLiteral] = kEOpAssNo,
|
||||||
|
|
||||||
|
// Does not really matter.
|
||||||
|
[kExprNodeArrow] = kEOpAssNo,
|
||||||
|
|
||||||
|
[kExprNodeColon] = kEOpAssNo,
|
||||||
|
|
||||||
|
// Right associativity for comma because this means easier access to arguments
|
||||||
|
// list, etc: for "[a, b, c, d]" you can access "a" in one step if it is
|
||||||
|
// represented as "list(comma(a, comma(b, comma(c, d))))" then if it is
|
||||||
|
// "list(comma(comma(comma(a, b), c), d))" in which case you will need to
|
||||||
|
// traverse all three comma() structures. And with comma operator (including
|
||||||
|
// actual comma operator from C which is not present in VimL) nobody cares
|
||||||
|
// about associativity, only about order of execution.
|
||||||
|
[kExprNodeComma] = kEOpAssRight,
|
||||||
|
|
||||||
[kExprNodeTernary] = kEOpAssNo,
|
[kExprNodeTernary] = kEOpAssNo,
|
||||||
|
|
||||||
@@ -552,14 +621,31 @@ static const ExprOpAssociativity node_type_to_op_ass[] = {
|
|||||||
[kExprNodeSubscript] = kEOpAssLeft,
|
[kExprNodeSubscript] = kEOpAssLeft,
|
||||||
[kExprNodeCall] = kEOpAssLeft,
|
[kExprNodeCall] = kEOpAssLeft,
|
||||||
|
|
||||||
|
[kExprNodePlainIdentifier] = kEOpAssLeft,
|
||||||
|
[kExprNodeComplexIdentifier] = kEOpAssLeft,
|
||||||
|
[kExprNodeCurlyBracesIdentifier] = kEOpAssLeft,
|
||||||
|
|
||||||
[kExprNodeRegister] = kEOpAssNo,
|
[kExprNodeRegister] = kEOpAssNo,
|
||||||
[kExprNodeListLiteral] = kEOpAssNo,
|
[kExprNodeListLiteral] = kEOpAssNo,
|
||||||
[kExprNodePlainIdentifier] = kEOpAssNo,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef UNIT_TESTING
|
#ifdef UNIT_TESTING
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
REAL_FATTR_UNUSED
|
REAL_FATTR_UNUSED
|
||||||
|
static inline void viml_pexpr_debug_print_ast_node(
|
||||||
|
const ExprASTNode *const *const eastnode_p,
|
||||||
|
const char *const prefix)
|
||||||
|
{
|
||||||
|
if (*eastnode_p == NULL) {
|
||||||
|
fprintf(stderr, "%s %p : NULL\n", prefix, (void *)eastnode_p);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "%s %p : %p : %c : %zu:%zu:%zu\n",
|
||||||
|
prefix, (void *)eastnode_p, (void *)(*eastnode_p),
|
||||||
|
(*eastnode_p)->type, (*eastnode_p)->start.line,
|
||||||
|
(*eastnode_p)->start.col, (*eastnode_p)->len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
REAL_FATTR_UNUSED
|
||||||
static inline void viml_pexpr_debug_print_ast_stack(
|
static inline void viml_pexpr_debug_print_ast_stack(
|
||||||
const ExprASTStack *const ast_stack,
|
const ExprASTStack *const ast_stack,
|
||||||
const char *const msg)
|
const char *const msg)
|
||||||
@@ -567,22 +653,17 @@ static inline void viml_pexpr_debug_print_ast_stack(
|
|||||||
{
|
{
|
||||||
fprintf(stderr, "\n%sstack: %zu:\n", msg, kv_size(*ast_stack));
|
fprintf(stderr, "\n%sstack: %zu:\n", msg, kv_size(*ast_stack));
|
||||||
for (size_t i = 0; i < kv_size(*ast_stack); i++) {
|
for (size_t i = 0; i < kv_size(*ast_stack); i++) {
|
||||||
const ExprASTNode *const *const eastnode_p = (
|
viml_pexpr_debug_print_ast_node(
|
||||||
(const ExprASTNode *const *)kv_A(*ast_stack, i));
|
(const ExprASTNode *const *)kv_A(*ast_stack, i),
|
||||||
if (*eastnode_p == NULL) {
|
"-");
|
||||||
fprintf(stderr, "- %p : NULL\n", (void *)eastnode_p);
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "- %p : %p : %c : %zu:%zu:%zu\n",
|
|
||||||
(void *)eastnode_p, (void *)(*eastnode_p), (*eastnode_p)->type,
|
|
||||||
(*eastnode_p)->start.line, (*eastnode_p)->start.col,
|
|
||||||
(*eastnode_p)->len);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#define PSTACK(msg) \
|
#define PSTACK(msg) \
|
||||||
viml_pexpr_debug_print_ast_stack(&ast_stack, #msg)
|
viml_pexpr_debug_print_ast_stack(&ast_stack, #msg)
|
||||||
#define PSTACK_P(msg) \
|
#define PSTACK_P(msg) \
|
||||||
viml_pexpr_debug_print_ast_stack(ast_stack, #msg)
|
viml_pexpr_debug_print_ast_stack(ast_stack, #msg)
|
||||||
|
#define PNODE_P(eastnode_p, msg) \
|
||||||
|
viml_pexpr_debug_print_ast_node((const ExprASTNode *const *)ast_stack, #msg)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// Handle binary operator
|
/// Handle binary operator
|
||||||
@@ -590,7 +671,7 @@ static inline void viml_pexpr_debug_print_ast_stack(
|
|||||||
/// 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,
|
static void viml_pexpr_handle_bop(ExprASTStack *const ast_stack,
|
||||||
ExprASTNode *const bop_node,
|
ExprASTNode *const bop_node,
|
||||||
ExprASTLevel *const want_level_p)
|
ExprASTWantedNode *const want_node_p)
|
||||||
FUNC_ATTR_NONNULL_ALL
|
FUNC_ATTR_NONNULL_ALL
|
||||||
{
|
{
|
||||||
ExprASTNode **top_node_p = NULL;
|
ExprASTNode **top_node_p = NULL;
|
||||||
@@ -599,6 +680,9 @@ static void viml_pexpr_handle_bop(ExprASTStack *const ast_stack,
|
|||||||
ExprOpAssociativity top_node_ass;
|
ExprOpAssociativity top_node_ass;
|
||||||
assert(kv_size(*ast_stack));
|
assert(kv_size(*ast_stack));
|
||||||
const ExprOpLvl bop_node_lvl = node_type_to_op_lvl[bop_node->type];
|
const ExprOpLvl bop_node_lvl = node_type_to_op_lvl[bop_node->type];
|
||||||
|
#ifndef NDEBUG
|
||||||
|
const ExprOpAssociativity bop_node_ass = node_type_to_op_ass[bop_node->type];
|
||||||
|
#endif
|
||||||
do {
|
do {
|
||||||
ExprASTNode **new_top_node_p = kv_last(*ast_stack);
|
ExprASTNode **new_top_node_p = kv_last(*ast_stack);
|
||||||
ExprASTNode *new_top_node = *new_top_node_p;
|
ExprASTNode *new_top_node = *new_top_node_p;
|
||||||
@@ -606,6 +690,8 @@ static void viml_pexpr_handle_bop(ExprASTStack *const ast_stack,
|
|||||||
const ExprOpLvl new_top_node_lvl = node_type_to_op_lvl[new_top_node->type];
|
const ExprOpLvl new_top_node_lvl = node_type_to_op_lvl[new_top_node->type];
|
||||||
const ExprOpAssociativity new_top_node_ass = (
|
const ExprOpAssociativity new_top_node_ass = (
|
||||||
node_type_to_op_ass[new_top_node->type]);
|
node_type_to_op_ass[new_top_node->type]);
|
||||||
|
assert(bop_node_lvl != new_top_node_lvl
|
||||||
|
|| bop_node_ass == new_top_node_ass);
|
||||||
if (top_node_p != NULL
|
if (top_node_p != NULL
|
||||||
&& ((bop_node_lvl > new_top_node_lvl
|
&& ((bop_node_lvl > new_top_node_lvl
|
||||||
|| (bop_node_lvl == new_top_node_lvl
|
|| (bop_node_lvl == new_top_node_lvl
|
||||||
@@ -617,14 +703,60 @@ static void viml_pexpr_handle_bop(ExprASTStack *const ast_stack,
|
|||||||
top_node = new_top_node;
|
top_node = new_top_node;
|
||||||
top_node_lvl = new_top_node_lvl;
|
top_node_lvl = new_top_node_lvl;
|
||||||
top_node_ass = new_top_node_ass;
|
top_node_ass = new_top_node_ass;
|
||||||
|
if (bop_node_lvl == top_node_lvl && top_node_ass == kEOpAssRight) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
} while (kv_size(*ast_stack));
|
} while (kv_size(*ast_stack));
|
||||||
// FIXME Handle right and no associativity correctly
|
// FIXME: Handle no associativity
|
||||||
*top_node_p = bop_node;
|
if (top_node_ass == kEOpAssLeft || top_node_lvl != bop_node_lvl) {
|
||||||
bop_node->children = top_node;
|
// outer(op(x,y)) -> outer(new_op(op(x,y),*))
|
||||||
assert(bop_node->children->next == NULL);
|
//
|
||||||
kvi_push(*ast_stack, top_node_p);
|
// Before: top_node_p = outer(*), points to op(x,y)
|
||||||
kvi_push(*ast_stack, &bop_node->children->next);
|
// Other stack elements unknown
|
||||||
*want_level_p = kELvlValue;
|
//
|
||||||
|
// After: top_node_p = outer(*), points to new_op(op(x,y))
|
||||||
|
// &bop_node->children->next = new_op(op(x,y),*), points to NULL
|
||||||
|
*top_node_p = bop_node;
|
||||||
|
bop_node->children = top_node;
|
||||||
|
assert(bop_node->children->next == NULL);
|
||||||
|
kvi_push(*ast_stack, top_node_p);
|
||||||
|
kvi_push(*ast_stack, &bop_node->children->next);
|
||||||
|
} else {
|
||||||
|
assert(top_node_lvl == bop_node_lvl && top_node_ass == kEOpAssRight);
|
||||||
|
assert(top_node->children != NULL && top_node->children->next != NULL);
|
||||||
|
// outer(op(x,y)) -> outer(op(x,new_op(y,*)))
|
||||||
|
//
|
||||||
|
// Before: top_node_p = outer(*), points to op(x,y)
|
||||||
|
// Other stack elements unknown
|
||||||
|
//
|
||||||
|
// After: top_node_p = outer(*), points to op(x,new_op(y))
|
||||||
|
// &top_node->children->next = op(x,*), points to new_op(y)
|
||||||
|
// &bop_node->children->next = new_op(y,*), points to NULL
|
||||||
|
bop_node->children = top_node->children->next;
|
||||||
|
top_node->children->next = bop_node;
|
||||||
|
assert(bop_node->children->next == NULL);
|
||||||
|
kvi_push(*ast_stack, top_node_p);
|
||||||
|
kvi_push(*ast_stack, &top_node->children->next);
|
||||||
|
kvi_push(*ast_stack, &bop_node->children->next);
|
||||||
|
}
|
||||||
|
*want_node_p = (*want_node_p == kENodeArgumentSeparator
|
||||||
|
? kENodeArgument
|
||||||
|
: kENodeValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ParserPosition literal based on ParserPosition pos with columns shifted
|
||||||
|
///
|
||||||
|
/// Function does not check whether remaining position is valid.
|
||||||
|
///
|
||||||
|
/// @param[in] pos Position to shift.
|
||||||
|
/// @param[in] shift Number of bytes to shift.
|
||||||
|
///
|
||||||
|
/// @return Shifted position.
|
||||||
|
static inline ParserPosition shifted_pos(const ParserPosition pos,
|
||||||
|
const size_t shift)
|
||||||
|
FUNC_ATTR_CONST FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_WARN_UNUSED_RESULT
|
||||||
|
{
|
||||||
|
return (ParserPosition) { .line = pos.line, .col = pos.col + shift };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get highlight group name
|
/// Get highlight group name
|
||||||
@@ -692,12 +824,25 @@ static void viml_pexpr_handle_bop(ExprASTStack *const ast_stack,
|
|||||||
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_level); \
|
viml_pexpr_handle_bop(&ast_stack, cur_node, &want_node); \
|
||||||
is_invalid = true; \
|
|
||||||
goto viml_pexpr_parse_process_token; \
|
goto viml_pexpr_parse_process_token; \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
/// Record missing value: for things like "* 5"
|
||||||
|
///
|
||||||
|
/// @param[in] msg Error message.
|
||||||
|
#define ADD_VALUE_IF_MISSING(msg) \
|
||||||
|
do { \
|
||||||
|
if (want_node == kENodeValue) { \
|
||||||
|
ERROR_FROM_TOKEN_AND_MSG(cur_token, (msg)); \
|
||||||
|
NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeMissing); \
|
||||||
|
cur_node->len = 0; \
|
||||||
|
*top_node_p = cur_node; \
|
||||||
|
want_node = kENodeOperator; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
/// Set AST error, unless AST already is not correct
|
/// Set AST error, unless AST already is not correct
|
||||||
///
|
///
|
||||||
/// @param[out] ret_ast AST to set error in.
|
/// @param[out] ret_ast AST to set error in.
|
||||||
@@ -721,14 +866,42 @@ static inline void east_set_error(ExprAST *const ret_ast,
|
|||||||
ret_ast->err.arg = pline.data + start.col;
|
ret_ast->err.arg = pline.data + start.col;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set error from the given kExprLexInvalid 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) \
|
||||||
east_set_error(&ast, pstate, msg, cur_token.start)
|
do { \
|
||||||
|
is_invalid = true; \
|
||||||
|
east_set_error(&ast, pstate, msg, cur_token.start); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/// Like #ERROR_FROM_TOKEN_AND_MSG, but gets position from a node
|
||||||
|
#define ERROR_FROM_NODE_AND_MSG(node, msg) \
|
||||||
|
do { \
|
||||||
|
is_invalid = true; \
|
||||||
|
east_set_error(&ast, pstate, msg, node->start); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
/// Set error from the given kExprLexInvalid token
|
/// Set error from the given kExprLexInvalid token
|
||||||
#define ERROR_FROM_TOKEN(cur_token) \
|
#define ERROR_FROM_TOKEN(cur_token) \
|
||||||
ERROR_FROM_TOKEN_AND_MSG(cur_token, cur_token.data.err.msg)
|
ERROR_FROM_TOKEN_AND_MSG(cur_token, cur_token.data.err.msg)
|
||||||
|
|
||||||
|
/// Select figure brace type, altering highlighting as well if needed
|
||||||
|
///
|
||||||
|
/// @param[out] node Node to modify type.
|
||||||
|
/// @param[in] new_type New type, one of ExprASTNodeType values without
|
||||||
|
/// kExprNode prefix.
|
||||||
|
/// @param[in] hl Corresponding highlighting, passed as an argument to #HL.
|
||||||
|
#define SELECT_FIGURE_BRACE_TYPE(node, new_type, hl) \
|
||||||
|
do { \
|
||||||
|
ExprASTNode *const node_ = (node); \
|
||||||
|
assert(node_->type == kExprNodeUnknownFigure \
|
||||||
|
|| node_->type == kExprNode##new_type); \
|
||||||
|
node_->type = kExprNode##new_type; \
|
||||||
|
if (pstate->colors) { \
|
||||||
|
kv_A(*pstate->colors, node_->data.fig.opening_hl_idx).group = \
|
||||||
|
HL(hl); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
/// Parse one VimL expression
|
/// Parse one VimL expression
|
||||||
///
|
///
|
||||||
/// @param pstate Parser state.
|
/// @param pstate Parser state.
|
||||||
@@ -751,13 +924,15 @@ ExprAST viml_pexpr_parse(ParserState *const pstate, const int flags)
|
|||||||
kvi_init(ast_stack);
|
kvi_init(ast_stack);
|
||||||
kvi_push(ast_stack, &ast.root);
|
kvi_push(ast_stack, &ast.root);
|
||||||
// Expressions stack:
|
// Expressions stack:
|
||||||
// 1. *last is NULL if want_level is kExprLexValue. Indicates where expression
|
// 1. *last is NULL if want_node is kExprLexValue. Indicates where expression
|
||||||
// is to be put.
|
// is to be put.
|
||||||
// 2. *last is not NULL otherwise, indicates current expression to be used as
|
// 2. *last is not NULL otherwise, indicates current expression to be used as
|
||||||
// an operator argument.
|
// an operator argument.
|
||||||
ExprASTLevel want_level = kELvlValue;
|
ExprASTWantedNode want_node = kENodeValue;
|
||||||
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.
|
||||||
|
ExprASTNode *lambda_node = NULL;
|
||||||
do {
|
do {
|
||||||
LexExprToken cur_token = viml_pexpr_next_token(pstate, true);
|
LexExprToken cur_token = viml_pexpr_next_token(pstate, true);
|
||||||
if (cur_token.type == kExprLexEOC) {
|
if (cur_token.type == kExprLexEOC) {
|
||||||
@@ -789,8 +964,7 @@ ExprAST viml_pexpr_parse(ParserState *const pstate, const int flags)
|
|||||||
viml_pexpr_parse_process_token:
|
viml_pexpr_parse_process_token:
|
||||||
if (tok_type == kExprLexSpacing) {
|
if (tok_type == kExprLexSpacing) {
|
||||||
if (is_invalid) {
|
if (is_invalid) {
|
||||||
viml_parser_highlight(pstate, cur_token.start, cur_token.len,
|
HL_CUR_TOKEN(Spacing);
|
||||||
HL(Spacing));
|
|
||||||
} else {
|
} else {
|
||||||
// Do not do anything: let regular spacing be highlighted as normal.
|
// Do not do anything: let regular spacing be highlighted as normal.
|
||||||
// This also allows later to highlight spacing as invalid.
|
// This also allows later to highlight spacing as invalid.
|
||||||
@@ -803,11 +977,44 @@ viml_pexpr_parse_process_token:
|
|||||||
is_invalid = false;
|
is_invalid = false;
|
||||||
highlighted_prev_spacing = true;
|
highlighted_prev_spacing = true;
|
||||||
}
|
}
|
||||||
|
const ParserLine pline = pstate->reader.lines.items[cur_token.start.line];
|
||||||
ExprASTNode **const top_node_p = kv_last(ast_stack);
|
ExprASTNode **const top_node_p = kv_last(ast_stack);
|
||||||
ExprASTNode *cur_node = NULL;
|
ExprASTNode *cur_node = NULL;
|
||||||
// Keep these two asserts separate for debugging purposes.
|
assert((want_node == kENodeValue || want_node == kENodeArgument)
|
||||||
assert(want_level == kELvlValue || *top_node_p != NULL);
|
== (*top_node_p == NULL));
|
||||||
assert(want_level != kELvlValue || *top_node_p == NULL);
|
if ((want_node == kENodeArgumentSeparator
|
||||||
|
&& tok_type != kExprLexComma
|
||||||
|
&& tok_type != kExprLexArrow)
|
||||||
|
|| (want_node == kENodeArgument
|
||||||
|
&& !(tok_type == kExprLexPlainIdentifier
|
||||||
|
&& cur_token.data.var.scope == 0
|
||||||
|
&& !cur_token.data.var.autoload)
|
||||||
|
&& tok_type != kExprLexArrow)) {
|
||||||
|
lambda_node->data.fig.type_guesses.allow_lambda = false;
|
||||||
|
if (lambda_node->children != NULL
|
||||||
|
&& lambda_node->children->type == kExprNodeComma) {
|
||||||
|
// If lambda has comma child this means that parser has already seen at
|
||||||
|
// least "{arg1,", so node cannot possibly be anything, but lambda.
|
||||||
|
|
||||||
|
// Vim may give E121 or E720 in this case, but it does not look right to
|
||||||
|
// have either because both are results of reevaluation possibly-lambda
|
||||||
|
// node as a dictionary and here this is not going to happen.
|
||||||
|
ERROR_FROM_TOKEN_AND_MSG(
|
||||||
|
cur_token, _("E15: Expected lambda arguments list or arrow: %.*s"));
|
||||||
|
} else {
|
||||||
|
// Else it may appear that possibly-lambda node is actually a dictionary
|
||||||
|
// or curly-braces-name identifier.
|
||||||
|
lambda_node = NULL;
|
||||||
|
if (want_node == kENodeArgumentSeparator) {
|
||||||
|
want_node = kENodeOperator;
|
||||||
|
} else {
|
||||||
|
want_node = kENodeValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(lambda_node == NULL
|
||||||
|
|| want_node == kENodeArgumentSeparator
|
||||||
|
|| want_node == kENodeArgument);
|
||||||
switch (tok_type) {
|
switch (tok_type) {
|
||||||
case kExprLexEOC: {
|
case kExprLexEOC: {
|
||||||
assert(false);
|
assert(false);
|
||||||
@@ -818,13 +1025,12 @@ viml_pexpr_parse_process_token:
|
|||||||
goto viml_pexpr_parse_process_token;
|
goto viml_pexpr_parse_process_token;
|
||||||
}
|
}
|
||||||
case kExprLexRegister: {
|
case kExprLexRegister: {
|
||||||
if (want_level == kELvlValue) {
|
if (want_node == kENodeValue) {
|
||||||
NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeRegister);
|
NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeRegister);
|
||||||
cur_node->data.reg.name = cur_token.data.reg.name;
|
cur_node->data.reg.name = cur_token.data.reg.name;
|
||||||
*top_node_p = cur_node;
|
*top_node_p = cur_node;
|
||||||
want_level = kELvlOperator;
|
want_node = kENodeOperator;
|
||||||
viml_parser_highlight(pstate, cur_token.start, cur_token.len,
|
HL_CUR_TOKEN(Register);
|
||||||
HL(Register));
|
|
||||||
} else {
|
} else {
|
||||||
// Register in operator position: e.g. @a @a
|
// Register in operator position: e.g. @a @a
|
||||||
OP_MISSING;
|
OP_MISSING;
|
||||||
@@ -832,23 +1038,343 @@ viml_pexpr_parse_process_token:
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case kExprLexPlus: {
|
case kExprLexPlus: {
|
||||||
if (want_level == kELvlValue) {
|
if (want_node == kENodeValue) {
|
||||||
// Value level: assume unary plus
|
// Value level: assume unary plus
|
||||||
NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeUnaryPlus);
|
NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeUnaryPlus);
|
||||||
*top_node_p = cur_node;
|
*top_node_p = cur_node;
|
||||||
kvi_push(ast_stack, &cur_node->children);
|
kvi_push(ast_stack, &cur_node->children);
|
||||||
HL_CUR_TOKEN(UnaryPlus);
|
HL_CUR_TOKEN(UnaryPlus);
|
||||||
} else if (want_level < kELvlValue) {
|
} 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_level);
|
viml_pexpr_handle_bop(&ast_stack, cur_node, &want_node);
|
||||||
HL_CUR_TOKEN(BinaryPlus);
|
HL_CUR_TOKEN(BinaryPlus);
|
||||||
}
|
}
|
||||||
want_level = kELvlValue;
|
want_node = kENodeValue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case kExprLexComma: {
|
||||||
|
assert(want_node != kENodeArgument);
|
||||||
|
if (want_node == kENodeValue) {
|
||||||
|
// Value level: comma appearing here is not valid.
|
||||||
|
// Note: in Vim string(,x) will give E116, this is not the case here.
|
||||||
|
ERROR_FROM_TOKEN_AND_MSG(
|
||||||
|
cur_token, _("E15: Expected value, got comma: %.*s"));
|
||||||
|
NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeMissing);
|
||||||
|
cur_node->len = 0;
|
||||||
|
*top_node_p = cur_node;
|
||||||
|
want_node = (want_node == kENodeArgument
|
||||||
|
? kENodeArgumentSeparator
|
||||||
|
: kENodeOperator);
|
||||||
|
}
|
||||||
|
if (want_node == kENodeArgumentSeparator) {
|
||||||
|
assert(lambda_node->data.fig.type_guesses.allow_lambda);
|
||||||
|
assert(lambda_node != NULL);
|
||||||
|
SELECT_FIGURE_BRACE_TYPE(lambda_node, Lambda, Lambda);
|
||||||
|
}
|
||||||
|
if (kv_size(ast_stack) < 2) {
|
||||||
|
goto viml_pexpr_parse_invalid_comma;
|
||||||
|
}
|
||||||
|
for (size_t i = 1; i < kv_size(ast_stack); i++) {
|
||||||
|
const ExprASTNode *const *const eastnode_p =
|
||||||
|
(const ExprASTNode *const *)kv_Z(ast_stack, i);
|
||||||
|
if (!((*eastnode_p)->type == kExprNodeComma
|
||||||
|
|| ((*eastnode_p)->type == kExprNodeColon
|
||||||
|
&& i == 1))
|
||||||
|
|| i == kv_size(ast_stack) - 1) {
|
||||||
|
switch ((*eastnode_p)->type) {
|
||||||
|
case kExprNodeLambda: {
|
||||||
|
assert(want_node == kENodeArgumentSeparator);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case kExprNodeDictLiteral:
|
||||||
|
case kExprNodeListLiteral:
|
||||||
|
case kExprNodeCall: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
viml_pexpr_parse_invalid_comma:
|
||||||
|
ERROR_FROM_TOKEN_AND_MSG(
|
||||||
|
cur_token,
|
||||||
|
_("E15: Comma outside of call, lambda or literal: %.*s"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeComma);
|
||||||
|
viml_pexpr_handle_bop(&ast_stack, cur_node, &want_node);
|
||||||
|
HL_CUR_TOKEN(Comma);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case kExprLexColon: {
|
||||||
|
ADD_VALUE_IF_MISSING(_("E15: Expected value, got colon: %.*s"));
|
||||||
|
if (kv_size(ast_stack) < 2) {
|
||||||
|
goto viml_pexpr_parse_invalid_colon;
|
||||||
|
}
|
||||||
|
for (size_t i = 1; i < kv_size(ast_stack); i++) {
|
||||||
|
ExprASTNode *const *const eastnode_p =
|
||||||
|
(ExprASTNode *const *)kv_Z(ast_stack, i);
|
||||||
|
if ((*eastnode_p)->type != kExprNodeColon
|
||||||
|
|| i == kv_size(ast_stack) - 1) {
|
||||||
|
switch ((*eastnode_p)->type) {
|
||||||
|
case kExprNodeUnknownFigure: {
|
||||||
|
SELECT_FIGURE_BRACE_TYPE((*eastnode_p), DictLiteral, Dict);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case kExprNodeComma:
|
||||||
|
case kExprNodeDictLiteral:
|
||||||
|
case kExprNodeTernary: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
viml_pexpr_parse_invalid_colon:
|
||||||
|
ERROR_FROM_TOKEN_AND_MSG(
|
||||||
|
cur_token,
|
||||||
|
_("E15: Colon outside of dictionary or ternary operator: "
|
||||||
|
"%.*s"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeColon);
|
||||||
|
viml_pexpr_handle_bop(&ast_stack, cur_node, &want_node);
|
||||||
|
// FIXME: Handle ternary operator.
|
||||||
|
HL_CUR_TOKEN(Colon);
|
||||||
|
want_node = kENodeValue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case kExprLexFigureBrace: {
|
||||||
|
if (cur_token.data.brc.closing) {
|
||||||
|
ExprASTNode **new_top_node_p = NULL;
|
||||||
|
// Always drop the topmost value:
|
||||||
|
//
|
||||||
|
// 1. When want_node != kENodeValue topmost item on stack is
|
||||||
|
// a *finished* left operand, which may as well be "{@a}" which
|
||||||
|
// needs not be finished again.
|
||||||
|
// 2. Otherwise it is pointing to NULL what nobody wants.
|
||||||
|
kv_drop(ast_stack, 1);
|
||||||
|
if (!kv_size(ast_stack)) {
|
||||||
|
NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeUnknownFigure);
|
||||||
|
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 = false;
|
||||||
|
cur_node->len = 0;
|
||||||
|
if (want_node != kENodeValue) {
|
||||||
|
cur_node->children = *top_node_p;
|
||||||
|
}
|
||||||
|
*top_node_p = cur_node;
|
||||||
|
goto viml_pexpr_parse_figure_brace_closing_error;
|
||||||
|
}
|
||||||
|
if (want_node == kENodeValue) {
|
||||||
|
if ((*kv_last(ast_stack))->type != kExprNodeUnknownFigure
|
||||||
|
&& (*kv_last(ast_stack))->type != kExprNodeComma) {
|
||||||
|
// kv_last being UnknownFigure may occur for empty dictionary
|
||||||
|
// literal, while Comma is expected in case of non-empty one.
|
||||||
|
ERROR_FROM_TOKEN_AND_MSG(
|
||||||
|
cur_token,
|
||||||
|
_("E15: Expected value, got closing figure brace: %.*s"));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!kv_size(ast_stack)) {
|
||||||
|
new_top_node_p = top_node_p;
|
||||||
|
goto viml_pexpr_parse_figure_brace_closing_error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
new_top_node_p = kv_pop(ast_stack);
|
||||||
|
} while (kv_size(ast_stack)
|
||||||
|
&& (new_top_node_p == NULL
|
||||||
|
|| ((*new_top_node_p)->type != kExprNodeUnknownFigure
|
||||||
|
&& (*new_top_node_p)->type != kExprNodeDictLiteral
|
||||||
|
&& ((*new_top_node_p)->type
|
||||||
|
!= kExprNodeCurlyBracesIdentifier)
|
||||||
|
&& (*new_top_node_p)->type != kExprNodeLambda)));
|
||||||
|
ExprASTNode *new_top_node = *new_top_node_p;
|
||||||
|
switch (new_top_node->type) {
|
||||||
|
case kExprNodeUnknownFigure: {
|
||||||
|
if (new_top_node->children == NULL) {
|
||||||
|
// No children of curly braces node indicates empty dictionary.
|
||||||
|
|
||||||
|
// Should actually be kENodeArgument, but that was changed
|
||||||
|
// earlier.
|
||||||
|
assert(want_node == kENodeValue);
|
||||||
|
assert(new_top_node->data.fig.type_guesses.allow_dict);
|
||||||
|
SELECT_FIGURE_BRACE_TYPE(new_top_node, DictLiteral, Dict);
|
||||||
|
HL_CUR_TOKEN(Dict);
|
||||||
|
} else if (new_top_node->data.fig.type_guesses.allow_ident) {
|
||||||
|
SELECT_FIGURE_BRACE_TYPE(new_top_node, CurlyBracesIdentifier,
|
||||||
|
Curly);
|
||||||
|
HL_CUR_TOKEN(Curly);
|
||||||
|
} else {
|
||||||
|
// If by this time type of the node has not already been
|
||||||
|
// guessed, but it definitely is not a curly braces name then
|
||||||
|
// it is invalid for sure.
|
||||||
|
ERROR_FROM_NODE_AND_MSG(
|
||||||
|
new_top_node,
|
||||||
|
_("E15: Don't know what figure brace means: %.*s"));
|
||||||
|
if (pstate->colors) {
|
||||||
|
// Will reset to NVimInvalidFigureBrace.
|
||||||
|
kv_A(*pstate->colors,
|
||||||
|
new_top_node->data.fig.opening_hl_idx).group = (
|
||||||
|
HL(FigureBrace));
|
||||||
|
}
|
||||||
|
HL_CUR_TOKEN(FigureBrace);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case kExprNodeDictLiteral: {
|
||||||
|
HL_CUR_TOKEN(Dict);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case kExprNodeCurlyBracesIdentifier: {
|
||||||
|
HL_CUR_TOKEN(Curly);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case kExprNodeLambda: {
|
||||||
|
HL_CUR_TOKEN(Lambda);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
viml_pexpr_parse_figure_brace_closing_error:
|
||||||
|
assert(!kv_size(ast_stack));
|
||||||
|
ERROR_FROM_TOKEN_AND_MSG(
|
||||||
|
cur_token, _("E15: Unexpected closing figure brace: %.*s"));
|
||||||
|
HL_CUR_TOKEN(FigureBrace);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
kvi_push(ast_stack, new_top_node_p);
|
||||||
|
want_node = kENodeOperator;
|
||||||
|
} else {
|
||||||
|
if (want_node == kENodeValue) {
|
||||||
|
HL_CUR_TOKEN(FigureBrace);
|
||||||
|
// Value: may be any of lambda, dictionary literal and curly braces
|
||||||
|
// name.
|
||||||
|
NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeUnknownFigure);
|
||||||
|
cur_node->data.fig.type_guesses.allow_lambda = true;
|
||||||
|
cur_node->data.fig.type_guesses.allow_dict = true;
|
||||||
|
cur_node->data.fig.type_guesses.allow_ident = true;
|
||||||
|
if (pstate->colors) {
|
||||||
|
cur_node->data.fig.opening_hl_idx = kv_size(*pstate->colors) - 1;
|
||||||
|
}
|
||||||
|
*top_node_p = cur_node;
|
||||||
|
kvi_push(ast_stack, &cur_node->children);
|
||||||
|
want_node = kENodeArgument;
|
||||||
|
lambda_node = cur_node;
|
||||||
|
} else {
|
||||||
|
// Operator: may only be curly braces name, but only under certain
|
||||||
|
// conditions.
|
||||||
|
|
||||||
|
// First condition is that there is no space before {.
|
||||||
|
if (prev_token.type == kExprLexSpacing) {
|
||||||
|
OP_MISSING;
|
||||||
|
}
|
||||||
|
switch ((*top_node_p)->type) {
|
||||||
|
// Second is that previous node is one of the identifiers:
|
||||||
|
// complex, plain, curly braces.
|
||||||
|
|
||||||
|
// TODO(ZyX-I): Extend syntax to allow ${expr}. This is needed to
|
||||||
|
// handle environment variables like those bash uses for
|
||||||
|
// `export -f`: their names consist not only of alphanumeric
|
||||||
|
// characetrs.
|
||||||
|
case kExprNodeComplexIdentifier:
|
||||||
|
case kExprNodePlainIdentifier:
|
||||||
|
case kExprNodeCurlyBracesIdentifier: {
|
||||||
|
NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeComplexIdentifier);
|
||||||
|
cur_node->len = 0;
|
||||||
|
viml_pexpr_handle_bop(&ast_stack, cur_node, &want_node);
|
||||||
|
ExprASTNode *const new_top_node = *kv_last(ast_stack);
|
||||||
|
assert(new_top_node->next == NULL);
|
||||||
|
NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeCurlyBracesIdentifier);
|
||||||
|
new_top_node->next = cur_node;
|
||||||
|
kvi_push(ast_stack, &cur_node->children);
|
||||||
|
HL_CUR_TOKEN(Curly);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
OP_MISSING;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case kExprLexArrow: {
|
||||||
|
if (want_node == kENodeArgumentSeparator
|
||||||
|
|| want_node == kENodeArgument) {
|
||||||
|
if (want_node == kENodeArgument) {
|
||||||
|
kv_drop(ast_stack, 1);
|
||||||
|
}
|
||||||
|
assert(kv_size(ast_stack) >= 1);
|
||||||
|
while ((*kv_last(ast_stack))->type != kExprNodeLambda
|
||||||
|
&& (*kv_last(ast_stack))->type != kExprNodeUnknownFigure) {
|
||||||
|
kv_drop(ast_stack, 1);
|
||||||
|
}
|
||||||
|
assert((*kv_last(ast_stack)) == lambda_node);
|
||||||
|
SELECT_FIGURE_BRACE_TYPE(lambda_node, Lambda, Lambda);
|
||||||
|
NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeArrow);
|
||||||
|
if (lambda_node->children == NULL) {
|
||||||
|
assert(want_node == kENodeArgument);
|
||||||
|
lambda_node->children = cur_node;
|
||||||
|
kvi_push(ast_stack, &lambda_node->children);
|
||||||
|
} else {
|
||||||
|
assert(lambda_node->children->next == NULL);
|
||||||
|
lambda_node->children->next = cur_node;
|
||||||
|
kvi_push(ast_stack, &lambda_node->children->next);
|
||||||
|
}
|
||||||
|
kvi_push(ast_stack, &cur_node->children);
|
||||||
|
lambda_node = NULL;
|
||||||
|
} else {
|
||||||
|
// Only first branch is valid.
|
||||||
|
is_invalid = true;
|
||||||
|
ADD_VALUE_IF_MISSING(_("E15: Unexpected arrow: %.*s"));
|
||||||
|
NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeArrow);
|
||||||
|
viml_pexpr_handle_bop(&ast_stack, cur_node, &want_node);
|
||||||
|
}
|
||||||
|
want_node = kENodeValue;
|
||||||
|
HL_CUR_TOKEN(Arrow);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case kExprLexPlainIdentifier: {
|
||||||
|
if (want_node == kENodeValue || want_node == kENodeArgument) {
|
||||||
|
want_node = (want_node == kENodeArgument
|
||||||
|
? kENodeArgumentSeparator
|
||||||
|
: kENodeOperator);
|
||||||
|
// FIXME: It is not valid to have scope inside complex identifier,
|
||||||
|
// check that.
|
||||||
|
NEW_NODE_WITH_CUR_POS(cur_node, kExprNodePlainIdentifier);
|
||||||
|
cur_node->data.var.scope = cur_token.data.var.scope;
|
||||||
|
const size_t scope_shift = (cur_token.data.var.scope == 0
|
||||||
|
? 0
|
||||||
|
: 2);
|
||||||
|
cur_node->data.var.ident = (pline.data + cur_token.start.col
|
||||||
|
+ scope_shift);
|
||||||
|
cur_node->data.var.ident_len = cur_token.len - scope_shift;
|
||||||
|
*top_node_p = cur_node;
|
||||||
|
if (scope_shift) {
|
||||||
|
viml_parser_highlight(pstate, cur_token.start, 1,
|
||||||
|
HL(IdentifierScope));
|
||||||
|
viml_parser_highlight(pstate, shifted_pos(cur_token.start, 1), 1,
|
||||||
|
HL(IdentifierScopeDelimiter));
|
||||||
|
}
|
||||||
|
if (scope_shift < cur_token.len) {
|
||||||
|
viml_parser_highlight(pstate, shifted_pos(cur_token.start,
|
||||||
|
scope_shift),
|
||||||
|
cur_token.len - scope_shift,
|
||||||
|
HL(Identifier));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
OP_MISSING;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case kExprLexParenthesis: {
|
case kExprLexParenthesis: {
|
||||||
if (cur_token.data.brc.closing) {
|
if (cur_token.data.brc.closing) {
|
||||||
if (want_level == kELvlValue) {
|
if (want_node == kENodeValue) {
|
||||||
if (kv_size(ast_stack) > 1) {
|
if (kv_size(ast_stack) > 1) {
|
||||||
const ExprASTNode *const prev_top_node = *kv_Z(ast_stack, 1);
|
const ExprASTNode *const prev_top_node = *kv_Z(ast_stack, 1);
|
||||||
if (prev_top_node->type == kExprNodeCall) {
|
if (prev_top_node->type == kExprNodeCall) {
|
||||||
@@ -858,15 +1384,15 @@ viml_pexpr_parse_process_token:
|
|||||||
goto viml_pexpr_parse_no_paren_closing_error;
|
goto viml_pexpr_parse_no_paren_closing_error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is_invalid = true;
|
ERROR_FROM_TOKEN_AND_MSG(
|
||||||
ERROR_FROM_TOKEN_AND_MSG(cur_token, _("E15: Expected value: %.*s"));
|
cur_token, _("E15: Expected value, got parenthesis: %.*s"));
|
||||||
NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeMissing);
|
NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeMissing);
|
||||||
cur_node->len = 0;
|
cur_node->len = 0;
|
||||||
*top_node_p = cur_node;
|
*top_node_p = cur_node;
|
||||||
} else {
|
} else {
|
||||||
// Always drop the topmost value: when want_level != kELvlValue
|
// Always drop the topmost value: when want_node != kENodeValue
|
||||||
// topmost item on stack is a *finished* left operand, which may as
|
// topmost item on stack is a *finished* left operand, which may as
|
||||||
// well be "(@a)" which needs not be finished.
|
// well be "(@a)" which needs not be finished again.
|
||||||
kv_drop(ast_stack, 1);
|
kv_drop(ast_stack, 1);
|
||||||
}
|
}
|
||||||
viml_pexpr_parse_no_paren_closing_error: {}
|
viml_pexpr_parse_no_paren_closing_error: {}
|
||||||
@@ -892,10 +1418,9 @@ viml_pexpr_parse_no_paren_closing_error: {}
|
|||||||
if (new_top_node_p == NULL) {
|
if (new_top_node_p == NULL) {
|
||||||
new_top_node_p = top_node_p;
|
new_top_node_p = top_node_p;
|
||||||
}
|
}
|
||||||
is_invalid = true;
|
|
||||||
HL_CUR_TOKEN(NestingParenthesis);
|
|
||||||
ERROR_FROM_TOKEN_AND_MSG(
|
ERROR_FROM_TOKEN_AND_MSG(
|
||||||
cur_token, _("E15: Unexpected closing parenthesis: %.*s"));
|
cur_token, _("E15: Unexpected closing parenthesis: %.*s"));
|
||||||
|
HL_CUR_TOKEN(NestingParenthesis);
|
||||||
cur_node = NEW_NODE(kExprNodeNested);
|
cur_node = NEW_NODE(kExprNodeNested);
|
||||||
cur_node->start = cur_token.start;
|
cur_node->start = cur_token.start;
|
||||||
cur_node->len = 0;
|
cur_node->len = 0;
|
||||||
@@ -906,14 +1431,14 @@ viml_pexpr_parse_no_paren_closing_error: {}
|
|||||||
assert(cur_node->next == NULL);
|
assert(cur_node->next == NULL);
|
||||||
}
|
}
|
||||||
kvi_push(ast_stack, new_top_node_p);
|
kvi_push(ast_stack, new_top_node_p);
|
||||||
want_level = kELvlOperator;
|
want_node = kENodeOperator;
|
||||||
} else {
|
} else {
|
||||||
if (want_level == kELvlValue) {
|
if (want_node == kENodeValue) {
|
||||||
NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeNested);
|
NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeNested);
|
||||||
*top_node_p = cur_node;
|
*top_node_p = cur_node;
|
||||||
kvi_push(ast_stack, &cur_node->children);
|
kvi_push(ast_stack, &cur_node->children);
|
||||||
HL_CUR_TOKEN(NestingParenthesis);
|
HL_CUR_TOKEN(NestingParenthesis);
|
||||||
} else if (want_level == kELvlOperator) {
|
} else if (want_node == kENodeOperator) {
|
||||||
if (prev_token.type == kExprLexSpacing) {
|
if (prev_token.type == kExprLexSpacing) {
|
||||||
// For some reason "function (args)" is a function call, but
|
// For some reason "function (args)" is a function call, but
|
||||||
// "(funcref) (args)" is not. AFAIR this somehow involves
|
// "(funcref) (args)" is not. AFAIR this somehow involves
|
||||||
@@ -926,13 +1451,13 @@ 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_level);
|
viml_pexpr_handle_bop(&ast_stack, cur_node, &want_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.
|
||||||
assert(false);
|
assert(false);
|
||||||
}
|
}
|
||||||
want_level = kELvlValue;
|
want_node = kENodeValue;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -943,8 +1468,9 @@ viml_pexpr_parse_cycle_end:
|
|||||||
viml_parser_advance(pstate, cur_token.len);
|
viml_parser_advance(pstate, cur_token.len);
|
||||||
} while (true);
|
} while (true);
|
||||||
viml_pexpr_parse_end:
|
viml_pexpr_parse_end:
|
||||||
if (want_level == kELvlValue) {
|
if (want_node == kENodeValue) {
|
||||||
east_set_error(&ast, pstate, _("E15: Expected value: %.*s"), pstate->pos);
|
east_set_error(&ast, pstate, _("E15: Expected value, got EOC: %.*s"),
|
||||||
|
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.
|
||||||
|
|
||||||
@@ -956,7 +1482,7 @@ viml_pexpr_parse_end:
|
|||||||
kv_drop(ast_stack, 1);
|
kv_drop(ast_stack, 1);
|
||||||
while (ast.correct && kv_size(ast_stack)) {
|
while (ast.correct && 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_level == kELvlValue.
|
// This should only happen when want_node == kENodeValue.
|
||||||
assert(cur_node != NULL);
|
assert(cur_node != NULL);
|
||||||
switch (cur_node->type) {
|
switch (cur_node->type) {
|
||||||
case kExprNodeOpMissing:
|
case kExprNodeOpMissing:
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
#define NVIM_VIML_PARSER_EXPRESSIONS_H
|
#define NVIM_VIML_PARSER_EXPRESSIONS_H
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
#include "nvim/types.h"
|
#include "nvim/types.h"
|
||||||
@@ -130,6 +131,17 @@ typedef enum {
|
|||||||
kExprNodePlainIdentifier = 'i',
|
kExprNodePlainIdentifier = 'i',
|
||||||
/// Complex identifier: variable/function name with curly braces
|
/// Complex identifier: variable/function name with curly braces
|
||||||
kExprNodeComplexIdentifier = 'I',
|
kExprNodeComplexIdentifier = 'I',
|
||||||
|
/// Figure brace expression which is not yet known
|
||||||
|
///
|
||||||
|
/// May resolve to any of kExprNodeDictLiteral, kExprNodeLambda or
|
||||||
|
/// kExprNodeCurlyBracesIdentifier.
|
||||||
|
kExprNodeUnknownFigure = '{',
|
||||||
|
kExprNodeLambda = '\\', ///< Lambda.
|
||||||
|
kExprNodeDictLiteral = 'd', ///< Dictionary literal.
|
||||||
|
kExprNodeCurlyBracesIdentifier= '}', ///< Part of the curly braces name.
|
||||||
|
kExprNodeComma = ',', ///< Comma “operator”.
|
||||||
|
kExprNodeColon = ':', ///< Colon “operator”.
|
||||||
|
kExprNodeArrow = '>', ///< Arrow “operator”.
|
||||||
} ExprASTNodeType;
|
} ExprASTNodeType;
|
||||||
|
|
||||||
typedef struct expr_ast_node ExprASTNode;
|
typedef struct expr_ast_node ExprASTNode;
|
||||||
@@ -149,6 +161,27 @@ struct expr_ast_node {
|
|||||||
struct {
|
struct {
|
||||||
int name; ///< Register name, may be -1 if name not present.
|
int name; ///< Register name, may be -1 if name not present.
|
||||||
} reg; ///< For kExprNodeRegister.
|
} reg; ///< For kExprNodeRegister.
|
||||||
|
struct {
|
||||||
|
/// Which nodes UnknownFigure can’t possibly represent.
|
||||||
|
struct {
|
||||||
|
/// True if UnknownFigure may actually represent dictionary literal.
|
||||||
|
bool allow_dict;
|
||||||
|
/// True if UnknownFigure may actually represent lambda.
|
||||||
|
bool allow_lambda;
|
||||||
|
/// True if UnknownFigure may actually be part of curly braces name.
|
||||||
|
bool allow_ident;
|
||||||
|
} type_guesses;
|
||||||
|
/// Highlight chunk index, used for rehighlighting if needed
|
||||||
|
size_t opening_hl_idx;
|
||||||
|
} fig; ///< For kExprNodeUnknownFigure.
|
||||||
|
struct {
|
||||||
|
int scope; ///< Scope character or 0 if not present.
|
||||||
|
/// Actual identifier without scope.
|
||||||
|
///
|
||||||
|
/// Points to inside parser reader state.
|
||||||
|
const char *ident;
|
||||||
|
size_t ident_len; ///< Actual identifier length.
|
||||||
|
} var;
|
||||||
} data;
|
} data;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -166,6 +199,8 @@ enum {
|
|||||||
///
|
///
|
||||||
/// Without the flag they are only taken into account when parsing.
|
/// Without the flag they are only taken into account when parsing.
|
||||||
kExprFlagsPrintError = (1 << 2),
|
kExprFlagsPrintError = (1 << 2),
|
||||||
|
// WARNING: whenever you add a new flag, alter klee_assume() statement in
|
||||||
|
// viml_expressions_parser.c.
|
||||||
} ExprParserFlags;
|
} ExprParserFlags;
|
||||||
|
|
||||||
/// Structure representing complety AST for one expression
|
/// Structure representing complety AST for one expression
|
||||||
|
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user