diff options
author | zeertzjq <zeertzjq@outlook.com> | 2024-08-01 09:46:32 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-08-01 09:46:32 +0800 |
commit | 02eaf7c4c10f08a73c2d9e2618918f424ac18fd2 (patch) | |
tree | 85f4a3dfbac23bd08a4d18abf9bb6876316ad035 | |
parent | e820474cde09273608be5f57e1032aab21e3c97d (diff) | |
parent | 7f1ba04421516c466ac9c3a63721f1e34abb3db1 (diff) | |
download | rneovim-02eaf7c4c10f08a73c2d9e2618918f424ac18fd2.tar.gz rneovim-02eaf7c4c10f08a73c2d9e2618918f424ac18fd2.tar.bz2 rneovim-02eaf7c4c10f08a73c2d9e2618918f424ac18fd2.zip |
Merge pull request #29935 from zeertzjq/vim-9.1.0450
vim-patch:partial:9.1.{0450,0462}
-rw-r--r-- | src/nvim/eval.c | 341 |
1 files changed, 193 insertions, 148 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c index be643b7990..b8c3df5688 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1443,7 +1443,7 @@ Object eval_foldtext(win_T *wp) /// The Dict is returned in 'lp'. Returns GLV_OK on success and GLV_FAIL on /// failure. Returns GLV_STOP to stop processing the characters following /// 'key_end'. -static glv_status_T get_lval_dict_item(char *name, lval_T *lp, char *key, int len, char **key_end, +static glv_status_T get_lval_dict_item(lval_T *lp, char *name, char *key, int len, char **key_end, typval_T *var1, int flags, bool unlet, typval_T *rettv) { bool quiet = flags & GLV_QUIET; @@ -1738,7 +1738,7 @@ static char *get_lval_subscript(lval_T *lp, char *p, char *name, typval_T *rettv } if (lp->ll_tv->v_type == VAR_DICT) { - glv_status_T glv_status = get_lval_dict_item(name, lp, key, len, &p, &var1, + glv_status_T glv_status = get_lval_dict_item(lp, name, key, len, &p, &var1, flags, unlet, rettv); if (glv_status == GLV_FAIL) { goto done; @@ -2950,6 +2950,93 @@ static int eval_addlist(typval_T *tv1, typval_T *tv2) return OK; } +/// Concatenate strings "tv1" and "tv2" and store the result in "tv1". +static int eval_concat_str(typval_T *tv1, typval_T *tv2) +{ + char buf1[NUMBUFLEN]; + char buf2[NUMBUFLEN]; + // s1 already checked + const char *const s1 = tv_get_string_buf(tv1, buf1); + const char *const s2 = tv_get_string_buf_chk(tv2, buf2); + if (s2 == NULL) { // Type error? + tv_clear(tv1); + tv_clear(tv2); + return FAIL; + } + + char *p = concat_str(s1, s2); + tv_clear(tv1); + tv1->v_type = VAR_STRING; + tv1->vval.v_string = p; + + return OK; +} + +/// Add or subtract numbers "tv1" and "tv2" and store the result in "tv1". +/// The numbers can be whole numbers or floats. +static int eval_addsub_number(typval_T *tv1, typval_T *tv2, int op) +{ + bool error = false; + varnumber_T n1, n2; + float_T f1 = 0; + float_T f2 = 0; + + if (tv1->v_type == VAR_FLOAT) { + f1 = tv1->vval.v_float; + n1 = 0; + } else { + n1 = tv_get_number_chk(tv1, &error); + if (error) { + // This can only happen for "list + non-list" or + // "blob + non-blob". For "non-list + ..." or + // "something - ...", we returned before evaluating the + // 2nd operand. + tv_clear(tv1); + tv_clear(tv2); + return FAIL; + } + if (tv2->v_type == VAR_FLOAT) { + f1 = (float_T)n1; + } + } + if (tv2->v_type == VAR_FLOAT) { + f2 = tv2->vval.v_float; + n2 = 0; + } else { + n2 = tv_get_number_chk(tv2, &error); + if (error) { + tv_clear(tv1); + tv_clear(tv2); + return FAIL; + } + if (tv1->v_type == VAR_FLOAT) { + f2 = (float_T)n2; + } + } + tv_clear(tv1); + + // If there is a float on either side the result is a float. + if (tv1->v_type == VAR_FLOAT || tv2->v_type == VAR_FLOAT) { + if (op == '+') { + f1 = f1 + f2; + } else { + f1 = f1 - f2; + } + tv1->v_type = VAR_FLOAT; + tv1->vval.v_float = f1; + } else { + if (op == '+') { + n1 = n1 + n2; + } else { + n1 = n1 - n2; + } + tv1->v_type = VAR_NUMBER; + tv1->vval.v_number = n1; + } + + return OK; +} + /// Handle fourth level expression: /// + number addition, concatenation of list or blob /// - number subtraction @@ -3005,20 +3092,9 @@ static int eval5(char **arg, typval_T *rettv, evalarg_T *const evalarg) if (evaluate) { // Compute the result. if (op == '.') { - char buf1[NUMBUFLEN]; - char buf2[NUMBUFLEN]; - // s1 already checked - const char *const s1 = tv_get_string_buf(rettv, buf1); - const char *const s2 = tv_get_string_buf_chk(&var2, buf2); - if (s2 == NULL) { // Type error? - tv_clear(rettv); - tv_clear(&var2); + if (eval_concat_str(rettv, &var2) == FAIL) { return FAIL; } - char *p = concat_str(s1, s2); - tv_clear(rettv); - rettv->v_type = VAR_STRING; - rettv->vval.v_string = p; } else if (op == '+' && rettv->v_type == VAR_BLOB && var2.v_type == VAR_BLOB) { eval_addblob(rettv, &var2); } else if (op == '+' && rettv->v_type == VAR_LIST && var2.v_type == VAR_LIST) { @@ -3026,62 +3102,8 @@ static int eval5(char **arg, typval_T *rettv, evalarg_T *const evalarg) return FAIL; } } else { - bool error = false; - varnumber_T n1, n2; - float_T f1 = 0; - float_T f2 = 0; - - if (rettv->v_type == VAR_FLOAT) { - f1 = rettv->vval.v_float; - n1 = 0; - } else { - n1 = tv_get_number_chk(rettv, &error); - if (error) { - // This can only happen for "list + non-list" or - // "blob + non-blob". For "non-list + ..." or - // "something - ...", we returned before evaluating the - // 2nd operand. - tv_clear(rettv); - tv_clear(&var2); - return FAIL; - } - if (var2.v_type == VAR_FLOAT) { - f1 = (float_T)n1; - } - } - if (var2.v_type == VAR_FLOAT) { - f2 = var2.vval.v_float; - n2 = 0; - } else { - n2 = tv_get_number_chk(&var2, &error); - if (error) { - tv_clear(rettv); - tv_clear(&var2); - return FAIL; - } - if (rettv->v_type == VAR_FLOAT) { - f2 = (float_T)n2; - } - } - tv_clear(rettv); - - // If there is a float on either side the result is a float. - if (rettv->v_type == VAR_FLOAT || var2.v_type == VAR_FLOAT) { - if (op == '+') { - f1 = f1 + f2; - } else { - f1 = f1 - f2; - } - rettv->v_type = VAR_FLOAT; - rettv->vval.v_float = f1; - } else { - if (op == '+') { - n1 = n1 + n2; - } else { - n1 = n1 - n2; - } - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = n1; + if (eval_addsub_number(rettv, &var2, op) == FAIL) { + return FAIL; } } tv_clear(&var2); @@ -3090,6 +3112,85 @@ static int eval5(char **arg, typval_T *rettv, evalarg_T *const evalarg) return OK; } +/// Multiply or divide or compute the modulo of numbers "tv1" and "tv2" and +/// store the result in "tv1". The numbers can be whole numbers or floats. +static int eval_multdiv_number(typval_T *tv1, typval_T *tv2, int op) + FUNC_ATTR_NO_SANITIZE_UNDEFINED +{ + varnumber_T n1, n2; + bool use_float = false; + + float_T f1 = 0; + float_T f2 = 0; + bool error = false; + if (tv1->v_type == VAR_FLOAT) { + f1 = tv1->vval.v_float; + use_float = true; + n1 = 0; + } else { + n1 = tv_get_number_chk(tv1, &error); + } + tv_clear(tv1); + if (error) { + tv_clear(tv2); + return FAIL; + } + + if (tv2->v_type == VAR_FLOAT) { + if (!use_float) { + f1 = (float_T)n1; + use_float = true; + } + f2 = tv2->vval.v_float; + n2 = 0; + } else { + n2 = tv_get_number_chk(tv2, &error); + tv_clear(tv2); + if (error) { + return FAIL; + } + if (use_float) { + f2 = (float_T)n2; + } + } + + // Compute the result. + // When either side is a float the result is a float. + if (use_float) { + if (op == '*') { + f1 = f1 * f2; + } else if (op == '/') { + // uncrustify:off + + // Division by zero triggers error from AddressSanitizer + f1 = (f2 == 0 ? ( +#ifdef NAN + f1 == 0 ? (float_T)NAN : +#endif + (f1 > 0 ? (float_T)INFINITY : (float_T)-INFINITY)) : f1 / f2); + + // uncrustify:on + } else { + emsg(_("E804: Cannot use '%' with Float")); + return FAIL; + } + tv1->v_type = VAR_FLOAT; + tv1->vval.v_float = f1; + } else { + if (op == '*') { + n1 = n1 * n2; + } else if (op == '/') { + n1 = num_divide(n1, n2); + } else { + n1 = num_modulus(n1, n2); + } + tv1->v_type = VAR_NUMBER; + tv1->vval.v_number = n1; + } + + return OK; +} + /// Handle fifth level expression: /// - * number multiplication /// - / number division @@ -3103,10 +3204,7 @@ static int eval5(char **arg, typval_T *rettv, evalarg_T *const evalarg) /// float /// @return OK or FAIL. static int eval6(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool want_string) - FUNC_ATTR_NO_SANITIZE_UNDEFINED { - bool use_float = false; - // Get the first variable. if (eval7(arg, rettv, evalarg, want_string) == FAIL) { return FAIL; @@ -3119,26 +3217,7 @@ static int eval6(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan break; } - varnumber_T n1, n2; - float_T f1 = 0; - float_T f2 = 0; - bool error = false; const bool evaluate = evalarg == NULL ? 0 : (evalarg->eval_flags & EVAL_EVALUATE); - if (evaluate) { - if (rettv->v_type == VAR_FLOAT) { - f1 = rettv->vval.v_float; - use_float = true; - n1 = 0; - } else { - n1 = tv_get_number_chk(rettv, &error); - } - tv_clear(rettv); - if (error) { - return FAIL; - } - } else { - n1 = 0; - } // Get the second variable. *arg = skipwhite(*arg + 1); @@ -3148,56 +3227,9 @@ static int eval6(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan } if (evaluate) { - if (var2.v_type == VAR_FLOAT) { - if (!use_float) { - f1 = (float_T)n1; - use_float = true; - } - f2 = var2.vval.v_float; - n2 = 0; - } else { - n2 = tv_get_number_chk(&var2, &error); - tv_clear(&var2); - if (error) { - return FAIL; - } - if (use_float) { - f2 = (float_T)n2; - } - } - // Compute the result. - // When either side is a float the result is a float. - if (use_float) { - if (op == '*') { - f1 = f1 * f2; - } else if (op == '/') { - // uncrustify:off - - // Division by zero triggers error from AddressSanitizer - f1 = (f2 == 0 ? ( -#ifdef NAN - f1 == 0 ? (float_T)NAN : -#endif - (f1 > 0 ? (float_T)INFINITY : (float_T)-INFINITY)) : f1 / f2); - - // uncrustify:on - } else { - emsg(_("E804: Cannot use '%' with Float")); - return FAIL; - } - rettv->v_type = VAR_FLOAT; - rettv->vval.v_float = f1; - } else { - if (op == '*') { - n1 = n1 * n2; - } else if (op == '/') { - n1 = num_divide(n1, n2); - } else { - n1 = num_modulus(n1, n2); - } - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = n1; + if (eval_multdiv_number(rettv, &var2, op) == FAIL) { + return FAIL; } } } @@ -3301,14 +3333,9 @@ static int eval7(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan ret = eval_list(arg, rettv, evalarg); break; - // Dictionary: #{key: val, key: val} + // Literal Dictionary: #{key: val, key: val} case '#': - if ((*arg)[1] == '{') { - (*arg)++; - ret = eval_dict(arg, rettv, evalarg, true); - } else { - ret = NOTDONE; - } + ret = eval_lit_dict(arg, rettv, evalarg); break; // Lambda: {arg, arg -> expr} @@ -5274,6 +5301,24 @@ failret: return OK; } +/// Evaluate a literal dictionary: #{key: val, key: val} +/// "*arg" points to the "#". +/// On return, "*arg" points to the character after the Dict. +/// Return OK or FAIL. Returns NOTDONE for {expr}. +static int eval_lit_dict(char **arg, typval_T *rettv, evalarg_T *const evalarg) +{ + int ret = OK; + + if ((*arg)[1] == '{') { + (*arg)++; + ret = eval_dict(arg, rettv, evalarg, true); + } else { + ret = NOTDONE; + } + + return ret; +} + /// Convert the string to a floating point number /// /// This uses strtod(). setlocale(LC_NUMERIC, "C") has been used earlier to |