aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/viml/parser/expressions.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/viml/parser/expressions.c')
-rw-r--r--src/nvim/viml/parser/expressions.c105
1 files changed, 72 insertions, 33 deletions
diff --git a/src/nvim/viml/parser/expressions.c b/src/nvim/viml/parser/expressions.c
index cabf2dac58..3027c0046b 100644
--- a/src/nvim/viml/parser/expressions.c
+++ b/src/nvim/viml/parser/expressions.c
@@ -47,10 +47,10 @@ typedef enum {
/// Get next token for the VimL expression input
///
/// @param pstate Parser state.
-/// @param[in] peek If true, do not advance pstate cursor.
+/// @param[in] flags Flags, @see LexExprFlags.
///
/// @return Next token.
-LexExprToken viml_pexpr_next_token(ParserState *const pstate, const bool peek)
+LexExprToken viml_pexpr_next_token(ParserState *const pstate, const int flags)
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
LexExprToken ret = {
@@ -153,12 +153,33 @@ LexExprToken viml_pexpr_next_token(ParserState *const pstate, const bool peek)
}
// Number.
- // Note: determining whether dot is (not) a part of a float needs more
- // context, so lexer does not do this.
- // FIXME: Resolve ambiguity by additional argument.
case '0': case '1': case '2': case '3': case '4': case '5': case '6':
case '7': case '8': case '9': {
+ ret.data.num.is_float = false;
CHARREG(kExprLexNumber, ascii_isdigit);
+ if (flags & kELFlagAllowFloat) {
+ if (pline.size > ret.len + 1
+ && pline.data[ret.len] == '.'
+ && ascii_isdigit(pline.data[ret.len + 1])) {
+ ret.len++;
+ ret.data.num.is_float = true;
+ CHARREG(kExprLexNumber, ascii_isdigit);
+ if (pline.size > ret.len + 1
+ && (pline.data[ret.len] == 'e'
+ || pline.data[ret.len] == 'E')
+ && ((pline.size > ret.len + 2
+ && (pline.data[ret.len + 1] == '+'
+ || pline.data[ret.len + 1] == '-')
+ && ascii_isdigit(pline.data[ret.len + 2]))
+ || ascii_isdigit(pline.data[ret.len + 1]))) {
+ ret.len++;
+ if (pline.data[ret.len] == '+' || pline.data[ret.len] == '-') {
+ ret.len++;
+ }
+ CHARREG(kExprLexNumber, ascii_isdigit);
+ }
+ }
+ }
break;
}
@@ -187,8 +208,9 @@ LexExprToken viml_pexpr_next_token(ParserState *const pstate, const bool peek)
ret.data.var.autoload = false;
CHARREG(kExprLexPlainIdentifier, ISWORD);
// "is" and "isnot" operators.
- if ((ret.len == 2 && memcmp(pline.data, "is", 2) == 0)
- || (ret.len == 5 && memcmp(pline.data, "isnot", 5) == 0)) {
+ if (!(flags & kELFlagIsNotCmp)
+ && ((ret.len == 2 && memcmp(pline.data, "is", 2) == 0)
+ || (ret.len == 5 && memcmp(pline.data, "isnot", 5) == 0))) {
ret.type = kExprLexComparison;
ret.data.cmp.type = kExprLexCmpIdentical;
ret.data.cmp.inv = (ret.len == 5);
@@ -197,14 +219,14 @@ LexExprToken viml_pexpr_next_token(ParserState *const pstate, const bool peek)
} else if (ret.len == 1
&& pline.size > 1
&& strchr("sgvbwtla", schar) != NULL
- && pline.data[ret.len] == ':') {
+ && pline.data[ret.len] == ':'
+ && !(flags & kELFlagForbidScope)) {
ret.len++;
ret.data.var.scope = schar;
CHARREG(kExprLexPlainIdentifier, ISWORD_OR_AUTOLOAD);
ret.data.var.autoload = (
memchr(pline.data + 2, AUTOLOAD_CHAR, ret.len - 2)
!= NULL);
- // FIXME: Resolve ambiguity with an argument to the lexer function.
// Previous CHARREG stopped at autoload character in order to make it
// possible to detect `is#`. Continue now with autoload characters
// included.
@@ -373,7 +395,30 @@ viml_pexpr_next_token_invalid_comparison:
// Expression end because Ex command ended.
case NUL:
case NL: {
- ret.type = kExprLexEOC;
+ if (flags & kELFlagForbidEOC) {
+ ret.type = kExprLexInvalid;
+ ret.data.err.msg = _("E15: Unexpected EOC character: %.*s");
+ ret.data.err.type = kExprLexSpacing;
+ } else {
+ ret.type = kExprLexEOC;
+ }
+ break;
+ }
+
+ case '|': {
+ if (pline.size >= 2 && pline.data[ret.len] == '|') {
+ // "||" is or.
+ ret.len++;
+ ret.type = kExprLexOr;
+ } else if (flags & kELFlagForbidEOC) {
+ // Note: `<C-r>=1 | 2<CR>` actually yields 1 in Vim without any
+ // errors. This will be changed here.
+ ret.type = kExprLexInvalid;
+ ret.data.err.msg = _("E15: Unexpected EOC character: %.*s");
+ ret.data.err.type = kExprLexOr;
+ } else {
+ ret.type = kExprLexEOC;
+ }
break;
}
@@ -389,7 +434,7 @@ viml_pexpr_next_token_invalid_comparison:
}
#undef GET_CCS
viml_pexpr_next_token_adv_return:
- if (!peek) {
+ if (!(flags & kELFlagPeek)) {
viml_parser_advance(pstate, ret.len);
}
return ret;
@@ -990,34 +1035,28 @@ ExprAST viml_pexpr_parse(ParserState *const pstate, const int flags)
// Lambda node, valid when parsing lambda arguments only.
ExprASTNode *lambda_node = NULL;
do {
- LexExprToken cur_token = viml_pexpr_next_token(pstate, true);
+ const int want_node_to_lexer_flags[] = {
+ [kENodeValue] = kELFlagIsNotCmp,
+ [kENodeOperator] = kELFlagForbidScope,
+ [kENodeArgument] = kELFlagIsNotCmp,
+ [kENodeArgumentSeparator] = kELFlagForbidScope,
+ };
+ // FIXME Determine when (not) to allow floating-point numbers.
+ const int lexer_additional_flags = (
+ kELFlagPeek
+ | ((flags & kExprFlagsDisallowEOC) ? kELFlagForbidEOC : 0));
+ LexExprToken cur_token = viml_pexpr_next_token(
+ pstate, want_node_to_lexer_flags[want_node] | lexer_additional_flags);
if (cur_token.type == kExprLexEOC) {
- if (flags & kExprFlagsDisallowEOC) {
- if (cur_token.len == 0) {
- // It is end of string, break.
- break;
- } else {
- // It is NL, NUL or bar.
- //
- // Note: `<C-r>=1 | 2<CR>` actually yields 1 in Vim without any
- // errors. This will be changed here.
- cur_token.type = kExprLexInvalid;
- cur_token.data.err.msg = _("E15: Unexpected EOC character: %.*s");
- const ParserLine pline = (
- pstate->reader.lines.items[cur_token.start.line]);
- const char eoc_char = pline.data[cur_token.start.col];
- cur_token.data.err.type = ((eoc_char == NUL || eoc_char == NL)
- ? kExprLexSpacing
- : kExprLexOr);
- }
- } else {
- break;
- }
+ break;
}
LexExprTokenType tok_type = cur_token.type;
const bool token_invalid = (tok_type == kExprLexInvalid);
bool is_invalid = token_invalid;
viml_pexpr_parse_process_token:
+ // May use different flags this time.
+ cur_token = viml_pexpr_next_token(
+ pstate, want_node_to_lexer_flags[want_node] | lexer_additional_flags);
if (tok_type == kExprLexSpacing) {
if (is_invalid) {
HL_CUR_TOKEN(Spacing);