diff options
author | dundargoc <33953936+dundargoc@users.noreply.github.com> | 2021-09-18 18:34:23 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-09-18 09:34:23 -0700 |
commit | 51a98aa0c2fe3231a0ffc8a78189bc6fafd6abf6 (patch) | |
tree | b0a582d67883864eda3231bce45dc7c159cbe97f /src/nvim/viml/parser/expressions.c | |
parent | 6cad86fffdb91d3997a707ff6adb0b5991587b3e (diff) | |
download | rneovim-51a98aa0c2fe3231a0ffc8a78189bc6fafd6abf6.tar.gz rneovim-51a98aa0c2fe3231a0ffc8a78189bc6fafd6abf6.tar.bz2 rneovim-51a98aa0c2fe3231a0ffc8a78189bc6fafd6abf6.zip |
refactor: format #15702
Diffstat (limited to 'src/nvim/viml/parser/expressions.c')
-rw-r--r-- | src/nvim/viml/parser/expressions.c | 3267 |
1 files changed, 1623 insertions, 1644 deletions
diff --git a/src/nvim/viml/parser/expressions.c b/src/nvim/viml/parser/expressions.c index 967d656dc5..c2aa923c49 100644 --- a/src/nvim/viml/parser/expressions.c +++ b/src/nvim/viml/parser/expressions.c @@ -50,20 +50,19 @@ // 7. 'isident' no longer applies to environment variables, they always include // ASCII alphanumeric characters and underscore and nothing except this. +#include <assert.h> #include <stdbool.h> #include <stddef.h> -#include <assert.h> #include <string.h> -#include "nvim/vim.h" -#include "nvim/memory.h" -#include "nvim/types.h" -#include "nvim/charset.h" #include "nvim/ascii.h" #include "nvim/assert.h" -#include "nvim/lib/kvec.h" +#include "nvim/charset.h" #include "nvim/eval/typval.h" - +#include "nvim/lib/kvec.h" +#include "nvim/memory.h" +#include "nvim/types.h" +#include "nvim/vim.h" #include "nvim/viml/parser/expressions.h" #include "nvim/viml/parser/parser.h" @@ -143,10 +142,8 @@ typedef enum { /// numbers are not supported. /// @param[in] exponent Exponent to scale by. /// @param[in] exponent_negative True if exponent is negative. -static inline float_T scale_number(const float_T num, - const uint8_t base, - const uvarnumber_T exponent, - const bool exponent_negative) +static inline float_T scale_number(const float_T num, const uint8_t base, + const uvarnumber_T exponent, const bool exponent_negative) FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_CONST { if (num == 0 || exponent == 0) { @@ -200,7 +197,7 @@ LexExprToken viml_pexpr_next_token(ParserState *const pstate, const int flags) if (ret.len < pline.size \ && strchr("?#", pline.data[ret.len]) != NULL) { \ ret.data.cmp.ccs = \ - (ExprCaseCompareStrategy)pline.data[ret.len]; \ + (ExprCaseCompareStrategy)pline.data[ret.len]; \ ret.len++; \ } else { \ ret.data.cmp.ccs = kCCStrategyUseOption; \ @@ -209,12 +206,12 @@ LexExprToken viml_pexpr_next_token(ParserState *const pstate, const int flags) switch (schar) { // Paired brackets. #define BRACKET(typ, opning, clsing) \ - case opning: \ - case clsing: { \ - ret.type = typ; \ - ret.data.brc.closing = (schar == clsing); \ - break; \ - } +case opning: \ +case clsing: { \ + ret.type = typ; \ + ret.data.brc.closing = (schar == clsing); \ + break; \ +} BRACKET(kExprLexParenthesis, '(', ')') BRACKET(kExprLexBracket, '[', ']') BRACKET(kExprLexFigureBrace, '{', '}') @@ -222,10 +219,10 @@ LexExprToken viml_pexpr_next_token(ParserState *const pstate, const int flags) // Single character tokens without data. #define CHAR(typ, ch) \ - case ch: { \ - ret.type = typ; \ - break; \ - } +case ch: { \ + ret.type = typ; \ + break; \ +} CHAR(kExprLexQuestion, '?') CHAR(kExprLexColon, ':') CHAR(kExprLexComma, ',') @@ -233,198 +230,265 @@ LexExprToken viml_pexpr_next_token(ParserState *const pstate, const int flags) // Multiplication/division/modulo. #define MUL(mul_type, ch) \ - case ch: { \ - ret.type = kExprLexMultiplication; \ - ret.data.mul.type = mul_type; \ - break; \ - } +case ch: { \ + ret.type = kExprLexMultiplication; \ + ret.data.mul.type = mul_type; \ + break; \ +} MUL(kExprLexMulMul, '*') MUL(kExprLexMulDiv, '/') MUL(kExprLexMulMod, '%') #undef MUL #define CHARREG(typ, cond) \ - do { \ - ret.type = typ; \ - for (; (ret.len < pline.size \ - && cond(pline.data[ret.len])) \ - ; ret.len++) { \ - } \ - } while (0) - - // Whitespace. - case ' ': - case TAB: { - CHARREG(kExprLexSpacing, ascii_iswhite); - break; - } - - // Control character, except for NUL, NL and TAB. - case Ctrl_A: case Ctrl_B: case Ctrl_C: case Ctrl_D: case Ctrl_E: - case Ctrl_F: case Ctrl_G: case Ctrl_H: + do { \ + ret.type = typ; \ + for (; (ret.len < pline.size \ + && cond(pline.data[ret.len])) \ + ; ret.len++) { \ + } \ + } while (0) - case Ctrl_K: case Ctrl_L: case Ctrl_M: case Ctrl_N: case Ctrl_O: - case Ctrl_P: case Ctrl_Q: case Ctrl_R: case Ctrl_S: case Ctrl_T: - case Ctrl_U: case Ctrl_V: case Ctrl_W: case Ctrl_X: case Ctrl_Y: - case Ctrl_Z: { + // Whitespace. + case ' ': + case TAB: + CHARREG(kExprLexSpacing, ascii_iswhite); + break; + + // Control character, except for NUL, NL and TAB. + case Ctrl_A: + case Ctrl_B: + case Ctrl_C: + case Ctrl_D: + case Ctrl_E: + case Ctrl_F: + case Ctrl_G: + case Ctrl_H: + + case Ctrl_K: + case Ctrl_L: + case Ctrl_M: + case Ctrl_N: + case Ctrl_O: + case Ctrl_P: + case Ctrl_Q: + case Ctrl_R: + case Ctrl_S: + case Ctrl_T: + case Ctrl_U: + case Ctrl_V: + case Ctrl_W: + case Ctrl_X: + case Ctrl_Y: + case Ctrl_Z: #define ISCTRL(schar) (schar < ' ') - CHARREG(kExprLexInvalid, ISCTRL); - ret.data.err.type = kExprLexSpacing; - ret.data.err.msg = - _("E15: Invalid control character present in input: %.*s"); - break; + CHARREG(kExprLexInvalid, ISCTRL); + ret.data.err.type = kExprLexSpacing; + ret.data.err.msg = + _("E15: Invalid control character present in input: %.*s"); + break; #undef ISCTRL - } - // Number. - 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; - ret.data.num.base = 10; - size_t frac_start = 0; - size_t exp_start = 0; - size_t frac_end = 0; - bool exp_negative = false; - CHARREG(kExprLexNumber, ascii_isdigit); - if (flags & kELFlagAllowFloat) { - const LexExprToken non_float_ret = ret; + // Number. + 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; + ret.data.num.base = 10; + size_t frac_start = 0; + size_t exp_start = 0; + size_t frac_end = 0; + bool exp_negative = false; + CHARREG(kExprLexNumber, ascii_isdigit); + if (flags & kELFlagAllowFloat) { + const LexExprToken non_float_ret = ret; + if (pline.size > ret.len + 1 + && pline.data[ret.len] == '.' + && ascii_isdigit(pline.data[ret.len + 1])) { + ret.len++; + frac_start = ret.len; + frac_end = ret.len; + ret.data.num.is_float = true; + for (; ret.len < pline.size && ascii_isdigit(pline.data[ret.len]) + ; ret.len++) { + // A small optimization: trailing zeroes in fractional part do not + // add anything to significand, so it is useless to include them in + // frac_end. + if (pline.data[ret.len] != '0') { + frac_end = ret.len + 1; + } + } if (pline.size > ret.len + 1 - && pline.data[ret.len] == '.' - && ascii_isdigit(pline.data[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++; - frac_start = ret.len; - frac_end = ret.len; - ret.data.num.is_float = true; - for (; ret.len < pline.size && ascii_isdigit(pline.data[ret.len]) - ; ret.len++) { - // A small optimization: trailing zeroes in fractional part do not - // add anything to significand, so it is useless to include them in - // frac_end. - if (pline.data[ret.len] != '0') { - frac_end = ret.len + 1; - } - } - 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]))) { + if (pline.data[ret.len] == '+' + || (exp_negative = (pline.data[ret.len] == '-'))) { ret.len++; - if (pline.data[ret.len] == '+' - || (exp_negative = (pline.data[ret.len] == '-'))) { - ret.len++; - } - exp_start = ret.len; - CHARREG(kExprLexNumber, ascii_isdigit); } - } - if (pline.size > ret.len - && (pline.data[ret.len] == '.' - || ASCII_ISALPHA(pline.data[ret.len]))) { - ret = non_float_ret; + exp_start = ret.len; + CHARREG(kExprLexNumber, ascii_isdigit); } } - // TODO(ZyX-I): detect overflows - if (ret.data.num.is_float) { - // Vim used to use string2float here which in turn uses strtod(). There - // are two problems with this approach: - // 1. strtod() is locale-dependent. Not sure how it is worked around so - // that I do not see relevant bugs, but it still does not look like - // a good idea. - // 2. strtod() does not accept length argument. - // - // The below variant of parsing floats was recognized as acceptable - // because it is basically how uClibc does the thing: it generates - // a number ignoring decimal point (but recording its position), then - // uses recorded position to scale number down when processing exponent. - float_T significand_part = 0; - uvarnumber_T exp_part = 0; - const size_t frac_size = (size_t)(frac_end - frac_start); - for (size_t i = 0; i < frac_end; i++) { - if (i == frac_start - 1) { - continue; - } - significand_part = significand_part * 10 + (pline.data[i] - '0'); - } - if (exp_start) { - vim_str2nr(pline.data + exp_start, NULL, NULL, 0, NULL, &exp_part, - (int)(ret.len - exp_start), false); + if (pline.size > ret.len + && (pline.data[ret.len] == '.' + || ASCII_ISALPHA(pline.data[ret.len]))) { + ret = non_float_ret; + } + } + // TODO(ZyX-I): detect overflows + if (ret.data.num.is_float) { + // Vim used to use string2float here which in turn uses strtod(). There + // are two problems with this approach: + // 1. strtod() is locale-dependent. Not sure how it is worked around so + // that I do not see relevant bugs, but it still does not look like + // a good idea. + // 2. strtod() does not accept length argument. + // + // The below variant of parsing floats was recognized as acceptable + // because it is basically how uClibc does the thing: it generates + // a number ignoring decimal point (but recording its position), then + // uses recorded position to scale number down when processing exponent. + float_T significand_part = 0; + uvarnumber_T exp_part = 0; + const size_t frac_size = (size_t)(frac_end - frac_start); + for (size_t i = 0; i < frac_end; i++) { + if (i == frac_start - 1) { + continue; } - if (exp_negative) { - exp_part += frac_size; + significand_part = significand_part * 10 + (pline.data[i] - '0'); + } + if (exp_start) { + vim_str2nr(pline.data + exp_start, NULL, NULL, 0, NULL, &exp_part, + (int)(ret.len - exp_start), false); + } + if (exp_negative) { + exp_part += frac_size; + } else { + if (exp_part < frac_size) { + exp_negative = true; + exp_part = frac_size - exp_part; } else { - if (exp_part < frac_size) { - exp_negative = true; - exp_part = frac_size - exp_part; - } else { - exp_part -= frac_size; - } + exp_part -= frac_size; } - ret.data.num.val.floating = scale_number(significand_part, 10, exp_part, - exp_negative); - } else { - int len; - int prep; - vim_str2nr(pline.data, &prep, &len, STR2NR_ALL, NULL, - &ret.data.num.val.integer, (int)pline.size, false); - ret.len = (size_t)len; - const uint8_t bases[] = { - [0] = 10, - ['0'] = 8, - ['x'] = 16, ['X'] = 16, - ['b'] = 2, ['B'] = 2, - }; - ret.data.num.base = bases[prep]; } - break; + ret.data.num.val.floating = scale_number(significand_part, 10, exp_part, + exp_negative); + } else { + int len; + int prep; + vim_str2nr(pline.data, &prep, &len, STR2NR_ALL, NULL, + &ret.data.num.val.integer, (int)pline.size, false); + ret.len = (size_t)len; + const uint8_t bases[] = { + [0] = 10, + ['0'] = 8, + ['x'] = 16, ['X'] = 16, + ['b'] = 2, ['B'] = 2, + }; + ret.data.num.base = bases[prep]; } + break; + } #define ISWORD_OR_AUTOLOAD(x) \ - (ascii_isident(x) || (x) == AUTOLOAD_CHAR) - - // Environment variable. - case '$': { - CHARREG(kExprLexEnv, ascii_isident); - break; - } - - // Normal variable/function name. - case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': - case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': - case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': - case 'v': case 'w': case 'x': case 'y': case 'z': - case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': - case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': - case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': - case 'V': case 'W': case 'X': case 'Y': case 'Z': - case '_': { - ret.data.var.scope = 0; - ret.data.var.autoload = false; - CHARREG(kExprLexPlainIdentifier, ascii_isident); - // "is" and "isnot" operators. - 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 = kExprCmpIdentical; - ret.data.cmp.inv = (ret.len == 5); - GET_CCS(ret, pline); + (ascii_isident(x) || (x) == AUTOLOAD_CHAR) + + // Environment variable. + case '$': + CHARREG(kExprLexEnv, ascii_isident); + break; + + // Normal variable/function name. + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'g': + case 'h': + case 'i': + case 'j': + case 'k': + case 'l': + case 'm': + case 'n': + case 'o': + case 'p': + case 'q': + case 'r': + case 's': + case 't': + case 'u': + case 'v': + case 'w': + case 'x': + case 'y': + case 'z': + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + case 'G': + case 'H': + case 'I': + case 'J': + case 'K': + case 'L': + case 'M': + case 'N': + case 'O': + case 'P': + case 'Q': + case 'R': + case 'S': + case 'T': + case 'U': + case 'V': + case 'W': + case 'X': + case 'Y': + case 'Z': + case '_': + ret.data.var.scope = 0; + ret.data.var.autoload = false; + CHARREG(kExprLexPlainIdentifier, ascii_isident); + // "is" and "isnot" operators. + 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 = kExprCmpIdentical; + ret.data.cmp.inv = (ret.len == 5); + GET_CCS(ret, pline); // Scope: `s:`, etc. - } else if (ret.len == 1 - && pline.size > 1 - && memchr(EXPR_VAR_SCOPE_LIST, schar, - sizeof(EXPR_VAR_SCOPE_LIST)) != NULL - && pline.data[ret.len] == ':' - && !(flags & kELFlagForbidScope)) { - ret.len++; - ret.data.var.scope = (ExprVarScope)schar; - CHARREG(kExprLexPlainIdentifier, ISWORD_OR_AUTOLOAD); - ret.data.var.autoload = ( - memchr(pline.data + 2, AUTOLOAD_CHAR, ret.len - 2) - != NULL); + } else if (ret.len == 1 + && pline.size > 1 + && memchr(EXPR_VAR_SCOPE_LIST, schar, + sizeof(EXPR_VAR_SCOPE_LIST)) != NULL + && pline.data[ret.len] == ':' + && !(flags & kELFlagForbidScope)) { + ret.len++; + ret.data.var.scope = (ExprVarScope)schar; + CHARREG(kExprLexPlainIdentifier, ISWORD_OR_AUTOLOAD); + ret.data.var.autoload = ( + memchr(pline.data + 2, AUTOLOAD_CHAR, ret.len - 2) + != NULL); // Previous CHARREG stopped at autoload character in order to make it // possible to detect `is#`. Continue now with autoload characters // included. @@ -432,221 +496,213 @@ LexExprToken viml_pexpr_next_token(ParserState *const pstate, const int flags) // Warning: there is ambiguity for the lexer: `is#Foo(1)` is a call of // function `is#Foo()`, `1is#Foo(1)` is a comparison `1 is# Foo(1)`. This // needs to be resolved on the higher level where context is available. - } else if (pline.size > ret.len - && pline.data[ret.len] == AUTOLOAD_CHAR) { - ret.data.var.autoload = true; - CHARREG(kExprLexPlainIdentifier, ISWORD_OR_AUTOLOAD); - } - break; + } else if (pline.size > ret.len + && pline.data[ret.len] == AUTOLOAD_CHAR) { + ret.data.var.autoload = true; + CHARREG(kExprLexPlainIdentifier, ISWORD_OR_AUTOLOAD); } + break; #undef ISWORD_OR_AUTOLOAD #undef CHARREG - // Option. - case '&': { + // Option. + case '&': { #define OPTNAMEMISS(ret) \ - do { \ - ret.type = kExprLexInvalid; \ - ret.data.err.type = kExprLexOption; \ - ret.data.err.msg = _("E112: Option name missing: %.*s"); \ - } while (0) - if (pline.size > 1 && pline.data[1] == '&') { - ret.type = kExprLexAnd; - ret.len++; - break; + do { \ + ret.type = kExprLexInvalid; \ + ret.data.err.type = kExprLexOption; \ + ret.data.err.msg = _("E112: Option name missing: %.*s"); \ + } while (0) + if (pline.size > 1 && pline.data[1] == '&') { + ret.type = kExprLexAnd; + ret.len++; + break; + } + if (pline.size == 1 || !ASCII_ISALPHA(pline.data[1])) { + OPTNAMEMISS(ret); + break; + } + ret.type = kExprLexOption; + if (pline.size > 2 + && pline.data[2] == ':' + && memchr(EXPR_OPT_SCOPE_LIST, pline.data[1], + sizeof(EXPR_OPT_SCOPE_LIST)) != NULL) { + ret.len += 2; + ret.data.opt.scope = (ExprOptScope)pline.data[1]; + ret.data.opt.name = pline.data + 3; + } else { + ret.data.opt.scope = kExprOptScopeUnspecified; + ret.data.opt.name = pline.data + 1; + } + const char *p = ret.data.opt.name; + const char *const e = pline.data + pline.size; + if (e - p >= 4 && p[0] == 't' && p[1] == '_') { + ret.data.opt.len = 4; + ret.len += 4; + } else { + for (; p < e && ASCII_ISALPHA(*p); p++) { } - if (pline.size == 1 || !ASCII_ISALPHA(pline.data[1])) { + ret.data.opt.len = (size_t)(p - ret.data.opt.name); + if (ret.data.opt.len == 0) { OPTNAMEMISS(ret); - break; - } - ret.type = kExprLexOption; - if (pline.size > 2 - && pline.data[2] == ':' - && memchr(EXPR_OPT_SCOPE_LIST, pline.data[1], - sizeof(EXPR_OPT_SCOPE_LIST)) != NULL) { - ret.len += 2; - ret.data.opt.scope = (ExprOptScope)pline.data[1]; - ret.data.opt.name = pline.data + 3; } else { - ret.data.opt.scope = kExprOptScopeUnspecified; - ret.data.opt.name = pline.data + 1; - } - const char *p = ret.data.opt.name; - const char *const e = pline.data + pline.size; - if (e - p >= 4 && p[0] == 't' && p[1] == '_') { - ret.data.opt.len = 4; - ret.len += 4; - } else { - for (; p < e && ASCII_ISALPHA(*p); p++) { - } - ret.data.opt.len = (size_t)(p - ret.data.opt.name); - if (ret.data.opt.len == 0) { - OPTNAMEMISS(ret); - } else { - ret.len += ret.data.opt.len; - } + ret.len += ret.data.opt.len; } - break; -#undef OPTNAMEMISS } + break; +#undef OPTNAMEMISS + } - // Register. - case '@': { - ret.type = kExprLexRegister; - if (pline.size > 1) { - ret.len++; - ret.data.reg.name = (uint8_t)pline.data[1]; - } else { - ret.data.reg.name = -1; - } - break; + // Register. + case '@': + ret.type = kExprLexRegister; + if (pline.size > 1) { + ret.len++; + ret.data.reg.name = (uint8_t)pline.data[1]; + } else { + ret.data.reg.name = -1; } - - // Single quoted string. - case '\'': { - ret.type = kExprLexSingleQuotedString; - ret.data.str.closed = false; - for (; ret.len < pline.size && !ret.data.str.closed; ret.len++) { - if (pline.data[ret.len] == '\'') { - if (ret.len + 1 < pline.size && pline.data[ret.len + 1] == '\'') { - ret.len++; - } else { - ret.data.str.closed = true; - } + break; + + // Single quoted string. + case '\'': + ret.type = kExprLexSingleQuotedString; + ret.data.str.closed = false; + for (; ret.len < pline.size && !ret.data.str.closed; ret.len++) { + if (pline.data[ret.len] == '\'') { + if (ret.len + 1 < pline.size && pline.data[ret.len + 1] == '\'') { + ret.len++; + } else { + ret.data.str.closed = true; } } - break; } - - // Double quoted string. - case '"': { - ret.type = kExprLexDoubleQuotedString; - ret.data.str.closed = false; - for (; ret.len < pline.size && !ret.data.str.closed; ret.len++) { - if (pline.data[ret.len] == '\\') { - if (ret.len + 1 < pline.size) { - ret.len++; - } - } else if (pline.data[ret.len] == '"') { - ret.data.str.closed = true; + break; + + // Double quoted string. + case '"': + ret.type = kExprLexDoubleQuotedString; + ret.data.str.closed = false; + for (; ret.len < pline.size && !ret.data.str.closed; ret.len++) { + if (pline.data[ret.len] == '\\') { + if (ret.len + 1 < pline.size) { + ret.len++; } + } else if (pline.data[ret.len] == '"') { + ret.data.str.closed = true; } - break; } - - // Unary not, (un)equality and regex (not) match comparison operators. - case '!': - case '=': { - if (pline.size == 1) { - ret.type = (schar == '!' ? kExprLexNot : kExprLexAssignment); - ret.data.ass.type = kExprAsgnPlain; - break; - } - ret.type = kExprLexComparison; - ret.data.cmp.inv = (schar == '!'); - if (pline.data[1] == '=') { - ret.data.cmp.type = kExprCmpEqual; - ret.len++; - } else if (pline.data[1] == '~') { - ret.data.cmp.type = kExprCmpMatches; - ret.len++; - } else if (schar == '!') { - ret.type = kExprLexNot; - } else { - ret.type = kExprLexAssignment; - ret.data.ass.type = kExprAsgnPlain; - } - GET_CCS(ret, pline); + break; + + // Unary not, (un)equality and regex (not) match comparison operators. + case '!': + case '=': + if (pline.size == 1) { + ret.type = (schar == '!' ? kExprLexNot : kExprLexAssignment); + ret.data.ass.type = kExprAsgnPlain; break; } - - // Less/greater [or equal to] comparison operators. - case '>': - case '<': { - ret.type = kExprLexComparison; - const bool haseqsign = (pline.size > 1 && pline.data[1] == '='); - if (haseqsign) { - ret.len++; - } - GET_CCS(ret, pline); - ret.data.cmp.inv = (schar == '<'); - ret.data.cmp.type = ((ret.data.cmp.inv ^ haseqsign) + ret.type = kExprLexComparison; + ret.data.cmp.inv = (schar == '!'); + if (pline.data[1] == '=') { + ret.data.cmp.type = kExprCmpEqual; + ret.len++; + } else if (pline.data[1] == '~') { + ret.data.cmp.type = kExprCmpMatches; + ret.len++; + } else if (schar == '!') { + ret.type = kExprLexNot; + } else { + ret.type = kExprLexAssignment; + ret.data.ass.type = kExprAsgnPlain; + } + GET_CCS(ret, pline); + break; + + // Less/greater [or equal to] comparison operators. + case '>': + case '<': { + ret.type = kExprLexComparison; + const bool haseqsign = (pline.size > 1 && pline.data[1] == '='); + if (haseqsign) { + ret.len++; + } + GET_CCS(ret, pline); + ret.data.cmp.inv = (schar == '<'); + ret.data.cmp.type = ((ret.data.cmp.inv ^ haseqsign) ? kExprCmpGreaterOrEqual : kExprCmpGreater); - break; - } + break; + } - // Minus sign, arrow from lambdas or augmented assignment. - case '-': { - if (pline.size > 1 && pline.data[1] == '>') { - ret.len++; - ret.type = kExprLexArrow; - } else if (pline.size > 1 && pline.data[1] == '=') { - ret.len++; - ret.type = kExprLexAssignment; - ret.data.ass.type = kExprAsgnSubtract; - } else { - ret.type = kExprLexMinus; - } - break; + // Minus sign, arrow from lambdas or augmented assignment. + case '-': { + if (pline.size > 1 && pline.data[1] == '>') { + ret.len++; + ret.type = kExprLexArrow; + } else if (pline.size > 1 && pline.data[1] == '=') { + ret.len++; + ret.type = kExprLexAssignment; + ret.data.ass.type = kExprAsgnSubtract; + } else { + ret.type = kExprLexMinus; } + break; + } // Sign or augmented assignment. #define CHAR_OR_ASSIGN(ch, ch_type, ass_type) \ - case ch: { \ - if (pline.size > 1 && pline.data[1] == '=') { \ - ret.len++; \ - ret.type = kExprLexAssignment; \ - ret.data.ass.type = ass_type; \ - } else { \ - ret.type = ch_type; \ - } \ - break; \ - } +case ch: { \ + if (pline.size > 1 && pline.data[1] == '=') { \ + ret.len++; \ + ret.type = kExprLexAssignment; \ + ret.data.ass.type = ass_type; \ + } else { \ + ret.type = ch_type; \ + } \ + break; \ +} CHAR_OR_ASSIGN('+', kExprLexPlus, kExprAsgnAdd) CHAR_OR_ASSIGN('.', kExprLexDot, kExprAsgnConcat) #undef CHAR_OR_ASSIGN - // Expression end because Ex command ended. - case NUL: - case NL: { - 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; + // Expression end because Ex command ended. + case NUL: + case NL: + if (flags & kELFlagForbidEOC) { + ret.type = kExprLexInvalid; + ret.data.err.msg = _("E15: Unexpected EOC character: %.*s"); + ret.data.err.type = kExprLexSpacing; + } else { + ret.type = kExprLexEOC; } - - // Everything else is not valid. - default: { - ret.len = (size_t)utfc_ptr2len_len((const char_u *)pline.data, - (int)pline.size); + 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.type = kExprLexPlainIdentifier; - ret.data.err.msg = _("E15: Unidentified character: %.*s"); - break; + ret.data.err.msg = _("E15: Unexpected EOC character: %.*s"); + ret.data.err.type = kExprLexOr; + } else { + ret.type = kExprLexEOC; } + break; + + // Everything else is not valid. + default: + ret.len = (size_t)utfc_ptr2len_len((const char_u *)pline.data, + (int)pline.size); + ret.type = kExprLexInvalid; + ret.data.err.type = kExprLexPlainIdentifier; + ret.data.err.msg = _("E15: Unidentified character: %.*s"); + break; } #undef GET_CCS viml_pexpr_next_token_adv_return: @@ -737,8 +793,7 @@ static const char *const eltkn_opt_scope_tab[] = { /// /// @return Token represented in a string form, in a static buffer (overwritten /// on each call). -const char *viml_pexpr_repr_token(const ParserState *const pstate, - const LexExprToken token, +const char *viml_pexpr_repr_token(const ParserState *const pstate, const LexExprToken token, size_t *const ret_size) FUNC_ATTR_WARN_UNUSED_RESULT { @@ -756,10 +811,10 @@ const char *viml_pexpr_repr_token(const ParserState *const pstate, eltkn_type_tab[token.type]); switch (token.type) { #define TKNARGS(tkn_type, ...) \ - case tkn_type: { \ - ADDSTR(__VA_ARGS__); \ - break; \ - } +case tkn_type: { \ + ADDSTR(__VA_ARGS__); \ + break; \ +} TKNARGS(kExprLexComparison, "(type=%s,ccs=%s,inv=%i)", eltkn_cmp_type_tab[token.data.cmp.type], ccs_tab[token.data.cmp.ccs], @@ -769,7 +824,7 @@ const char *viml_pexpr_repr_token(const ParserState *const pstate, TKNARGS(kExprLexAssignment, "(type=%s)", expr_asgn_type_tab[token.data.ass.type]) TKNARGS(kExprLexRegister, "(name=%s)", intchar2str(token.data.reg.name)) - case kExprLexDoubleQuotedString: + case kExprLexDoubleQuotedString: TKNARGS(kExprLexSingleQuotedString, "(closed=%i)", (int)token.data.str.closed) TKNARGS(kExprLexOption, "(scope=%s,name=%.*s)", @@ -785,19 +840,17 @@ const char *viml_pexpr_repr_token(const ParserState *const pstate, ? (double)token.data.num.val.floating : (double)token.data.num.val.integer)) TKNARGS(kExprLexInvalid, "(msg=%s)", token.data.err.msg) - default: { - // No additional arguments. - break; - } + default: + // No additional arguments. + break; #undef TKNARGS } if (pstate == NULL) { ADDSTR("::%zu", token.len); } else { *p++ = ':'; - memmove( - p, &pstate->reader.lines.items[token.start.line].data[token.start.col], - token.len); + memmove(p, &pstate->reader.lines.items[token.start.line].data[token.start.col], + token.len); p += token.len; *p = NUL; } @@ -886,9 +939,8 @@ static const char *intchar2str(const int ch) #include <stdio.h> REAL_FATTR_UNUSED -static inline void viml_pexpr_debug_print_ast_node( - const ExprASTNode *const *const eastnode_p, - const char *const prefix) +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); @@ -901,35 +953,33 @@ static inline void viml_pexpr_debug_print_ast_node( } REAL_FATTR_UNUSED -static inline void viml_pexpr_debug_print_ast_stack( - const ExprASTStack *const ast_stack, - const char *const msg) +static inline void viml_pexpr_debug_print_ast_stack(const ExprASTStack *const ast_stack, + const char *const msg) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_ALWAYS_INLINE { fprintf(stderr, "\n%sstack: %zu:\n", msg, kv_size(*ast_stack)); for (size_t i = 0; i < kv_size(*ast_stack); i++) { - viml_pexpr_debug_print_ast_node( - (const ExprASTNode *const *)kv_A(*ast_stack, i), - "-"); + viml_pexpr_debug_print_ast_node((const ExprASTNode *const *)kv_A(*ast_stack, i), + "-"); } } REAL_FATTR_UNUSED -static inline void viml_pexpr_debug_print_token( - const ParserState *const pstate, const LexExprToken token) +static inline void viml_pexpr_debug_print_token(const ParserState *const pstate, + const LexExprToken token) FUNC_ATTR_ALWAYS_INLINE { fprintf(stderr, "\ntkn: %s\n", viml_pexpr_repr_token(pstate, token, NULL)); } #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) \ - 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 *)eastnode_p, \ - (#msg)) + viml_pexpr_debug_print_ast_node((const ExprASTNode *const *)eastnode_p, \ + (#msg)) #define PTOKEN(tkn) \ - viml_pexpr_debug_print_token(pstate, tkn) + viml_pexpr_debug_print_token(pstate, tkn) #endif const uint8_t node_maxchildren[] = { @@ -1009,50 +1059,48 @@ void viml_pexpr_free_ast(ExprAST ast) } else if (*cur_node != NULL) { kv_drop(ast_stack, 1); switch ((*cur_node)->type) { - case kExprNodeDoubleQuotedString: - case kExprNodeSingleQuotedString: { - xfree((*cur_node)->data.str.value); - break; - } - case kExprNodeMissing: - case kExprNodeOpMissing: - case kExprNodeTernary: - case kExprNodeTernaryValue: - case kExprNodeRegister: - case kExprNodeSubscript: - case kExprNodeListLiteral: - case kExprNodeUnaryPlus: - case kExprNodeBinaryPlus: - case kExprNodeNested: - case kExprNodeCall: - case kExprNodePlainIdentifier: - case kExprNodePlainKey: - case kExprNodeComplexIdentifier: - case kExprNodeUnknownFigure: - case kExprNodeLambda: - case kExprNodeDictLiteral: - case kExprNodeCurlyBracesIdentifier: - case kExprNodeAssignment: - case kExprNodeComma: - case kExprNodeColon: - case kExprNodeArrow: - case kExprNodeComparison: - case kExprNodeConcat: - case kExprNodeConcatOrSubscript: - case kExprNodeInteger: - case kExprNodeFloat: - case kExprNodeOr: - case kExprNodeAnd: - case kExprNodeUnaryMinus: - case kExprNodeBinaryMinus: - case kExprNodeNot: - case kExprNodeMultiplication: - case kExprNodeDivision: - case kExprNodeMod: - case kExprNodeOption: - case kExprNodeEnvironment: { - break; - } + case kExprNodeDoubleQuotedString: + case kExprNodeSingleQuotedString: + xfree((*cur_node)->data.str.value); + break; + case kExprNodeMissing: + case kExprNodeOpMissing: + case kExprNodeTernary: + case kExprNodeTernaryValue: + case kExprNodeRegister: + case kExprNodeSubscript: + case kExprNodeListLiteral: + case kExprNodeUnaryPlus: + case kExprNodeBinaryPlus: + case kExprNodeNested: + case kExprNodeCall: + case kExprNodePlainIdentifier: + case kExprNodePlainKey: + case kExprNodeComplexIdentifier: + case kExprNodeUnknownFigure: + case kExprNodeLambda: + case kExprNodeDictLiteral: + case kExprNodeCurlyBracesIdentifier: + case kExprNodeAssignment: + case kExprNodeComma: + case kExprNodeColon: + case kExprNodeArrow: + case kExprNodeComparison: + case kExprNodeConcat: + case kExprNodeConcatOrSubscript: + case kExprNodeInteger: + case kExprNodeFloat: + case kExprNodeOr: + case kExprNodeAnd: + case kExprNodeUnaryMinus: + case kExprNodeBinaryMinus: + case kExprNodeNot: + case kExprNodeMultiplication: + case kExprNodeDivision: + case kExprNodeMod: + case kExprNodeOption: + case kExprNodeEnvironment: + break; } xfree(*cur_node); *cur_node = NULL; @@ -1204,10 +1252,8 @@ static inline ExprOpAssociativity node_ass(const ExprASTNode 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, - ExprASTWantedNode *const want_node_p, +static bool viml_pexpr_handle_bop(const ParserState *const pstate, ExprASTStack *const ast_stack, + ExprASTNode *const bop_node, ExprASTWantedNode *const want_node_p, ExprASTError *const ast_err) FUNC_ATTR_NONNULL_ALL { @@ -1223,8 +1269,8 @@ static bool viml_pexpr_handle_bop(const ParserState *const pstate, : node_lvl(*bop_node)); #ifndef NDEBUG const ExprOpAssociativity bop_node_ass = ( - (bop_node->type == kExprNodeCall - || bop_node->type == kExprNodeSubscript) + (bop_node->type == kExprNodeCall + || bop_node->type == kExprNodeSubscript) ? kEOpAssLeft : node_ass(*bop_node)); #endif @@ -1301,8 +1347,7 @@ static bool viml_pexpr_handle_bop(const ParserState *const pstate, /// @param[in] shift Number of bytes to shift. /// /// @return Shifted position. -static inline ParserPosition shifted_pos(const ParserPosition pos, - const size_t shift) +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 }; @@ -1316,8 +1361,7 @@ static inline ParserPosition shifted_pos(const ParserPosition pos, /// @param[in] new_col New column. /// /// @return Shifted position. -static inline ParserPosition recol_pos(const ParserPosition pos, - const size_t new_col) +static inline ParserPosition recol_pos(const ParserPosition pos, const size_t new_col) FUNC_ATTR_CONST FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_WARN_UNUSED_RESULT { return (ParserPosition) { .line = pos.line, .col = new_col }; @@ -1328,22 +1372,22 @@ static inline ParserPosition recol_pos(const ParserPosition pos, /// Highlight current token with the given group #define HL_CUR_TOKEN(g) \ - viml_parser_highlight(pstate, cur_token.start, cur_token.len, \ - HL(g)) + viml_parser_highlight(pstate, cur_token.start, cur_token.len, \ + HL(g)) /// Allocate new node, saving some values #define NEW_NODE(type) \ - viml_pexpr_new_node(type) + viml_pexpr_new_node(type) /// Set position of the given node to position from the given token /// /// @param cur_node Node to modify. /// @param cur_token Token to set position from. #define POS_FROM_TOKEN(cur_node, cur_token) \ - do { \ - (cur_node)->start = cur_token.start; \ - (cur_node)->len = cur_token.len; \ - } while (0) + do { \ + (cur_node)->start = cur_token.start; \ + (cur_node)->len = cur_token.len; \ + } while (0) /// Allocate new node and set its position from the current token /// @@ -1352,27 +1396,27 @@ static inline ParserPosition recol_pos(const ParserPosition pos, /// @param cur_node Variable to save allocated node to. /// @param typ Node type. #define NEW_NODE_WITH_CUR_POS(cur_node, typ) \ - do { \ - (cur_node) = NEW_NODE(typ); \ - POS_FROM_TOKEN((cur_node), cur_token); \ - if (prev_token.type == kExprLexSpacing) { \ - (cur_node)->start = prev_token.start; \ - (cur_node)->len += prev_token.len; \ - } \ - } while (0) + do { \ + (cur_node) = NEW_NODE(typ); \ + POS_FROM_TOKEN((cur_node), cur_token); \ + if (prev_token.type == kExprLexSpacing) { \ + (cur_node)->start = prev_token.start; \ + (cur_node)->len += prev_token.len; \ + } \ + } while (0) /// Check whether it is possible to have next expression after current /// /// For :echo: `:echo @a @a` is a valid expression. `:echo (@a @a)` is not. #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) + is_invalid |= !viml_pexpr_handle_bop(pstate, &ast_stack, cur_node, \ + &want_node, &ast.err) /// Record missing operator: for things like /// @@ -1384,33 +1428,33 @@ static inline ParserPosition recol_pos(const ParserPosition pos, /// /// (parsed as OpMissing(@a, @a)). #define OP_MISSING \ - do { \ - if (flags & kExprFlagsMulti && MAY_HAVE_NEXT_EXPR) { \ - /* Multiple expressions allowed, return without calling */ \ - /* viml_parser_advance(). */ \ - goto viml_pexpr_parse_end; \ - } else { \ - assert(*top_node_p != NULL); \ - ERROR_FROM_TOKEN_AND_MSG(cur_token, _("E15: Missing operator: %.*s")); \ - NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeOpMissing); \ - cur_node->len = 0; \ - ADD_OP_NODE(cur_node); \ - goto viml_pexpr_parse_process_token; \ - } \ - } while (0) + do { \ + if (flags & kExprFlagsMulti && MAY_HAVE_NEXT_EXPR) { \ + /* Multiple expressions allowed, return without calling */ \ + /* viml_parser_advance(). */ \ + goto viml_pexpr_parse_end; \ + } else { \ + assert(*top_node_p != NULL); \ + ERROR_FROM_TOKEN_AND_MSG(cur_token, _("E15: Missing operator: %.*s")); \ + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeOpMissing); \ + cur_node->len = 0; \ + ADD_OP_NODE(cur_node); \ + goto viml_pexpr_parse_process_token; \ + } \ + } 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((*top_node_p), kExprNodeMissing); \ - (*top_node_p)->len = 0; \ - want_node = kENodeOperator; \ - } \ - } while (0) + do { \ + if (want_node == kENodeValue) { \ + ERROR_FROM_TOKEN_AND_MSG(cur_token, (msg)); \ + NEW_NODE_WITH_CUR_POS((*top_node_p), kExprNodeMissing); \ + (*top_node_p)->len = 0; \ + want_node = kENodeOperator; \ + } \ + } while (0) /// Set AST error, unless AST already is not correct /// @@ -1419,10 +1463,8 @@ static inline ParserPosition recol_pos(const ParserPosition pos, /// @param[in] msg Error message, assumed to be already translated and /// containing a single %token "%.*s". /// @param[in] start Position at which error occurred. -static inline void east_set_error(const ParserState *const pstate, - ExprASTError *const ret_ast_err, - const char *const msg, - const ParserPosition start) +static inline void east_set_error(const ParserState *const pstate, ExprASTError *const ret_ast_err, + const char *const msg, const ParserPosition start) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_ALWAYS_INLINE { if (ret_ast_err->msg != NULL) { @@ -1436,21 +1478,21 @@ static inline void east_set_error(const ParserState *const pstate, /// Set error from the given token and given message #define ERROR_FROM_TOKEN_AND_MSG(cur_token, msg) \ - do { \ - is_invalid = true; \ - east_set_error(pstate, &ast.err, msg, cur_token.start); \ - } while (0) + do { \ + is_invalid = true; \ + east_set_error(pstate, &ast.err, 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(pstate, &ast.err, msg, node->start); \ - } while (0) + do { \ + is_invalid = true; \ + east_set_error(pstate, &ast.err, msg, node->start); \ + } while (0) /// Set error from the given kExprLexInvalid 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 /// @@ -1459,16 +1501,16 @@ static inline void east_set_error(const ParserState *const pstate, /// 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) + 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) /// Add identifier which should constitute complex identifier node /// @@ -1479,45 +1521,45 @@ static inline void east_set_error(const ParserState *const pstate, /// a trailing semicolon. /// @param hl Highlighting name to use, passed as an argument to #HL. #define ADD_IDENT(new_ident_node_code, hl) \ - do { \ - assert(want_node == kENodeOperator); \ - /* Operator: may only be curly braces name, but only under certain */ \ - /* conditions. */ \ + do { \ + assert(want_node == kENodeOperator); \ + /* Operator: may only be curly braces name, but only under certain */ \ + /* conditions. */ \ \ - /* First condition is that there is no space before a part of complex */ \ - /* identifier. */ \ - 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. */ \ + /* First condition is that there is no space before a part of complex */ \ + /* identifier. */ \ + 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; \ - cur_node->children = *top_node_p; \ - *top_node_p = cur_node; \ - kvi_push(ast_stack, &cur_node->children->next); \ - ExprASTNode **const new_top_node_p = kv_last(ast_stack); \ - assert(*new_top_node_p == NULL); \ - new_ident_node_code; \ - *new_top_node_p = cur_node; \ - HL_CUR_TOKEN(hl); \ - break; \ - } \ - default: { \ - OP_MISSING; \ - break; \ - } \ - } \ - } while (0) + /* 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; \ + cur_node->children = *top_node_p; \ + *top_node_p = cur_node; \ + kvi_push(ast_stack, &cur_node->children->next); \ + ExprASTNode **const new_top_node_p = kv_last(ast_stack); \ + assert(*new_top_node_p == NULL); \ + new_ident_node_code; \ + *new_top_node_p = cur_node; \ + HL_CUR_TOKEN(hl); \ + break; \ + } \ + default: { \ + OP_MISSING; \ + break; \ + } \ + } \ + } while (0) /// Determine whether given parse type is an assignment /// @@ -1551,10 +1593,8 @@ typedef struct { /// @param[in] ast_stack Parser AST stack, used to detect whether current /// string is a regex. /// @param[in] is_invalid Whether currently processed token is not valid. -static void parse_quoted_string(ParserState *const pstate, - ExprASTNode *const node, - const LexExprToken token, - const ExprASTStack ast_stack, +static void parse_quoted_string(ParserState *const pstate, ExprASTNode *const node, + const LexExprToken token, const ExprASTStack ast_stack, const bool is_invalid) FUNC_ATTR_NONNULL_ALL { @@ -1577,10 +1617,10 @@ static void parse_quoted_string(ParserState *const pstate, p = chunk_e + 2; if (pstate->colors) { kvi_push(shifts, ((StringShift) { - .start = token.start.col + (size_t)(chunk_e - s), - .orig_len = 2, - .act_len = 1, - .escape_not_known = false, + .start = token.start.col + (size_t)(chunk_e - s), + .orig_len = 2, + .act_len = 1, + .escape_not_known = false, })); } } @@ -1613,70 +1653,74 @@ static void parse_quoted_string(ParserState *const pstate, break; } switch (*p) { - // A "\<x>" form occupies at least 4 characters, and produces up to - // 6 characters: reserve space for 2 extra, but do not compute actual - // length just now, it would be costy. - case '<': { - size += 2; - break; - } - // Hexadecimal, always single byte, but at least three bytes each. - case 'x': case 'X': { + // A "\<x>" form occupies at least 4 characters, and produces up to + // 6 characters: reserve space for 2 extra, but do not compute actual + // length just now, it would be costy. + case '<': + size += 2; + break; + // Hexadecimal, always single byte, but at least three bytes each. + case 'x': + case 'X': + size--; + if (ascii_isxdigit(p[1])) { size--; - if (ascii_isxdigit(p[1])) { + if (p + 2 < e && ascii_isxdigit(p[2])) { size--; - if (p + 2 < e && ascii_isxdigit(p[2])) { - size--; - } } - break; } - // Unicode - // - // \uF takes 1 byte which is 2 bytes less then escape sequence. - // \uFF: 2 bytes, 2 bytes less. - // \uFFF: 3 bytes, 2 bytes less. - // \uFFFF: 3 bytes, 3 bytes less. - // \UFFFFF: 4 bytes, 3 bytes less. - // \UFFFFFF: 5 bytes, 3 bytes less. - // \UFFFFFFF: 6 bytes, 3 bytes less. - // \U7FFFFFFF: 6 bytes, 4 bytes less. - case 'u': case 'U': { - const char *const esc_start = p; - size_t n = (*p == 'u' ? 4 : 8); - int nr = 0; + break; + // Unicode + // + // \uF takes 1 byte which is 2 bytes less then escape sequence. + // \uFF: 2 bytes, 2 bytes less. + // \uFFF: 3 bytes, 2 bytes less. + // \uFFFF: 3 bytes, 3 bytes less. + // \UFFFFF: 4 bytes, 3 bytes less. + // \UFFFFFF: 5 bytes, 3 bytes less. + // \UFFFFFFF: 6 bytes, 3 bytes less. + // \U7FFFFFFF: 6 bytes, 4 bytes less. + case 'u': + case 'U': { + const char *const esc_start = p; + size_t n = (*p == 'u' ? 4 : 8); + int nr = 0; + p++; + while (p + 1 < e && n-- && ascii_isxdigit(p[1])) { p++; - while (p + 1 < e && n-- && ascii_isxdigit(p[1])) { - p++; - nr = (nr << 4) + hex2nr(*p); - } - // Escape length: (esc_start - 1) points to "\\", esc_start to "u" - // or "U", p to the byte after last byte. So escape sequence - // occupies p - (esc_start - 1), but it stands for a utf_char2len - // bytes. - size -= (size_t)((p - (esc_start - 1)) - utf_char2len(nr)); - p--; - break; + nr = (nr << 4) + hex2nr(*p); } - // Octal, always single byte, but at least two bytes each. - case '0': case '1': case '2': case '3': case '4': case '5': case '6': - case '7': { + // Escape length: (esc_start - 1) points to "\\", esc_start to "u" + // or "U", p to the byte after last byte. So escape sequence + // occupies p - (esc_start - 1), but it stands for a utf_char2len + // bytes. + size -= (size_t)((p - (esc_start - 1)) - utf_char2len(nr)); + p--; + break; + } + // Octal, always single byte, but at least two bytes each. + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + size--; + p++; + if (*p >= '0' && *p <= '7') { size--; p++; - if (*p >= '0' && *p <= '7') { + if (p < e && *p >= '0' && *p <= '7') { size--; p++; - if (p < e && *p >= '0' && *p <= '7') { - size--; - p++; - } } - break; - } - default: { - size--; - break; } + break; + default: + size--; + break; } } } @@ -1705,11 +1749,11 @@ static void parse_quoted_string(ParserState *const pstate, const char *const v_p_start = v_p; switch (*p) { #define SINGLE_CHAR_ESC(ch, real_ch) \ - case ch: { \ - *v_p++ = real_ch; \ - p++; \ - break; \ - } +case ch: { \ + *v_p++ = real_ch; \ + p++; \ + break; \ +} SINGLE_CHAR_ESC('b', BS) SINGLE_CHAR_ESC('e', ESC) SINGLE_CHAR_ESC('f', FF) @@ -1720,76 +1764,83 @@ static void parse_quoted_string(ParserState *const pstate, SINGLE_CHAR_ESC('\\', '\\') #undef SINGLE_CHAR_ESC - // Hexadecimal or unicode. - case 'X': case 'x': case 'u': case 'U': { - if (p + 1 < e && ascii_isxdigit(p[1])) { - size_t n; - int nr; - bool is_hex = (*p == 'x' || *p == 'X'); - - if (is_hex) { - n = 2; - } else if (*p == 'u') { - n = 4; - } else { - n = 8; - } - nr = 0; - while (p + 1 < e && n-- && ascii_isxdigit(p[1])) { - p++; - nr = (nr << 4) + hex2nr(*p); - } - p++; - if (is_hex) { - *v_p++ = (char)nr; - } else { - v_p += utf_char2bytes(nr, (char_u *)v_p); - } + // Hexadecimal or unicode. + case 'X': + case 'x': + case 'u': + case 'U': + if (p + 1 < e && ascii_isxdigit(p[1])) { + size_t n; + int nr; + bool is_hex = (*p == 'x' || *p == 'X'); + + if (is_hex) { + n = 2; + } else if (*p == 'u') { + n = 4; } else { - is_unknown = true; - *v_p++ = *p; + n = 8; + } + nr = 0; + while (p + 1 < e && n-- && ascii_isxdigit(p[1])) { p++; + nr = (nr << 4) + hex2nr(*p); + } + p++; + if (is_hex) { + *v_p++ = (char)nr; + } else { + v_p += utf_char2bytes(nr, (char_u *)v_p); } - break; + } else { + is_unknown = true; + *v_p++ = *p; + p++; } - // Octal: "\1", "\12", "\123". - case '0': case '1': case '2': case '3': case '4': case '5': case '6': - case '7': { - uint8_t ch = (uint8_t)(*p++ - '0'); + break; + // Octal: "\1", "\12", "\123". + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': { + uint8_t ch = (uint8_t)(*p++ - '0'); + if (p < e && *p >= '0' && *p <= '7') { + ch = (uint8_t)((ch << 3) + *p++ - '0'); if (p < e && *p >= '0' && *p <= '7') { ch = (uint8_t)((ch << 3) + *p++ - '0'); - if (p < e && *p >= '0' && *p <= '7') { - ch = (uint8_t)((ch << 3) + *p++ - '0'); - } } - *v_p++ = (char)ch; - break; } - // Special key, e.g.: "\<C-W>" - case '<': { - const size_t special_len = ( - trans_special((const char_u **)&p, (size_t)(e - p), - (char_u *)v_p, true, true)); - if (special_len != 0) { - v_p += special_len; - } else { - is_unknown = true; - mb_copy_char((const char_u **)&p, (char_u **)&v_p); - } - break; - } - default: { + *v_p++ = (char)ch; + break; + } + // Special key, e.g.: "\<C-W>" + case '<': { + const size_t special_len = ( + trans_special((const char_u **)&p, (size_t)(e - p), + (char_u *)v_p, true, true)); + if (special_len != 0) { + v_p += special_len; + } else { is_unknown = true; mb_copy_char((const char_u **)&p, (char_u **)&v_p); - break; } + break; + } + default: + is_unknown = true; + mb_copy_char((const char_u **)&p, (char_u **)&v_p); + break; } if (pstate->colors) { kvi_push(shifts, ((StringShift) { - .start = token.start.col + (size_t)(chunk_e - s), - .orig_len = (size_t)(p - chunk_e), - .act_len = (size_t)(v_p - (char *)v_p_start), - .escape_not_known = is_unknown, + .start = token.start.col + (size_t)(chunk_e - s), + .orig_len = (size_t)(p - chunk_e), + .act_len = (size_t)(v_p - (char *)v_p_start), + .escape_not_known = is_unknown, })); } } @@ -1901,21 +1952,23 @@ ExprAST viml_pexpr_parse(ParserState *const pstate, const int flags) size_t asgn_level = 0; do { const bool is_concat_or_subscript = ( - want_node == kENodeValue - && kv_size(ast_stack) > 1 - && (*kv_Z(ast_stack, 1))->type == kExprNodeConcatOrSubscript); + want_node == kENodeValue + && kv_size(ast_stack) > 1 + && (*kv_Z(ast_stack, + 1))->type == kExprNodeConcatOrSubscript); const int lexer_additional_flags = ( - kELFlagPeek - | ((flags & kExprFlagsDisallowEOC) ? kELFlagForbidEOC : 0) - | ((want_node == kENodeValue - && (kv_size(ast_stack) == 1 - || ((*kv_Z(ast_stack, 1))->type != kExprNodeConcat - && ((*kv_Z(ast_stack, 1))->type - != kExprNodeConcatOrSubscript)))) + kELFlagPeek + | ((flags & kExprFlagsDisallowEOC) ? kELFlagForbidEOC : 0) + | ((want_node == kENodeValue + && (kv_size(ast_stack) == 1 + || ((*kv_Z(ast_stack, 1))->type != kExprNodeConcat + && ((*kv_Z(ast_stack, 1))->type + != kExprNodeConcatOrSubscript)))) ? kELFlagAllowFloat : 0)); - LexExprToken cur_token = viml_pexpr_next_token( - pstate, want_node_to_lexer_flags[want_node] | lexer_additional_flags); + LexExprToken cur_token = viml_pexpr_next_token(pstate, + want_node_to_lexer_flags[want_node] | + lexer_additional_flags); if (cur_token.type == kExprLexEOC) { break; } @@ -1924,8 +1977,8 @@ ExprAST viml_pexpr_parse(ParserState *const pstate, const int flags) 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); + 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); @@ -1977,12 +2030,12 @@ viml_pexpr_parse_process_token: // for ternary and because concatenating dictionary with anything is not // valid. There are more cases when this will make a difference though. const bool node_is_key = ( - is_concat_or_subscript - && (cur_token.type == kExprLexPlainIdentifier + is_concat_or_subscript + && (cur_token.type == kExprLexPlainIdentifier ? (!cur_token.data.var.autoload && cur_token.data.var.scope == kExprVarScopeMissing) : (cur_token.type == kExprLexNumber)) - && prev_token.type != kExprLexSpacing); + && prev_token.type != kExprLexSpacing); if (is_concat_or_subscript && !node_is_key) { // Note: in Vim "d. a" (this is the reason behind `prev_token.type != // kExprLexSpacing` part of the condition) as well as any other "d.{expr}" @@ -1997,951 +2050,896 @@ viml_pexpr_parse_process_token: // Pop some stack pt_stack items in case of misplaced nodes. const bool is_single_assignment = kv_last(pt_stack) == kEPTSingleAssignment; switch (kv_last(pt_stack)) { - case kEPTExpr: { - break; - } - case kEPTLambdaArguments: { - if ((want_node == kENodeOperator - && tok_type != kExprLexComma - && tok_type != kExprLexArrow) - || (want_node == kENodeValue - && !(cur_token.type == kExprLexPlainIdentifier - && cur_token.data.var.scope == kExprVarScopeMissing - && !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; - kv_drop(pt_stack, 1); - } + case kEPTExpr: + break; + case kEPTLambdaArguments: + if ((want_node == kENodeOperator + && tok_type != kExprLexComma + && tok_type != kExprLexArrow) + || (want_node == kENodeValue + && !(cur_token.type == kExprLexPlainIdentifier + && cur_token.data.var.scope == kExprVarScopeMissing + && !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; + kv_drop(pt_stack, 1); } - break; } - case kEPTSingleAssignment: - case kEPTAssignment: { - if (want_node == kENodeValue - && tok_type != kExprLexBracket - && tok_type != kExprLexPlainIdentifier - && (tok_type != kExprLexFigureBrace || cur_token.data.brc.closing) - && !(node_is_key && tok_type == kExprLexNumber) - && tok_type != kExprLexEnv - && tok_type != kExprLexOption - && tok_type != kExprLexRegister) { - ERROR_FROM_TOKEN_AND_MSG( - cur_token, - _("E15: Expected value part of assignment lvalue: %.*s")); - kv_drop(pt_stack, 1); - } else if (want_node == kENodeOperator - && tok_type != kExprLexBracket - && (tok_type != kExprLexFigureBrace - || cur_token.data.brc.closing) - && tok_type != kExprLexDot - && (tok_type != kExprLexComma || !is_single_assignment) - && tok_type != kExprLexAssignment - // Curly brace identifiers: will contain plain identifier or - // another curly brace in position where operator is wanted. - && !((tok_type == kExprLexPlainIdentifier - || (tok_type == kExprLexFigureBrace - && !cur_token.data.brc.closing)) - && prev_token.type != kExprLexSpacing)) { - if (flags & kExprFlagsMulti && MAY_HAVE_NEXT_EXPR) { - goto viml_pexpr_parse_end; - } - ERROR_FROM_TOKEN_AND_MSG( - cur_token, - _("E15: Expected assignment operator or subscript: %.*s")); - kv_drop(pt_stack, 1); + break; + case kEPTSingleAssignment: + case kEPTAssignment: + if (want_node == kENodeValue + && tok_type != kExprLexBracket + && tok_type != kExprLexPlainIdentifier + && (tok_type != kExprLexFigureBrace || cur_token.data.brc.closing) + && !(node_is_key && tok_type == kExprLexNumber) + && tok_type != kExprLexEnv + && tok_type != kExprLexOption + && tok_type != kExprLexRegister) { + ERROR_FROM_TOKEN_AND_MSG(cur_token, + _("E15: Expected value part of assignment lvalue: %.*s")); + kv_drop(pt_stack, 1); + } else if (want_node == kENodeOperator + && tok_type != kExprLexBracket + && (tok_type != kExprLexFigureBrace + || cur_token.data.brc.closing) + && tok_type != kExprLexDot + && (tok_type != kExprLexComma || !is_single_assignment) + && tok_type != kExprLexAssignment + // Curly brace identifiers: will contain plain identifier or + // another curly brace in position where operator is wanted. + && !((tok_type == kExprLexPlainIdentifier + || (tok_type == kExprLexFigureBrace + && !cur_token.data.brc.closing)) + && prev_token.type != kExprLexSpacing)) { + if (flags & kExprFlagsMulti && MAY_HAVE_NEXT_EXPR) { + goto viml_pexpr_parse_end; } - assert(kv_size(pt_stack)); - break; + ERROR_FROM_TOKEN_AND_MSG(cur_token, + _("E15: Expected assignment operator or subscript: %.*s")); + kv_drop(pt_stack, 1); } + assert(kv_size(pt_stack)); + break; } assert(kv_size(pt_stack)); const ExprASTParseType cur_pt = kv_last(pt_stack); assert(lambda_node == NULL || cur_pt == kEPTLambdaArguments); switch (tok_type) { - case kExprLexMissing: - case kExprLexSpacing: - case kExprLexEOC: { - abort(); - } - case kExprLexInvalid: { - ERROR_FROM_TOKEN(cur_token); - tok_type = cur_token.data.err.type; - goto viml_pexpr_parse_process_token; - } - case kExprLexRegister: { - if (want_node == kENodeOperator) { - // Register in operator position: e.g. @a @a - OP_MISSING; - } - NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeRegister); - cur_node->data.reg.name = cur_token.data.reg.name; - *top_node_p = cur_node; - want_node = kENodeOperator; - HL_CUR_TOKEN(Register); - break; + case kExprLexMissing: + case kExprLexSpacing: + case kExprLexEOC: + abort(); + case kExprLexInvalid: + ERROR_FROM_TOKEN(cur_token); + tok_type = cur_token.data.err.type; + goto viml_pexpr_parse_process_token; + case kExprLexRegister: { + if (want_node == kENodeOperator) { + // Register in operator position: e.g. @a @a + OP_MISSING; } + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeRegister); + cur_node->data.reg.name = cur_token.data.reg.name; + *top_node_p = cur_node; + want_node = kENodeOperator; + HL_CUR_TOKEN(Register); + break; + } #define SIMPLE_UB_OP(op) \ - case kExprLex##op: { \ - if (want_node == kENodeValue) { \ - /* Value level: assume unary operator. */ \ - NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeUnary##op); \ - *top_node_p = cur_node; \ - kvi_push(ast_stack, &cur_node->children); \ - HL_CUR_TOKEN(Unary##op); \ - } else { \ - NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeBinary##op); \ - ADD_OP_NODE(cur_node); \ - HL_CUR_TOKEN(Binary##op); \ - } \ - want_node = kENodeValue; \ - break; \ - } +case kExprLex##op: { \ + if (want_node == kENodeValue) { \ + /* Value level: assume unary operator. */ \ + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeUnary##op); \ + *top_node_p = cur_node; \ + kvi_push(ast_stack, &cur_node->children); \ + HL_CUR_TOKEN(Unary##op); \ + } else { \ + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeBinary##op); \ + ADD_OP_NODE(cur_node); \ + HL_CUR_TOKEN(Binary##op); \ + } \ + want_node = kENodeValue; \ + break; \ +} SIMPLE_UB_OP(Plus) SIMPLE_UB_OP(Minus) #undef SIMPLE_UB_OP #define SIMPLE_B_OP(op, msg) \ - case kExprLex##op: { \ - ADD_VALUE_IF_MISSING(_("E15: Unexpected " msg ": %.*s")); \ - NEW_NODE_WITH_CUR_POS(cur_node, kExprNode##op); \ - HL_CUR_TOKEN(op); \ - ADD_OP_NODE(cur_node); \ - break; \ - } +case kExprLex##op: { \ + ADD_VALUE_IF_MISSING(_("E15: Unexpected " msg ": %.*s")); \ + NEW_NODE_WITH_CUR_POS(cur_node, kExprNode##op); \ + HL_CUR_TOKEN(op); \ + ADD_OP_NODE(cur_node); \ + break; \ +} SIMPLE_B_OP(Or, "or operator") SIMPLE_B_OP(And, "and operator") #undef SIMPLE_B_OP - case kExprLexMultiplication: { - ADD_VALUE_IF_MISSING( - _("E15: Unexpected multiplication-like operator: %.*s")); - switch (cur_token.data.mul.type) { + case kExprLexMultiplication: + ADD_VALUE_IF_MISSING(_("E15: Unexpected multiplication-like operator: %.*s")); + switch (cur_token.data.mul.type) { #define MUL_OP(lex_op_tail, node_op_tail) \ - case kExprLexMul##lex_op_tail: { \ - NEW_NODE_WITH_CUR_POS(cur_node, kExprNode##node_op_tail); \ - HL_CUR_TOKEN(node_op_tail); \ - break; \ - } - MUL_OP(Mul, Multiplication) - MUL_OP(Div, Division) - MUL_OP(Mod, Mod) +case kExprLexMul##lex_op_tail: { \ + NEW_NODE_WITH_CUR_POS(cur_node, kExprNode##node_op_tail); \ + HL_CUR_TOKEN(node_op_tail); \ + break; \ +} + MUL_OP(Mul, Multiplication) + MUL_OP(Div, Division) + MUL_OP(Mod, Mod) #undef MUL_OP - } - ADD_OP_NODE(cur_node); - break; } - case kExprLexOption: { - if (want_node == kENodeOperator) { - OP_MISSING; - } - NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeOption); - if (cur_token.type == kExprLexInvalid) { - assert(cur_token.len == 1 - || (cur_token.len == 3 - && pline.data[cur_token.start.col + 2] == ':')); - cur_node->data.opt.ident = ( - pline.data + cur_token.start.col + cur_token.len); - cur_node->data.opt.ident_len = 0; - cur_node->data.opt.scope = ( - cur_token.len == 3 + ADD_OP_NODE(cur_node); + break; + case kExprLexOption: { + if (want_node == kENodeOperator) { + OP_MISSING; + } + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeOption); + if (cur_token.type == kExprLexInvalid) { + assert(cur_token.len == 1 + || (cur_token.len == 3 + && pline.data[cur_token.start.col + 2] == ':')); + cur_node->data.opt.ident = ( + pline.data + cur_token.start.col + cur_token.len); + cur_node->data.opt.ident_len = 0; + cur_node->data.opt.scope = ( + cur_token.len == 3 ? (ExprOptScope)pline.data[cur_token.start.col + 1] : kExprOptScopeUnspecified); - } else { - cur_node->data.opt.ident = cur_token.data.opt.name; - cur_node->data.opt.ident_len = cur_token.data.opt.len; - cur_node->data.opt.scope = cur_token.data.opt.scope; - } + } else { + cur_node->data.opt.ident = cur_token.data.opt.name; + cur_node->data.opt.ident_len = cur_token.data.opt.len; + cur_node->data.opt.scope = cur_token.data.opt.scope; + } + *top_node_p = cur_node; + want_node = kENodeOperator; + viml_parser_highlight(pstate, cur_token.start, 1, HL(OptionSigil)); + const size_t scope_shift = ( + cur_token.data.opt.scope == kExprOptScopeUnspecified ? 0 : 2); + if (scope_shift) { + viml_parser_highlight(pstate, shifted_pos(cur_token.start, 1), 1, + HL(OptionScope)); + viml_parser_highlight(pstate, shifted_pos(cur_token.start, 2), 1, + HL(OptionScopeDelimiter)); + } + viml_parser_highlight(pstate, shifted_pos(cur_token.start, scope_shift + 1), + cur_token.len - (scope_shift + 1), HL(OptionName)); + break; + } + case kExprLexEnv: + if (want_node == kENodeOperator) { + OP_MISSING; + } + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeEnvironment); + cur_node->data.env.ident = pline.data + cur_token.start.col + 1; + cur_node->data.env.ident_len = cur_token.len - 1; + if (cur_node->data.env.ident_len == 0) { + ERROR_FROM_TOKEN_AND_MSG(cur_token, + _("E15: Environment variable name missing")); + } + *top_node_p = cur_node; + want_node = kENodeOperator; + viml_parser_highlight(pstate, cur_token.start, 1, HL(EnvironmentSigil)); + viml_parser_highlight(pstate, shifted_pos(cur_token.start, 1), + cur_token.len - 1, HL(EnvironmentName)); + break; + case kExprLexNot: + if (want_node == kENodeOperator) { + OP_MISSING; + } + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeNot); + *top_node_p = cur_node; + kvi_push(ast_stack, &cur_node->children); + HL_CUR_TOKEN(Not); + 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(Comparison)); + viml_parser_highlight(pstate, shifted_pos(cur_token.start, cur_token.len - 1), 1, + HL(ComparisonModifier)); + } else { + HL_CUR_TOKEN(Comparison); + } + want_node = kENodeValue; + break; + case kExprLexComma: + assert(!(want_node == kENodeValue && cur_pt == kEPTLambdaArguments)); + 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 = kENodeOperator; - viml_parser_highlight(pstate, cur_token.start, 1, HL(OptionSigil)); - const size_t scope_shift = ( - cur_token.data.opt.scope == kExprOptScopeUnspecified ? 0 : 2); - if (scope_shift) { - viml_parser_highlight(pstate, shifted_pos(cur_token.start, 1), 1, - HL(OptionScope)); - viml_parser_highlight(pstate, shifted_pos(cur_token.start, 2), 1, - HL(OptionScopeDelimiter)); - } - viml_parser_highlight( - pstate, shifted_pos(cur_token.start, scope_shift + 1), - cur_token.len - (scope_shift + 1), HL(OptionName)); - break; } - case kExprLexEnv: { - if (want_node == kENodeOperator) { - OP_MISSING; - } - NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeEnvironment); - cur_node->data.env.ident = pline.data + cur_token.start.col + 1; - cur_node->data.env.ident_len = cur_token.len - 1; - if (cur_node->data.env.ident_len == 0) { + if (cur_pt == kEPTLambdaArguments) { + assert(lambda_node != NULL); + assert(lambda_node->data.fig.type_guesses.allow_lambda); + 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++) { + ExprASTNode *const *const eastnode_p = + (ExprASTNode *const *)kv_Z(ast_stack, i); + const ExprASTNodeType eastnode_type = (*eastnode_p)->type; + const ExprOpLvl eastnode_lvl = node_lvl(**eastnode_p); + if (eastnode_type == kExprNodeLambda) { + assert(cur_pt == kEPTLambdaArguments + && want_node == kENodeOperator); + break; + } else if (eastnode_type == kExprNodeDictLiteral + || eastnode_type == kExprNodeListLiteral + || eastnode_type == kExprNodeCall) { + break; + } else if (eastnode_type == kExprNodeComma + || eastnode_type == kExprNodeColon + || eastnode_lvl > kEOpLvlComma) { + // Do nothing + } else { +viml_pexpr_parse_invalid_comma: ERROR_FROM_TOKEN_AND_MSG(cur_token, - _("E15: Environment variable name missing")); + _("E15: Comma outside of call, lambda or literal: %.*s")); + break; + } + if (i == kv_size(ast_stack) - 1) { + goto viml_pexpr_parse_invalid_comma; } - *top_node_p = cur_node; - want_node = kENodeOperator; - viml_parser_highlight(pstate, cur_token.start, 1, HL(EnvironmentSigil)); - viml_parser_highlight(pstate, shifted_pos(cur_token.start, 1), - cur_token.len - 1, HL(EnvironmentName)); - break; } - case kExprLexNot: { - if (want_node == kENodeOperator) { - OP_MISSING; + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeComma); + ADD_OP_NODE(cur_node); + HL_CUR_TOKEN(Comma); + break; +#define EXP_VAL_COLON "E15: Expected value, got colon: %.*s" + case kExprLexColon: { + bool is_ternary = false; + if (kv_size(ast_stack) < 2) { + goto viml_pexpr_parse_invalid_colon; + } + bool can_be_ternary = true; + bool is_subscript = false; + for (size_t i = 1; i < kv_size(ast_stack); i++) { + ExprASTNode *const *const eastnode_p = + (ExprASTNode *const *)kv_Z(ast_stack, i); + const ExprASTNodeType eastnode_type = (*eastnode_p)->type; + const ExprOpLvl eastnode_lvl = node_lvl(**eastnode_p); + STATIC_ASSERT(kEOpLvlTernary > kEOpLvlComma, + "Unexpected operator priorities"); + if (can_be_ternary && eastnode_type == kExprNodeTernaryValue + && !(*eastnode_p)->data.ter.got_colon) { + kv_drop(ast_stack, i); + (*eastnode_p)->start = cur_token.start; + (*eastnode_p)->len = cur_token.len; + if (prev_token.type == kExprLexSpacing) { + (*eastnode_p)->start = prev_token.start; + (*eastnode_p)->len += prev_token.len; + } + is_ternary = true; + (*eastnode_p)->data.ter.got_colon = true; + ADD_VALUE_IF_MISSING(_(EXP_VAL_COLON)); + assert((*eastnode_p)->children != NULL); + assert((*eastnode_p)->children->next == NULL); + kvi_push(ast_stack, &(*eastnode_p)->children->next); + break; + } else if (eastnode_type == kExprNodeUnknownFigure) { + SELECT_FIGURE_BRACE_TYPE(*eastnode_p, DictLiteral, Dict); + break; + } else if (eastnode_type == kExprNodeDictLiteral) { + break; + } else if (eastnode_type == kExprNodeSubscript) { + is_subscript = true; + // can_be_ternary = false; + assert(!is_ternary); + break; + } else if (eastnode_type == kExprNodeColon) { + goto viml_pexpr_parse_invalid_colon; + } else if (eastnode_lvl >= kEOpLvlTernaryValue) { + // Do nothing + } else if (eastnode_lvl >= kEOpLvlComma) { + can_be_ternary = false; + } else { + goto viml_pexpr_parse_invalid_colon; + } + if (i == kv_size(ast_stack) - 1) { + goto viml_pexpr_parse_invalid_colon; } - NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeNot); - *top_node_p = cur_node; - kvi_push(ast_stack, &cur_node->children); - HL_CUR_TOKEN(Not); - 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; + if (is_subscript) { + assert(kv_size(ast_stack) > 1); + // Colon immediately following subscript start: it is empty subscript + // part like a[:2]. + if (want_node == kENodeValue + && (*kv_Z(ast_stack, 1))->type == kExprNodeSubscript) { + NEW_NODE_WITH_CUR_POS(*top_node_p, kExprNodeMissing); + (*top_node_p)->len = 0; + want_node = kENodeOperator; } 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_VALUE_IF_MISSING(_(EXP_VAL_COLON)); } + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeColon); ADD_OP_NODE(cur_node); - if (cur_token.data.cmp.ccs != kCCStrategyUseOption) { - viml_parser_highlight(pstate, cur_token.start, cur_token.len - 1, - HL(Comparison)); - viml_parser_highlight( - pstate, shifted_pos(cur_token.start, cur_token.len - 1), 1, - HL(ComparisonModifier)); + HL_CUR_TOKEN(SubscriptColon); + } else { + goto viml_pexpr_parse_valid_colon; +viml_pexpr_parse_invalid_colon: + ERROR_FROM_TOKEN_AND_MSG(cur_token, + _("E15: Colon outside of dictionary or ternary operator: %.*s")); +viml_pexpr_parse_valid_colon: + ADD_VALUE_IF_MISSING(_(EXP_VAL_COLON)); + if (is_ternary) { + HL_CUR_TOKEN(TernaryColon); } else { - HL_CUR_TOKEN(Comparison); + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeColon); + ADD_OP_NODE(cur_node); + HL_CUR_TOKEN(Colon); } - want_node = kENodeValue; - break; } - case kExprLexComma: { - assert(!(want_node == kENodeValue && cur_pt == kEPTLambdaArguments)); - 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); + want_node = kENodeValue; + break; + } +#undef EXP_VAL_COLON + case kExprLexBracket: + 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, kExprNodeListLiteral); cur_node->len = 0; + if (want_node != kENodeValue) { + cur_node->children = *top_node_p; + } *top_node_p = cur_node; - want_node = kENodeOperator; - } - if (cur_pt == kEPTLambdaArguments) { - assert(lambda_node != NULL); - assert(lambda_node->data.fig.type_guesses.allow_lambda); - SELECT_FIGURE_BRACE_TYPE(lambda_node, Lambda, Lambda); + goto viml_pexpr_parse_bracket_closing_error; } - if (kv_size(ast_stack) < 2) { - goto viml_pexpr_parse_invalid_comma; + if (want_node == kENodeValue) { + // It is OK to want value if + // + // 1. It is empty list literal, in which case top node will be + // ListLiteral. + // 2. It is list literal with trailing comma, in which case top node + // will be that comma. + // 3. It is subscript with colon, but without one of the values: + // e.g. "a[:]", "a[1:]", top node will be colon in this case. + if ((*kv_last(ast_stack))->type != kExprNodeListLiteral + && (*kv_last(ast_stack))->type != kExprNodeComma + && (*kv_last(ast_stack))->type != kExprNodeColon) { + ERROR_FROM_TOKEN_AND_MSG(cur_token, + _("E15: Expected value, got closing bracket: %.*s")); + } } - for (size_t i = 1; i < kv_size(ast_stack); i++) { - ExprASTNode *const *const eastnode_p = - (ExprASTNode *const *)kv_Z(ast_stack, i); - const ExprASTNodeType eastnode_type = (*eastnode_p)->type; - const ExprOpLvl eastnode_lvl = node_lvl(**eastnode_p); - if (eastnode_type == kExprNodeLambda) { - assert(cur_pt == kEPTLambdaArguments - && want_node == kENodeOperator); - break; - } else if (eastnode_type == kExprNodeDictLiteral - || eastnode_type == kExprNodeListLiteral - || eastnode_type == kExprNodeCall) { - break; - } else if (eastnode_type == kExprNodeComma - || eastnode_type == kExprNodeColon - || eastnode_lvl > kEOpLvlComma) { - // Do nothing - } else { -viml_pexpr_parse_invalid_comma: - ERROR_FROM_TOKEN_AND_MSG( - cur_token, - _("E15: Comma outside of call, lambda or literal: %.*s")); - break; + do { + new_top_node_p = kv_pop(ast_stack); + } while (kv_size(ast_stack) + && (new_top_node_p == NULL + || ((*new_top_node_p)->type != kExprNodeListLiteral + && (*new_top_node_p)->type != kExprNodeSubscript))); + ExprASTNode *new_top_node = *new_top_node_p; + switch (new_top_node->type) { + case kExprNodeListLiteral: + if (pt_is_assignment(cur_pt) && new_top_node->children == NULL) { + ERROR_FROM_TOKEN_AND_MSG(cur_token, _("E475: Unable to assign to empty list: %.*s")); } - if (i == kv_size(ast_stack) - 1) { - goto viml_pexpr_parse_invalid_comma; + HL_CUR_TOKEN(List); + break; + case kExprNodeSubscript: + HL_CUR_TOKEN(SubscriptBracket); + break; + default: +viml_pexpr_parse_bracket_closing_error: + assert(!kv_size(ast_stack)); + ERROR_FROM_TOKEN_AND_MSG(cur_token, _("E15: Unexpected closing figure brace: %.*s")); + HL_CUR_TOKEN(List); + break; + } + kvi_push(ast_stack, new_top_node_p); + want_node = kENodeOperator; + if (kv_size(ast_stack) <= asgn_level) { + assert(kv_size(ast_stack) == asgn_level); + asgn_level = 0; + if (cur_pt == kEPTAssignment) { + assert(ast.err.msg); + } else if (cur_pt == kEPTExpr + && kv_size(pt_stack) > 1 + && pt_is_assignment(kv_Z(pt_stack, 1))) { + kv_drop(pt_stack, 1); } } - NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeComma); - ADD_OP_NODE(cur_node); - HL_CUR_TOKEN(Comma); - break; - } -#define EXP_VAL_COLON "E15: Expected value, got colon: %.*s" - case kExprLexColon: { - bool is_ternary = false; - if (kv_size(ast_stack) < 2) { - goto viml_pexpr_parse_invalid_colon; + if (cur_pt == kEPTSingleAssignment && kv_size(ast_stack) == 1) { + kv_drop(pt_stack, 1); } - bool can_be_ternary = true; - bool is_subscript = false; - for (size_t i = 1; i < kv_size(ast_stack); i++) { - ExprASTNode *const *const eastnode_p = - (ExprASTNode *const *)kv_Z(ast_stack, i); - const ExprASTNodeType eastnode_type = (*eastnode_p)->type; - const ExprOpLvl eastnode_lvl = node_lvl(**eastnode_p); - STATIC_ASSERT(kEOpLvlTernary > kEOpLvlComma, - "Unexpected operator priorities"); - if (can_be_ternary && eastnode_type == kExprNodeTernaryValue - && !(*eastnode_p)->data.ter.got_colon) { - kv_drop(ast_stack, i); - (*eastnode_p)->start = cur_token.start; - (*eastnode_p)->len = cur_token.len; - if (prev_token.type == kExprLexSpacing) { - (*eastnode_p)->start = prev_token.start; - (*eastnode_p)->len += prev_token.len; - } - is_ternary = true; - (*eastnode_p)->data.ter.got_colon = true; - ADD_VALUE_IF_MISSING(_(EXP_VAL_COLON)); - assert((*eastnode_p)->children != NULL); - assert((*eastnode_p)->children->next == NULL); - kvi_push(ast_stack, &(*eastnode_p)->children->next); - break; - } else if (eastnode_type == kExprNodeUnknownFigure) { - SELECT_FIGURE_BRACE_TYPE(*eastnode_p, DictLiteral, Dict); - break; - } else if (eastnode_type == kExprNodeDictLiteral) { - break; - } else if (eastnode_type == kExprNodeSubscript) { - is_subscript = true; - // can_be_ternary = false; - assert(!is_ternary); - break; - } else if (eastnode_type == kExprNodeColon) { - goto viml_pexpr_parse_invalid_colon; - } else if (eastnode_lvl >= kEOpLvlTernaryValue) { - // Do nothing - } else if (eastnode_lvl >= kEOpLvlComma) { - can_be_ternary = false; - } else { - goto viml_pexpr_parse_invalid_colon; - } - if (i == kv_size(ast_stack) - 1) { - goto viml_pexpr_parse_invalid_colon; + } else { + if (want_node == kENodeValue) { + // Value means list literal or list assignment. + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeListLiteral); + *top_node_p = cur_node; + kvi_push(ast_stack, &cur_node->children); + want_node = kENodeValue; + if (cur_pt == kEPTAssignment) { + // Additional assignment parse type allows to easily forbid nested + // lists. + kvi_push(pt_stack, kEPTSingleAssignment); + } else if (cur_pt == kEPTSingleAssignment) { + ERROR_FROM_TOKEN_AND_MSG(cur_token, + _("E475: Nested lists not allowed when assigning: %.*s")); } - } - if (is_subscript) { - assert(kv_size(ast_stack) > 1); - // Colon immediately following subscript start: it is empty subscript - // part like a[:2]. - if (want_node == kENodeValue - && (*kv_Z(ast_stack, 1))->type == kExprNodeSubscript) { - NEW_NODE_WITH_CUR_POS(*top_node_p, kExprNodeMissing); - (*top_node_p)->len = 0; - want_node = kENodeOperator; - } else { - ADD_VALUE_IF_MISSING(_(EXP_VAL_COLON)); + HL_CUR_TOKEN(List); + } else { + // Operator means subscript, also in assignment. But in assignment + // subscript may be pretty much any expression, so need to push + // kEPTExpr. + if (prev_token.type == kExprLexSpacing) { + OP_MISSING; } - NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeColon); + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeSubscript); ADD_OP_NODE(cur_node); - HL_CUR_TOKEN(SubscriptColon); - } else { - goto viml_pexpr_parse_valid_colon; -viml_pexpr_parse_invalid_colon: - ERROR_FROM_TOKEN_AND_MSG( - cur_token, - _("E15: Colon outside of dictionary or ternary operator: %.*s")); -viml_pexpr_parse_valid_colon: - ADD_VALUE_IF_MISSING(_(EXP_VAL_COLON)); - if (is_ternary) { - HL_CUR_TOKEN(TernaryColon); - } else { - NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeColon); - ADD_OP_NODE(cur_node); - HL_CUR_TOKEN(Colon); + HL_CUR_TOKEN(SubscriptBracket); + if (pt_is_assignment(cur_pt)) { + assert(want_node == kENodeValue); // Subtract 1 for NULL at top. + asgn_level = kv_size(ast_stack) - 1; + kvi_push(pt_stack, kEPTExpr); } } - want_node = kENodeValue; - break; } -#undef EXP_VAL_COLON - case kExprLexBracket: { - 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, kExprNodeListLiteral); - cur_node->len = 0; - if (want_node != kENodeValue) { - cur_node->children = *top_node_p; - } - *top_node_p = cur_node; - goto viml_pexpr_parse_bracket_closing_error; - } - if (want_node == kENodeValue) { - // It is OK to want value if - // - // 1. It is empty list literal, in which case top node will be - // ListLiteral. - // 2. It is list literal with trailing comma, in which case top node - // will be that comma. - // 3. It is subscript with colon, but without one of the values: - // e.g. "a[:]", "a[1:]", top node will be colon in this case. - if ((*kv_last(ast_stack))->type != kExprNodeListLiteral - && (*kv_last(ast_stack))->type != kExprNodeComma - && (*kv_last(ast_stack))->type != kExprNodeColon) { - ERROR_FROM_TOKEN_AND_MSG( - cur_token, - _("E15: Expected value, got closing bracket: %.*s")); - } + 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; } - do { - new_top_node_p = kv_pop(ast_stack); - } while (kv_size(ast_stack) - && (new_top_node_p == NULL - || ((*new_top_node_p)->type != kExprNodeListLiteral - && (*new_top_node_p)->type != kExprNodeSubscript))); - ExprASTNode *new_top_node = *new_top_node_p; - switch (new_top_node->type) { - case kExprNodeListLiteral: { - if (pt_is_assignment(cur_pt) && new_top_node->children == NULL) { - ERROR_FROM_TOKEN_AND_MSG( - cur_token, _("E475: Unable to assign to empty list: %.*s")); - } - HL_CUR_TOKEN(List); - break; - } - case kExprNodeSubscript: { - HL_CUR_TOKEN(SubscriptBracket); - break; - } - default: { -viml_pexpr_parse_bracket_closing_error: - assert(!kv_size(ast_stack)); - ERROR_FROM_TOKEN_AND_MSG( - cur_token, _("E15: Unexpected closing figure brace: %.*s")); - HL_CUR_TOKEN(List); - break; - } + *top_node_p = cur_node; + new_top_node_p = top_node_p; + 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")); } - kvi_push(ast_stack, new_top_node_p); - want_node = kENodeOperator; - if (kv_size(ast_stack) <= asgn_level) { - assert(kv_size(ast_stack) == asgn_level); - asgn_level = 0; - if (cur_pt == kEPTAssignment) { - assert(ast.err.msg); - } else if (cur_pt == kEPTExpr - && kv_size(pt_stack) > 1 - && pt_is_assignment(kv_Z(pt_stack, 1))) { - kv_drop(pt_stack, 1); + } + 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. + 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); } - if (cur_pt == kEPTSingleAssignment && kv_size(ast_stack) == 1) { + 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; + if (kv_size(ast_stack) <= asgn_level) { + assert(kv_size(ast_stack) == asgn_level); + if (cur_pt == kEPTExpr + && kv_size(pt_stack) > 1 + && pt_is_assignment(kv_Z(pt_stack, 1))) { kv_drop(pt_stack, 1); - } - } else { - if (want_node == kENodeValue) { - // Value means list literal or list assignment. - NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeListLiteral); - *top_node_p = cur_node; - kvi_push(ast_stack, &cur_node->children); - want_node = kENodeValue; - if (cur_pt == kEPTAssignment) { - // Additional assignment parse type allows to easily forbid nested - // lists. - kvi_push(pt_stack, kEPTSingleAssignment); - } else if (cur_pt == kEPTSingleAssignment) { - ERROR_FROM_TOKEN_AND_MSG( - cur_token, - _("E475: Nested lists not allowed when assigning: %.*s")); - } - HL_CUR_TOKEN(List); - } else { - // Operator means subscript, also in assignment. But in assignment - // subscript may be pretty much any expression, so need to push - // kEPTExpr. - if (prev_token.type == kExprLexSpacing) { - OP_MISSING; - } - NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeSubscript); - ADD_OP_NODE(cur_node); - HL_CUR_TOKEN(SubscriptBracket); - if (pt_is_assignment(cur_pt)) { - assert(want_node == kENodeValue); // Subtract 1 for NULL at top. - asgn_level = kv_size(ast_stack) - 1; - kvi_push(pt_stack, kEPTExpr); - } + asgn_level = 0; } } - 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); + } else { + if (want_node == kENodeValue) { + HL_CUR_TOKEN(FigureBrace); + // Value: may be any of lambda, dictionary literal and curly braces + // name. + + // Though if we are in an assignment this may only be a curly braces + // name. + if (pt_is_assignment(cur_pt)) { + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeCurlyBracesIdentifier); cur_node->data.fig.type_guesses.allow_lambda = false; cur_node->data.fig.type_guesses.allow_dict = false; - cur_node->data.fig.type_guesses.allow_ident = false; - cur_node->len = 0; - if (want_node != kENodeValue) { - cur_node->children = *top_node_p; - } - *top_node_p = cur_node; - new_top_node_p = top_node_p; - 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")); - } - } - 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. - 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; - } + cur_node->data.fig.type_guesses.allow_ident = true; + kvi_push(pt_stack, kEPTExpr); + } else { + 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; } - kvi_push(ast_stack, new_top_node_p); - want_node = kENodeOperator; - if (kv_size(ast_stack) <= asgn_level) { - assert(kv_size(ast_stack) == asgn_level); - if (cur_pt == kEPTExpr - && kv_size(pt_stack) > 1 - && pt_is_assignment(kv_Z(pt_stack, 1))) { - kv_drop(pt_stack, 1); - asgn_level = 0; - } + 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); + kvi_push(pt_stack, kEPTLambdaArguments); + lambda_node = cur_node; } else { - if (want_node == kENodeValue) { - HL_CUR_TOKEN(FigureBrace); - // Value: may be any of lambda, dictionary literal and curly braces - // name. - - // Though if we are in an assignment this may only be a curly braces - // name. + ADD_IDENT(do { + NEW_NODE_WITH_CUR_POS(cur_node, + kExprNodeCurlyBracesIdentifier); + cur_node->data.fig.opening_hl_idx = kv_size(*pstate->colors); + cur_node->data.fig.type_guesses.allow_lambda = false; + cur_node->data.fig.type_guesses.allow_dict = false; + cur_node->data.fig.type_guesses.allow_ident = true; + kvi_push(ast_stack, &cur_node->children); if (pt_is_assignment(cur_pt)) { - NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeCurlyBracesIdentifier); - cur_node->data.fig.type_guesses.allow_lambda = false; - cur_node->data.fig.type_guesses.allow_dict = false; - cur_node->data.fig.type_guesses.allow_ident = true; kvi_push(pt_stack, kEPTExpr); - } else { - NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeUnknownFigure); - 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); - kvi_push(pt_stack, kEPTLambdaArguments); - lambda_node = cur_node; - } else { - ADD_IDENT( - do { - NEW_NODE_WITH_CUR_POS(cur_node, - kExprNodeCurlyBracesIdentifier); - cur_node->data.fig.opening_hl_idx = kv_size(*pstate->colors); - cur_node->data.fig.type_guesses.allow_lambda = false; - cur_node->data.fig.type_guesses.allow_dict = false; - cur_node->data.fig.type_guesses.allow_ident = true; - kvi_push(ast_stack, &cur_node->children); - if (pt_is_assignment(cur_pt)) { - kvi_push(pt_stack, kEPTExpr); - } - want_node = kENodeValue; - } while (0), - Curly); - } - if (pt_is_assignment(cur_pt) - && !pt_is_assignment(kv_last(pt_stack))) { - assert(want_node == kENodeValue); // Subtract 1 for NULL at top. - asgn_level = kv_size(ast_stack) - 1; - } + want_node = kENodeValue; + } while (0), + Curly); + } + if (pt_is_assignment(cur_pt) + && !pt_is_assignment(kv_last(pt_stack))) { + assert(want_node == kENodeValue); // Subtract 1 for NULL at top. + asgn_level = kv_size(ast_stack) - 1; } - break; } - case kExprLexArrow: { - if (cur_pt == kEPTLambdaArguments) { - kv_drop(pt_stack, 1); - assert(kv_size(pt_stack)); - if (want_node == kENodeValue) { - // Wanting value means trailing comma and NULL at the top of the - // stack. - 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 == kENodeValue); - 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; + break; + case kExprLexArrow: + if (cur_pt == kEPTLambdaArguments) { + kv_drop(pt_stack, 1); + assert(kv_size(pt_stack)); + if (want_node == kENodeValue) { + // Wanting value means trailing comma and NULL at the top of the + // stack. + 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 == kENodeValue); + lambda_node->children = cur_node; + kvi_push(ast_stack, &lambda_node->children); } else { - // Only first branch is valid. - ADD_VALUE_IF_MISSING(_("E15: Unexpected arrow: %.*s")); - ERROR_FROM_TOKEN_AND_MSG( - cur_token, _("E15: Arrow outside of lambda: %.*s")); - NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeArrow); - ADD_OP_NODE(cur_node); + assert(lambda_node->children->next == NULL); + lambda_node->children->next = cur_node; + kvi_push(ast_stack, &lambda_node->children->next); } - want_node = kENodeValue; - HL_CUR_TOKEN(Arrow); - break; + kvi_push(ast_stack, &cur_node->children); + lambda_node = NULL; + } else { + // Only first branch is valid. + ADD_VALUE_IF_MISSING(_("E15: Unexpected arrow: %.*s")); + ERROR_FROM_TOKEN_AND_MSG(cur_token, _("E15: Arrow outside of lambda: %.*s")); + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeArrow); + ADD_OP_NODE(cur_node); } - case kExprLexPlainIdentifier: { - const ExprVarScope scope = (cur_token.type == kExprLexInvalid + want_node = kENodeValue; + HL_CUR_TOKEN(Arrow); + break; + case kExprLexPlainIdentifier: { + const ExprVarScope scope = (cur_token.type == kExprLexInvalid ? kExprVarScopeMissing : cur_token.data.var.scope); - if (want_node == kENodeValue) { - want_node = kENodeOperator; - NEW_NODE_WITH_CUR_POS(cur_node, - (node_is_key + if (want_node == kENodeValue) { + want_node = kENodeOperator; + NEW_NODE_WITH_CUR_POS(cur_node, + (node_is_key ? kExprNodePlainKey : kExprNodePlainIdentifier)); - cur_node->data.var.scope = scope; - const size_t scope_shift = (scope == kExprVarScopeMissing ? 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) { - assert(!node_is_key); - viml_parser_highlight(pstate, cur_token.start, 1, - HL(IdentifierScope)); - viml_parser_highlight(pstate, shifted_pos(cur_token.start, 1), 1, - HL(IdentifierScopeDelimiter)); - } - viml_parser_highlight(pstate, shifted_pos(cur_token.start, - scope_shift), - cur_token.len - scope_shift, - (node_is_key + cur_node->data.var.scope = scope; + const size_t scope_shift = (scope == kExprVarScopeMissing ? 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) { + assert(!node_is_key); + viml_parser_highlight(pstate, cur_token.start, 1, + HL(IdentifierScope)); + viml_parser_highlight(pstate, shifted_pos(cur_token.start, 1), 1, + HL(IdentifierScopeDelimiter)); + } + viml_parser_highlight(pstate, shifted_pos(cur_token.start, + scope_shift), + cur_token.len - scope_shift, + (node_is_key ? HL(IdentifierKey) : HL(IdentifierName))); + } else { + if (scope == kExprVarScopeMissing) { + ADD_IDENT(do { + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodePlainIdentifier); + cur_node->data.var.scope = scope; + cur_node->data.var.ident = pline.data + cur_token.start.col; + cur_node->data.var.ident_len = cur_token.len; + want_node = kENodeOperator; + } while (0), + IdentifierName); } else { - if (scope == kExprVarScopeMissing) { - ADD_IDENT( - do { - NEW_NODE_WITH_CUR_POS(cur_node, kExprNodePlainIdentifier); - cur_node->data.var.scope = scope; - cur_node->data.var.ident = pline.data + cur_token.start.col; - cur_node->data.var.ident_len = cur_token.len; - want_node = kENodeOperator; - } while (0), - IdentifierName); - } else { - OP_MISSING; - } - } - break; - } - case kExprLexNumber: { - if (want_node != kENodeValue) { OP_MISSING; } - if (node_is_key) { - NEW_NODE_WITH_CUR_POS(cur_node, kExprNodePlainKey); - cur_node->data.var.ident = pline.data + cur_token.start.col; - cur_node->data.var.ident_len = cur_token.len; - HL_CUR_TOKEN(IdentifierKey); - } else if (cur_token.data.num.is_float) { - NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeFloat); - cur_node->data.flt.value = cur_token.data.num.val.floating; - HL_CUR_TOKEN(Float); - } else { - NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeInteger); - cur_node->data.num.value = cur_token.data.num.val.integer; - const uint8_t prefix_length = base_to_prefix_length[ - cur_token.data.num.base]; - viml_parser_highlight(pstate, cur_token.start, prefix_length, - HL(NumberPrefix)); - viml_parser_highlight( - pstate, shifted_pos(cur_token.start, prefix_length), - cur_token.len - prefix_length, HL(Number)); - } - want_node = kENodeOperator; - *top_node_p = cur_node; - break; } - case kExprLexDot: { - ADD_VALUE_IF_MISSING(_("E15: Unexpected dot: %.*s")); - if (prev_token.type == kExprLexSpacing) { - if (cur_pt == kEPTAssignment) { - ERROR_FROM_TOKEN_AND_MSG( - cur_token, _("E15: Cannot concatenate in assignments: %.*s")); - } - NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeConcat); - HL_CUR_TOKEN(Concat); - } else { - NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeConcatOrSubscript); - HL_CUR_TOKEN(ConcatOrSubscript); + break; + } + case kExprLexNumber: + if (want_node != kENodeValue) { + OP_MISSING; + } + if (node_is_key) { + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodePlainKey); + cur_node->data.var.ident = pline.data + cur_token.start.col; + cur_node->data.var.ident_len = cur_token.len; + HL_CUR_TOKEN(IdentifierKey); + } else if (cur_token.data.num.is_float) { + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeFloat); + cur_node->data.flt.value = cur_token.data.num.val.floating; + HL_CUR_TOKEN(Float); + } else { + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeInteger); + cur_node->data.num.value = cur_token.data.num.val.integer; + const uint8_t prefix_length = base_to_prefix_length[ + cur_token.data.num.base]; + viml_parser_highlight(pstate, cur_token.start, prefix_length, + HL(NumberPrefix)); + viml_parser_highlight(pstate, shifted_pos(cur_token.start, prefix_length), + cur_token.len - prefix_length, HL(Number)); + } + want_node = kENodeOperator; + *top_node_p = cur_node; + break; + case kExprLexDot: + ADD_VALUE_IF_MISSING(_("E15: Unexpected dot: %.*s")); + if (prev_token.type == kExprLexSpacing) { + if (cur_pt == kEPTAssignment) { + ERROR_FROM_TOKEN_AND_MSG(cur_token, _("E15: Cannot concatenate in assignments: %.*s")); } - ADD_OP_NODE(cur_node); - break; + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeConcat); + HL_CUR_TOKEN(Concat); + } else { + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeConcatOrSubscript); + HL_CUR_TOKEN(ConcatOrSubscript); } - case kExprLexParenthesis: { - if (cur_token.data.brc.closing) { - if (want_node == kENodeValue) { - if (kv_size(ast_stack) > 1) { - const ExprASTNode *const prev_top_node = *kv_Z(ast_stack, 1); - if (prev_top_node->type == kExprNodeCall) { - // Function call without arguments, this is not an error. - // But further code does not expect NULL nodes. - kv_drop(ast_stack, 1); - goto viml_pexpr_parse_no_paren_closing_error; - } + ADD_OP_NODE(cur_node); + break; + case kExprLexParenthesis: + if (cur_token.data.brc.closing) { + if (want_node == kENodeValue) { + if (kv_size(ast_stack) > 1) { + const ExprASTNode *const prev_top_node = *kv_Z(ast_stack, 1); + if (prev_top_node->type == kExprNodeCall) { + // Function call without arguments, this is not an error. + // But further code does not expect NULL nodes. + kv_drop(ast_stack, 1); + goto viml_pexpr_parse_no_paren_closing_error; } - ERROR_FROM_TOKEN_AND_MSG( - cur_token, _("E15: Expected value, got parenthesis: %.*s")); - NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeMissing); - cur_node->len = 0; - *top_node_p = cur_node; - } else { - // Always drop the topmost value: 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. - kv_drop(ast_stack, 1); } + ERROR_FROM_TOKEN_AND_MSG(cur_token, _("E15: Expected value, got parenthesis: %.*s")); + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeMissing); + cur_node->len = 0; + *top_node_p = cur_node; + } else { + // Always drop the topmost value: 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. + kv_drop(ast_stack, 1); + } viml_pexpr_parse_no_paren_closing_error: {} - ExprASTNode **new_top_node_p = NULL; - while (kv_size(ast_stack) - && (new_top_node_p == NULL - || ((*new_top_node_p)->type != kExprNodeNested - && (*new_top_node_p)->type != kExprNodeCall))) { - new_top_node_p = kv_pop(ast_stack); - } - if (new_top_node_p != NULL - && ((*new_top_node_p)->type == kExprNodeNested - || (*new_top_node_p)->type == kExprNodeCall)) { - if ((*new_top_node_p)->type == kExprNodeNested) { - HL_CUR_TOKEN(NestingParenthesis); - } else { - HL_CUR_TOKEN(CallingParenthesis); - } - } else { - // “Always drop the topmost value” branch has got rid of the single - // value stack had, so there is nothing known to enclose. Correct - // this. - if (new_top_node_p == NULL) { - new_top_node_p = top_node_p; - } - ERROR_FROM_TOKEN_AND_MSG( - cur_token, _("E15: Unexpected closing parenthesis: %.*s")); + ExprASTNode **new_top_node_p = NULL; + while (kv_size(ast_stack) + && (new_top_node_p == NULL + || ((*new_top_node_p)->type != kExprNodeNested + && (*new_top_node_p)->type != kExprNodeCall))) { + new_top_node_p = kv_pop(ast_stack); + } + if (new_top_node_p != NULL + && ((*new_top_node_p)->type == kExprNodeNested + || (*new_top_node_p)->type == kExprNodeCall)) { + if ((*new_top_node_p)->type == kExprNodeNested) { HL_CUR_TOKEN(NestingParenthesis); - cur_node = NEW_NODE(kExprNodeNested); - cur_node->start = cur_token.start; - cur_node->len = 0; - // Unexpected closing parenthesis, assume that it was wanted to - // enclose everything in (). - cur_node->children = *new_top_node_p; - *new_top_node_p = cur_node; - assert(cur_node->next == NULL); + } else { + HL_CUR_TOKEN(CallingParenthesis); } - kvi_push(ast_stack, new_top_node_p); - want_node = kENodeOperator; } else { - switch (want_node) { - case kENodeValue: { - NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeNested); - *top_node_p = cur_node; - kvi_push(ast_stack, &cur_node->children); - HL_CUR_TOKEN(NestingParenthesis); - break; - } - case kENodeOperator: { - if (prev_token.type == kExprLexSpacing) { - // For some reason "function (args)" is a function call, but - // "(funcref) (args)" is not. AFAIR this somehow involves - // compatibility and Bram was commenting that this is - // intentionally inconsistent and he is not very happy with the - // situation himself. - if ((*top_node_p)->type != kExprNodePlainIdentifier - && (*top_node_p)->type != kExprNodeComplexIdentifier - && (*top_node_p)->type != kExprNodeCurlyBracesIdentifier) { - OP_MISSING; - } - } - NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeCall); - ADD_OP_NODE(cur_node); - HL_CUR_TOKEN(CallingParenthesis); - break; + // “Always drop the topmost value” branch has got rid of the single + // value stack had, so there is nothing known to enclose. Correct + // this. + if (new_top_node_p == NULL) { + new_top_node_p = top_node_p; + } + ERROR_FROM_TOKEN_AND_MSG(cur_token, _("E15: Unexpected closing parenthesis: %.*s")); + HL_CUR_TOKEN(NestingParenthesis); + cur_node = NEW_NODE(kExprNodeNested); + cur_node->start = cur_token.start; + cur_node->len = 0; + // Unexpected closing parenthesis, assume that it was wanted to + // enclose everything in (). + cur_node->children = *new_top_node_p; + *new_top_node_p = cur_node; + assert(cur_node->next == NULL); + } + kvi_push(ast_stack, new_top_node_p); + want_node = kENodeOperator; + } else { + switch (want_node) { + case kENodeValue: + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeNested); + *top_node_p = cur_node; + kvi_push(ast_stack, &cur_node->children); + HL_CUR_TOKEN(NestingParenthesis); + break; + case kENodeOperator: + if (prev_token.type == kExprLexSpacing) { + // For some reason "function (args)" is a function call, but + // "(funcref) (args)" is not. AFAIR this somehow involves + // compatibility and Bram was commenting that this is + // intentionally inconsistent and he is not very happy with the + // situation himself. + if ((*top_node_p)->type != kExprNodePlainIdentifier + && (*top_node_p)->type != kExprNodeComplexIdentifier + && (*top_node_p)->type != kExprNodeCurlyBracesIdentifier) { + OP_MISSING; } } - want_node = kENodeValue; + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeCall); + ADD_OP_NODE(cur_node); + HL_CUR_TOKEN(CallingParenthesis); + break; } - break; - } - case kExprLexQuestion: { - ADD_VALUE_IF_MISSING(_("E15: Expected value, got question mark: %.*s")); - NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeTernary); - ADD_OP_NODE(cur_node); - HL_CUR_TOKEN(Ternary); - ExprASTNode *ter_val_node; - NEW_NODE_WITH_CUR_POS(ter_val_node, kExprNodeTernaryValue); - ter_val_node->data.ter.got_colon = false; - assert(cur_node->children != NULL); - assert(cur_node->children->next == NULL); - assert(kv_last(ast_stack) == &cur_node->children->next); - *kv_last(ast_stack) = ter_val_node; - kvi_push(ast_stack, &ter_val_node->children); - break; + want_node = kENodeValue; } - case kExprLexDoubleQuotedString: - case kExprLexSingleQuotedString: { - const bool is_double = (tok_type == kExprLexDoubleQuotedString); - if (!cur_token.data.str.closed) { - // It is weird, but Vim has two identical errors messages with - // different error numbers: "E114: Missing quote" and - // "E115: Missing quote". - ERROR_FROM_TOKEN_AND_MSG( - cur_token, (is_double + break; + case kExprLexQuestion: { + ADD_VALUE_IF_MISSING(_("E15: Expected value, got question mark: %.*s")); + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeTernary); + ADD_OP_NODE(cur_node); + HL_CUR_TOKEN(Ternary); + ExprASTNode *ter_val_node; + NEW_NODE_WITH_CUR_POS(ter_val_node, kExprNodeTernaryValue); + ter_val_node->data.ter.got_colon = false; + assert(cur_node->children != NULL); + assert(cur_node->children->next == NULL); + assert(kv_last(ast_stack) == &cur_node->children->next); + *kv_last(ast_stack) = ter_val_node; + kvi_push(ast_stack, &ter_val_node->children); + break; + } + case kExprLexDoubleQuotedString: + case kExprLexSingleQuotedString: { + const bool is_double = (tok_type == kExprLexDoubleQuotedString); + if (!cur_token.data.str.closed) { + // It is weird, but Vim has two identical errors messages with + // different error numbers: "E114: Missing quote" and + // "E115: Missing quote". + ERROR_FROM_TOKEN_AND_MSG(cur_token, (is_double ? _("E114: Missing double quote: %.*s") : _("E115: Missing single quote: %.*s"))); - } - if (want_node == kENodeOperator) { - OP_MISSING; - } - NEW_NODE_WITH_CUR_POS( - cur_node, (is_double + } + if (want_node == kENodeOperator) { + OP_MISSING; + } + NEW_NODE_WITH_CUR_POS(cur_node, (is_double ? kExprNodeDoubleQuotedString : kExprNodeSingleQuotedString)); - *top_node_p = cur_node; - parse_quoted_string(pstate, cur_node, cur_token, ast_stack, is_invalid); - want_node = kENodeOperator; - break; + *top_node_p = cur_node; + parse_quoted_string(pstate, cur_node, cur_token, ast_stack, is_invalid); + want_node = kENodeOperator; + break; + } + case kExprLexAssignment: + if (cur_pt == kEPTAssignment) { + kv_drop(pt_stack, 1); + } else if (cur_pt == kEPTSingleAssignment) { + kv_drop(pt_stack, 2); + ERROR_FROM_TOKEN_AND_MSG(cur_token, + _("E475: Expected closing bracket to end list assignment " + "lvalue: %.*s")); + } else { + ERROR_FROM_TOKEN_AND_MSG(cur_token, _("E15: Misplaced assignment: %.*s")); } - case kExprLexAssignment: { - if (cur_pt == kEPTAssignment) { - kv_drop(pt_stack, 1); - } else if (cur_pt == kEPTSingleAssignment) { - kv_drop(pt_stack, 2); - ERROR_FROM_TOKEN_AND_MSG( - cur_token, - _("E475: Expected closing bracket to end list assignment " - "lvalue: %.*s")); - } else { - ERROR_FROM_TOKEN_AND_MSG( - cur_token, _("E15: Misplaced assignment: %.*s")); - } - assert(kv_size(pt_stack)); - assert(kv_last(pt_stack) == kEPTExpr); - ADD_VALUE_IF_MISSING(_("E15: Unexpected assignment: %.*s")); - NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeAssignment); - cur_node->data.ass.type = cur_token.data.ass.type; - switch (cur_token.data.ass.type) { + assert(kv_size(pt_stack)); + assert(kv_last(pt_stack) == kEPTExpr); + ADD_VALUE_IF_MISSING(_("E15: Unexpected assignment: %.*s")); + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeAssignment); + cur_node->data.ass.type = cur_token.data.ass.type; + switch (cur_token.data.ass.type) { #define HL_ASGN(asgn, hl) \ - case kExprAsgn##asgn: { HL_CUR_TOKEN(hl); break; } - HL_ASGN(Plain, PlainAssignment) - HL_ASGN(Add, AssignmentWithAddition) - HL_ASGN(Subtract, AssignmentWithSubtraction) - HL_ASGN(Concat, AssignmentWithConcatenation) +case kExprAsgn##asgn: { HL_CUR_TOKEN(hl); break; } + HL_ASGN(Plain, PlainAssignment) + HL_ASGN(Add, AssignmentWithAddition) + HL_ASGN(Subtract, AssignmentWithSubtraction) + HL_ASGN(Concat, AssignmentWithConcatenation) #undef HL_ASGN - } - ADD_OP_NODE(cur_node); - break; } + ADD_OP_NODE(cur_node); + break; } viml_pexpr_parse_cycle_end: prev_token = cur_token; @@ -2972,115 +2970,96 @@ viml_pexpr_parse_end: assert(cur_node != NULL); // TODO(ZyX-I): Rehighlight as invalid? switch (cur_node->type) { - case kExprNodeOpMissing: - case kExprNodeMissing: { - // Error should’ve been already reported. - break; - } - case kExprNodeCall: { - east_set_error( - pstate, &ast.err, - _("E116: Missing closing parenthesis for function call: %.*s"), - cur_node->start); - break; - } - case kExprNodeNested: { - east_set_error( - pstate, &ast.err, - _("E110: Missing closing parenthesis for nested expression" - ": %.*s"), - cur_node->start); - break; - } - case kExprNodeListLiteral: { - // For whatever reason "[1" yields "E696: Missing comma in list" error - // in Vim while "[1," yields E697. - east_set_error( - pstate, &ast.err, - _("E697: Missing end of List ']': %.*s"), - cur_node->start); - break; - } - case kExprNodeDictLiteral: { - // Same problem like with list literal with E722 (missing comma) vs - // E723, but additionally just "{" yields only E15. - east_set_error( - pstate, &ast.err, - _("E723: Missing end of Dictionary '}': %.*s"), - cur_node->start); - break; - } - case kExprNodeUnknownFigure: { - east_set_error( - pstate, &ast.err, - _("E15: Missing closing figure brace: %.*s"), - cur_node->start); - break; - } - case kExprNodeLambda: { - east_set_error( - pstate, &ast.err, - _("E15: Missing closing figure brace for lambda: %.*s"), - cur_node->start); - break; - } - case kExprNodeCurlyBracesIdentifier: { - // Until trailing "}" it is impossible to distinguish curly braces - // identifier and dictionary, so it must not appear in the stack like - // this. - abort(); - } - case kExprNodeInteger: - case kExprNodeFloat: - case kExprNodeSingleQuotedString: - case kExprNodeDoubleQuotedString: - case kExprNodeOption: - case kExprNodeEnvironment: - case kExprNodeRegister: - case kExprNodePlainIdentifier: - case kExprNodePlainKey: { - // These are plain values and not containers, for them it should only - // be possible to show up in the topmost stack element, but it was - // unconditionally popped at the start. - abort(); - } - case kExprNodeComma: - case kExprNodeColon: - case kExprNodeArrow: { - // It is actually only valid inside something else, but everything - // where one of the above is valid requires to be closed and thus is - // to be caught later. - break; - } - case kExprNodeSubscript: - case kExprNodeConcatOrSubscript: - case kExprNodeComplexIdentifier: - case kExprNodeAssignment: - case kExprNodeMod: - case kExprNodeDivision: - case kExprNodeMultiplication: - case kExprNodeNot: - case kExprNodeAnd: - case kExprNodeOr: - case kExprNodeConcat: - case kExprNodeComparison: - case kExprNodeUnaryMinus: - case kExprNodeUnaryPlus: - case kExprNodeBinaryMinus: - case kExprNodeTernary: - case kExprNodeBinaryPlus: { - // It is OK to see these in the stack. - break; - } - case kExprNodeTernaryValue: { - if (!cur_node->data.ter.got_colon) { - // Actually Vim throws E109 in more cases. - east_set_error( - pstate, &ast.err, _("E109: Missing ':' after '?': %.*s"), - cur_node->start); - } - break; + case kExprNodeOpMissing: + case kExprNodeMissing: + // Error should’ve been already reported. + break; + case kExprNodeCall: + east_set_error(pstate, &ast.err, + _("E116: Missing closing parenthesis for function call: %.*s"), + cur_node->start); + break; + case kExprNodeNested: + east_set_error(pstate, &ast.err, + _("E110: Missing closing parenthesis for nested expression" + ": %.*s"), + cur_node->start); + break; + case kExprNodeListLiteral: + // For whatever reason "[1" yields "E696: Missing comma in list" error + // in Vim while "[1," yields E697. + east_set_error(pstate, &ast.err, + _("E697: Missing end of List ']': %.*s"), + cur_node->start); + break; + case kExprNodeDictLiteral: + // Same problem like with list literal with E722 (missing comma) vs + // E723, but additionally just "{" yields only E15. + east_set_error(pstate, &ast.err, + _("E723: Missing end of Dictionary '}': %.*s"), + cur_node->start); + break; + case kExprNodeUnknownFigure: + east_set_error(pstate, &ast.err, + _("E15: Missing closing figure brace: %.*s"), + cur_node->start); + break; + case kExprNodeLambda: + east_set_error(pstate, &ast.err, + _("E15: Missing closing figure brace for lambda: %.*s"), + cur_node->start); + break; + case kExprNodeCurlyBracesIdentifier: + // Until trailing "}" it is impossible to distinguish curly braces + // identifier and dictionary, so it must not appear in the stack like + // this. + abort(); + case kExprNodeInteger: + case kExprNodeFloat: + case kExprNodeSingleQuotedString: + case kExprNodeDoubleQuotedString: + case kExprNodeOption: + case kExprNodeEnvironment: + case kExprNodeRegister: + case kExprNodePlainIdentifier: + case kExprNodePlainKey: + // These are plain values and not containers, for them it should only + // be possible to show up in the topmost stack element, but it was + // unconditionally popped at the start. + abort(); + case kExprNodeComma: + case kExprNodeColon: + case kExprNodeArrow: + // It is actually only valid inside something else, but everything + // where one of the above is valid requires to be closed and thus is + // to be caught later. + break; + case kExprNodeSubscript: + case kExprNodeConcatOrSubscript: + case kExprNodeComplexIdentifier: + case kExprNodeAssignment: + case kExprNodeMod: + case kExprNodeDivision: + case kExprNodeMultiplication: + case kExprNodeNot: + case kExprNodeAnd: + case kExprNodeOr: + case kExprNodeConcat: + case kExprNodeComparison: + case kExprNodeUnaryMinus: + case kExprNodeUnaryPlus: + case kExprNodeBinaryMinus: + case kExprNodeTernary: + case kExprNodeBinaryPlus: + // It is OK to see these in the stack. + break; + case kExprNodeTernaryValue: + if (!cur_node->data.ter.got_colon) { + // Actually Vim throws E109 in more cases. + east_set_error(pstate, &ast.err, _("E109: Missing ':' after '?': %.*s"), + cur_node->start); } + break; } } } |