diff options
| author | Josh Rahm <joshuarahm@gmail.com> | 2024-11-19 22:57:13 +0000 |
|---|---|---|
| committer | Josh Rahm <joshuarahm@gmail.com> | 2024-11-19 22:57:13 +0000 |
| commit | 9be89f131f87608f224f0ee06d199fcd09d32176 (patch) | |
| tree | 11022dcfa9e08cb4ac5581b16734196128688d48 /src/nvim/eval.c | |
| parent | ff7ed8f586589d620a806c3758fac4a47a8e7e15 (diff) | |
| parent | 88085c2e80a7e3ac29aabb6b5420377eed99b8b6 (diff) | |
| download | rneovim-9be89f131f87608f224f0ee06d199fcd09d32176.tar.gz rneovim-9be89f131f87608f224f0ee06d199fcd09d32176.tar.bz2 rneovim-9be89f131f87608f224f0ee06d199fcd09d32176.zip | |
Merge remote-tracking branch 'upstream/master' into mix_20240309
Diffstat (limited to 'src/nvim/eval.c')
| -rw-r--r-- | src/nvim/eval.c | 1706 |
1 files changed, 707 insertions, 999 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 64883e69a2..8682139b32 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -6,7 +6,6 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <sys/stat.h> #include <uv.h> #include "auto/config.h" @@ -21,9 +20,9 @@ #include "nvim/channel.h" #include "nvim/charset.h" #include "nvim/cmdexpand_defs.h" -#include "nvim/cmdhist.h" #include "nvim/cursor.h" #include "nvim/edit.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/encode.h" #include "nvim/eval/executor.h" @@ -33,18 +32,15 @@ #include "nvim/eval/vars.h" #include "nvim/event/loop.h" #include "nvim/event/multiqueue.h" -#include "nvim/event/process.h" +#include "nvim/event/proc.h" #include "nvim/event/time.h" #include "nvim/ex_cmds.h" #include "nvim/ex_docmd.h" #include "nvim/ex_eval.h" -#include "nvim/ex_getln.h" #include "nvim/garray.h" #include "nvim/garray_defs.h" -#include "nvim/getchar.h" #include "nvim/gettext_defs.h" #include "nvim/globals.h" -#include "nvim/grid_defs.h" #include "nvim/hashtab.h" #include "nvim/highlight_group.h" #include "nvim/insexpand.h" @@ -66,14 +62,11 @@ #include "nvim/option.h" #include "nvim/option_vars.h" #include "nvim/optionstr.h" -#include "nvim/os/fileio.h" #include "nvim/os/fs.h" -#include "nvim/os/fs_defs.h" #include "nvim/os/lang.h" #include "nvim/os/os.h" #include "nvim/os/os_defs.h" #include "nvim/os/shell.h" -#include "nvim/os/stdpaths_defs.h" #include "nvim/path.h" #include "nvim/pos_defs.h" #include "nvim/profile.h" @@ -82,14 +75,9 @@ #include "nvim/regexp_defs.h" #include "nvim/runtime.h" #include "nvim/runtime_defs.h" -#include "nvim/search.h" #include "nvim/strings.h" #include "nvim/tag.h" #include "nvim/types_defs.h" -#include "nvim/ui.h" -#include "nvim/ui_compositor.h" -#include "nvim/ui_defs.h" -#include "nvim/usercmd.h" #include "nvim/version.h" #include "nvim/vim_defs.h" #include "nvim/window.h" @@ -106,7 +94,6 @@ static const char e_cannot_index_special_variable[] = N_("E909: Cannot index a special variable"); static const char *e_nowhitespace = N_("E274: No white space allowed before parenthesis"); -static const char *e_write2 = N_("E80: Error while writing: %s"); static const char e_cannot_index_a_funcref[] = N_("E695: Cannot index a Funcref"); static const char e_variable_nested_too_deep_for_making_copy[] @@ -121,6 +108,8 @@ static const char e_empty_function_name[] = N_("E1192: Empty function name"); static const char e_argument_of_str_must_be_list_string_dictionary_or_blob[] = N_("E1250: Argument of %s must be a List, String, Dictionary or Blob"); +static const char e_cannot_use_partial_here[] + = N_("E1265: Cannot use a partial here"); static char * const namespace_char = "abglstvw"; @@ -151,6 +140,12 @@ typedef struct { int fi_byte_idx; // byte index in fi_string } forinfo_T; +typedef enum { + GLV_FAIL, + GLV_OK, + GLV_STOP, +} glv_status_T; + // values for vv_flags: #define VV_COMPAT 1 // compatible, also used without "v:" #define VV_RO 2 // read-only @@ -330,7 +325,6 @@ static const char *const msgpack_type_names[] = { [kMPInteger] = "integer", [kMPFloat] = "float", [kMPString] = "string", - [kMPBinary] = "binary", [kMPArray] = "array", [kMPMap] = "map", [kMPExt] = "ext", @@ -341,7 +335,6 @@ const list_T *eval_msgpack_type_lists[] = { [kMPInteger] = NULL, [kMPFloat] = NULL, [kMPString] = NULL, - [kMPBinary] = NULL, [kMPArray] = NULL, [kMPMap] = NULL, [kMPExt] = NULL, @@ -696,7 +689,7 @@ int eval_charconvert(const char *const enc_from, const char *const enc_to, } bool err = false; - if (eval_to_bool(p_ccv, &err, NULL, false)) { + if (eval_to_bool(p_ccv, &err, NULL, false, true)) { err = true; } @@ -725,7 +718,7 @@ void eval_diff(const char *const origfile, const char *const newfile, const char } // errors are ignored - typval_T *tv = eval_expr(p_dex, NULL); + typval_T *tv = eval_expr_ext(p_dex, NULL, true); tv_free(tv); set_vim_var_string(VV_FNAME_IN, NULL, -1); @@ -747,7 +740,7 @@ void eval_patch(const char *const origfile, const char *const difffile, const ch } // errors are ignored - typval_T *tv = eval_expr(p_pex, NULL); + typval_T *tv = eval_expr_ext(p_pex, NULL, true); tv_free(tv); set_vim_var_string(VV_FNAME_IN, NULL, -1); @@ -776,7 +769,8 @@ void fill_evalarg_from_eap(evalarg_T *evalarg, exarg_T *eap, bool skip) /// @param skip only parse, don't execute /// /// @return true or false. -bool eval_to_bool(char *arg, bool *error, exarg_T *eap, bool skip) +bool eval_to_bool(char *arg, bool *error, exarg_T *eap, const bool skip, + const bool use_simple_function) { typval_T tv; bool retval = false; @@ -787,7 +781,9 @@ bool eval_to_bool(char *arg, bool *error, exarg_T *eap, bool skip) if (skip) { emsg_skip++; } - if (eval0(arg, &tv, eap, &evalarg) == FAIL) { + int r = use_simple_function ? eval0_simple_funccal(arg, &tv, eap, &evalarg) + : eval0(arg, &tv, eap, &evalarg); + if (r == FAIL) { *error = true; } else { *error = false; @@ -840,6 +836,80 @@ bool eval_expr_valid_arg(const typval_T *const tv) && (tv->v_type != VAR_STRING || (tv->vval.v_string != NULL && *tv->vval.v_string != NUL)); } +/// Evaluate a partial. +/// Pass arguments "argv[argc]". +/// Return the result in "rettv" and OK or FAIL. +static int eval_expr_partial(const typval_T *expr, typval_T *argv, int argc, typval_T *rettv) + FUNC_ATTR_NONNULL_ALL +{ + partial_T *const partial = expr->vval.v_partial; + if (partial == NULL) { + return FAIL; + } + + const char *const s = partial_name(partial); + if (s == NULL || *s == NUL) { + return FAIL; + } + + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.fe_evaluate = true; + funcexe.fe_partial = partial; + if (call_func(s, -1, rettv, argc, argv, &funcexe) == FAIL) { + return FAIL; + } + + return OK; +} + +/// Evaluate an expression which is a function. +/// Pass arguments "argv[argc]". +/// Return the result in "rettv" and OK or FAIL. +static int eval_expr_func(const typval_T *expr, typval_T *argv, int argc, typval_T *rettv) + FUNC_ATTR_NONNULL_ALL +{ + char buf[NUMBUFLEN]; + const char *const s = (expr->v_type == VAR_FUNC + ? expr->vval.v_string + : tv_get_string_buf_chk(expr, buf)); + if (s == NULL || *s == NUL) { + return FAIL; + } + + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.fe_evaluate = true; + if (call_func(s, -1, rettv, argc, argv, &funcexe) == FAIL) { + return FAIL; + } + + return OK; +} + +/// Evaluate an expression, which is a string. +/// Return the result in "rettv" and OK or FAIL. +static int eval_expr_string(const typval_T *expr, typval_T *rettv) + FUNC_ATTR_NONNULL_ALL +{ + char buf[NUMBUFLEN]; + char *s = (char *)tv_get_string_buf_chk(expr, buf); + if (s == NULL) { + return FAIL; + } + + s = skipwhite(s); + if (eval1_emsg(&s, rettv, NULL) == FAIL) { + return FAIL; + } + + if (*skipwhite(s) != NUL) { // check for trailing chars after expr + tv_clear(rettv); + semsg(_(e_invexpr2), s); + return FAIL; + } + + return OK; +} + /// Evaluate an expression, which can be a function, partial or string. /// Pass arguments "argv[argc]". /// Return the result in "rettv" and OK or FAIL. @@ -849,49 +919,14 @@ int eval_expr_typval(const typval_T *expr, bool want_func, typval_T *argv, int a typval_T *rettv) FUNC_ATTR_NONNULL_ALL { - char buf[NUMBUFLEN]; - funcexe_T funcexe = FUNCEXE_INIT; - if (expr->v_type == VAR_PARTIAL) { - partial_T *const partial = expr->vval.v_partial; - if (partial == NULL) { - return FAIL; - } - const char *const s = partial_name(partial); - if (s == NULL || *s == NUL) { - return FAIL; - } - funcexe.fe_evaluate = true; - funcexe.fe_partial = partial; - if (call_func(s, -1, rettv, argc, argv, &funcexe) == FAIL) { - return FAIL; - } + return eval_expr_partial(expr, argv, argc, rettv); } else if (expr->v_type == VAR_FUNC || want_func) { - const char *const s = (expr->v_type == VAR_FUNC - ? expr->vval.v_string - : tv_get_string_buf_chk(expr, buf)); - if (s == NULL || *s == NUL) { - return FAIL; - } - funcexe.fe_evaluate = true; - if (call_func(s, -1, rettv, argc, argv, &funcexe) == FAIL) { - return FAIL; - } + return eval_expr_func(expr, argv, argc, rettv); } else { - char *s = (char *)tv_get_string_buf_chk(expr, buf); - if (s == NULL) { - return FAIL; - } - s = skipwhite(s); - if (eval1_emsg(&s, rettv, NULL) == FAIL) { - return FAIL; - } - if (*skipwhite(s) != NUL) { // check for trailing chars after expr - tv_clear(rettv); - semsg(_(e_invexpr2), s); - return FAIL; - } + return eval_expr_string(expr, rettv); } + return OK; } @@ -996,14 +1031,17 @@ static char *typval2string(typval_T *tv, bool join_list) /// @param join_list when true convert a List into a sequence of lines. /// /// @return pointer to allocated memory, or NULL for failure. -char *eval_to_string_eap(char *arg, bool join_list, exarg_T *eap) +char *eval_to_string_eap(char *arg, const bool join_list, exarg_T *eap, + const bool use_simple_function) { typval_T tv; char *retval; evalarg_T evalarg; fill_evalarg_from_eap(&evalarg, eap, eap != NULL && eap->skip); - if (eval0(arg, &tv, NULL, &evalarg) == FAIL) { + int r = use_simple_function ? eval0_simple_funccal(arg, &tv, NULL, &evalarg) + : eval0(arg, &tv, NULL, &evalarg); + if (r == FAIL) { retval = NULL; } else { retval = typval2string(&tv, join_list); @@ -1014,16 +1052,16 @@ char *eval_to_string_eap(char *arg, bool join_list, exarg_T *eap) return retval; } -char *eval_to_string(char *arg, bool join_list) +char *eval_to_string(char *arg, const bool join_list, const bool use_simple_function) { - return eval_to_string_eap(arg, join_list, NULL); + return eval_to_string_eap(arg, join_list, NULL, use_simple_function); } /// Call eval_to_string() without using current local variables and using /// textlock. /// /// @param use_sandbox when true, use the sandbox. -char *eval_to_string_safe(char *arg, const bool use_sandbox) +char *eval_to_string_safe(char *arg, const bool use_sandbox, const bool use_simple_function) { char *retval; funccal_entry_T funccal_entry; @@ -1033,7 +1071,7 @@ char *eval_to_string_safe(char *arg, const bool use_sandbox) sandbox++; } textlock++; - retval = eval_to_string(arg, false); + retval = eval_to_string(arg, false, use_simple_function); if (use_sandbox) { sandbox--; } @@ -1046,15 +1084,22 @@ char *eval_to_string_safe(char *arg, const bool use_sandbox) /// Evaluates "expr" silently. /// /// @return -1 for an error. -varnumber_T eval_to_number(char *expr) +varnumber_T eval_to_number(char *expr, const bool use_simple_function) { typval_T rettv; varnumber_T retval; char *p = skipwhite(expr); + int r = NOTDONE; emsg_off++; - if (eval1(&p, &rettv, &EVALARG_EVALUATE) == FAIL) { + if (use_simple_function) { + r = may_call_simple_func(expr, &rettv); + } + if (r == NOTDONE) { + r = eval1(&p, &rettv, &EVALARG_EVALUATE); + } + if (r == FAIL) { retval = -1; } else { retval = tv_get_number_chk(&rettv, NULL); @@ -1071,12 +1116,26 @@ varnumber_T eval_to_number(char *expr) /// NULL when there is an error. typval_T *eval_expr(char *arg, exarg_T *eap) { + return eval_expr_ext(arg, eap, false); +} + +static typval_T *eval_expr_ext(char *arg, exarg_T *eap, const bool use_simple_function) +{ typval_T *tv = xmalloc(sizeof(*tv)); evalarg_T evalarg; fill_evalarg_from_eap(&evalarg, eap, eap != NULL && eap->skip); - if (eval0(arg, tv, eap, &evalarg) == FAIL) { + int r = NOTDONE; + + if (use_simple_function) { + r = eval0_simple_funccal(arg, tv, eap, &evalarg); + } + if (r == NOTDONE) { + r = eval0(arg, tv, eap, &evalarg); + } + + if (r == FAIL) { XFREE_CLEAR(tv); } @@ -1162,7 +1221,11 @@ list_T *eval_spell_expr(char *badword, char *expr) current_sctx = *ctx; } - if (eval1(&p, &rettv, &EVALARG_EVALUATE) == OK) { + int r = may_call_simple_func(p, &rettv); + if (r == NOTDONE) { + r = eval1(&p, &rettv, &EVALARG_EVALUATE); + } + if (r == OK) { if (rettv.v_type != VAR_LIST) { tv_clear(&rettv); } else { @@ -1303,7 +1366,7 @@ int eval_foldexpr(win_T *wp, int *cp) const sctx_T saved_sctx = current_sctx; const bool use_sandbox = was_set_insecurely(wp, kOptFoldexpr, OPT_LOCAL); - char *arg = wp->w_p_fde; + char *arg = skipwhite(wp->w_p_fde); current_sctx = wp->w_p_script_ctx[WV_FDE].script_ctx; emsg_off++; @@ -1315,7 +1378,9 @@ int eval_foldexpr(win_T *wp, int *cp) typval_T tv; varnumber_T retval; - if (eval0(arg, &tv, NULL, &EVALARG_EVALUATE) == FAIL) { + // Evaluate the expression. If the expression is "FuncName()" call the + // function directly. + if (eval0_simple_funccal(arg, &tv, NULL, &EVALARG_EVALUATE) == FAIL) { retval = 0; } else { // If the result is a number, just return the number. @@ -1361,7 +1426,7 @@ Object eval_foldtext(win_T *wp) typval_T tv; Object retval; - if (eval0(arg, &tv, NULL, &EVALARG_EVALUATE) == FAIL) { + if (eval0_simple_funccal(arg, &tv, NULL, &EVALARG_EVALUATE) == FAIL) { retval = STRING_OBJ(NULL_STRING); } else { if (tv.v_type == VAR_LIST) { @@ -1382,22 +1447,232 @@ Object eval_foldtext(win_T *wp) return retval; } +/// Find the end of a variable or function name. Unlike find_name_end() this +/// does not recognize magic braces. +/// When "use_namespace" is true recognize "b:", "s:", etc. +/// Return a pointer to just after the name. Equal to "arg" if there is no +/// valid name. +static const char *to_name_end(const char *arg, bool use_namespace) +{ + // Quick check for valid starting character. + if (!eval_isnamec1(*arg)) { + return arg; + } + + const char *p; + for (p = arg + 1; *p != NUL && eval_isnamec(*p); MB_PTR_ADV(p)) { + // Include a namespace such as "s:var" and "v:var". But "n:" is not + // and can be used in slice "[n:]". + if (*p == ':' && (p != arg + 1 + || !use_namespace + || vim_strchr("bgstvw", *arg) == NULL)) { + break; + } + } + return p; +} + +/// Get an Dict lval variable that can be assigned a value to: "name", +/// "name[expr]", "name[expr][expr]", "name.key", "name.key[expr]" etc. +/// "name" points to the start of the name. +/// If "rettv" is not NULL it points to the value to be assigned. +/// "unlet" is true for ":unlet": slightly different behavior when something is +/// wrong; must end in space or cmd separator. +/// +/// flags: +/// GLV_QUIET: do not give error messages +/// GLV_READ_ONLY: will not change the variable +/// GLV_NO_AUTOLOAD: do not use script autoloading +/// +/// 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(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; + char *p = *key_end; + + if (len == -1) { + // "[key]": get key from "var1" + key = (char *)tv_get_string(var1); // is number or string + } + lp->ll_list = NULL; + + // a NULL dict is equivalent with an empty dict + if (lp->ll_tv->vval.v_dict == NULL) { + lp->ll_tv->vval.v_dict = tv_dict_alloc(); + lp->ll_tv->vval.v_dict->dv_refcount++; + } + lp->ll_dict = lp->ll_tv->vval.v_dict; + + lp->ll_di = tv_dict_find(lp->ll_dict, key, len); + + // When assigning to a scope dictionary check that a function and + // variable name is valid (only variable name unless it is l: or + // g: dictionary). Disallow overwriting a builtin function. + if (rettv != NULL && lp->ll_dict->dv_scope != 0) { + char prevval; + if (len != -1) { + prevval = key[len]; + key[len] = NUL; + } else { + prevval = 0; // Avoid compiler warning. + } + bool wrong = ((lp->ll_dict->dv_scope == VAR_DEF_SCOPE + && tv_is_func(*rettv) + && var_wrong_func_name(key, lp->ll_di == NULL)) + || !valid_varname(key)); + if (len != -1) { + key[len] = prevval; + } + if (wrong) { + return GLV_FAIL; + } + } + + if (lp->ll_di != NULL && tv_is_luafunc(&lp->ll_di->di_tv) + && len == -1 && rettv == NULL) { + semsg(e_illvar, "v:['lua']"); + return GLV_FAIL; + } + + if (lp->ll_di == NULL) { + // Can't add "v:" or "a:" variable. + if (lp->ll_dict == &vimvardict + || &lp->ll_dict->dv_hashtab == get_funccal_args_ht()) { + semsg(_(e_illvar), name); + return GLV_FAIL; + } + + // Key does not exist in dict: may need to add it. + if (*p == '[' || *p == '.' || unlet) { + if (!quiet) { + semsg(_(e_dictkey), key); + } + return GLV_FAIL; + } + if (len == -1) { + lp->ll_newkey = xstrdup(key); + } else { + lp->ll_newkey = xmemdupz(key, (size_t)len); + } + *key_end = p; + return GLV_STOP; + // existing variable, need to check if it can be changed + } else if (!(flags & GLV_READ_ONLY) + && (var_check_ro(lp->ll_di->di_flags, name, (size_t)(p - name)) + || var_check_lock(lp->ll_di->di_flags, name, (size_t)(p - name)))) { + return GLV_FAIL; + } + + lp->ll_tv = &lp->ll_di->di_tv; + + return GLV_OK; +} + +/// Get an blob lval variable that can be assigned a value to: "name", +/// "na{me}", "name[expr]", "name[expr:expr]", "name[expr][expr]", etc. +/// +/// 'var1' specifies the starting blob index and 'var2' specifies the ending +/// blob index. If the first index is not specified in a range, then 'empty1' +/// is true. If 'quiet' is true, then error messages are not displayed for +/// invalid indexes. +/// +/// The blob is returned in 'lp'. Returns OK on success and FAIL on failure. +static int get_lval_blob(lval_T *lp, typval_T *var1, typval_T *var2, bool empty1, bool quiet) +{ + const int bloblen = tv_blob_len(lp->ll_tv->vval.v_blob); + + // Get the number and item for the only or first index of the List. + if (empty1) { + lp->ll_n1 = 0; + } else { + // Is number or string. + lp->ll_n1 = (int)tv_get_number(var1); + } + + if (tv_blob_check_index(bloblen, lp->ll_n1, quiet) == FAIL) { + return FAIL; + } + if (lp->ll_range && !lp->ll_empty2) { + lp->ll_n2 = (int)tv_get_number(var2); + if (tv_blob_check_range(bloblen, lp->ll_n1, lp->ll_n2, quiet) == FAIL) { + return FAIL; + } + } + + lp->ll_blob = lp->ll_tv->vval.v_blob; + lp->ll_tv = NULL; + + return OK; +} + +/// Get a List lval variable that can be assigned a value to: "name", +/// "na{me}", "name[expr]", "name[expr:expr]", "name[expr][expr]", etc. +/// +/// 'var1' specifies the starting List index and 'var2' specifies the ending +/// List index. If the first index is not specified in a range, then 'empty1' +/// is true. If 'quiet' is true, then error messages are not displayed for +/// invalid indexes. +/// +/// The List is returned in 'lp'. Returns OK on success and FAIL on failure. +static int get_lval_list(lval_T *lp, typval_T *var1, typval_T *var2, bool empty1, int flags, + bool quiet) +{ + // Get the number and item for the only or first index of the List. + if (empty1) { + lp->ll_n1 = 0; + } else { + // Is number or string. + lp->ll_n1 = (int)tv_get_number(var1); + } + + lp->ll_dict = NULL; + lp->ll_list = lp->ll_tv->vval.v_list; + lp->ll_li = tv_list_check_range_index_one(lp->ll_list, &lp->ll_n1, quiet); + if (lp->ll_li == NULL) { + return FAIL; + } + + // May need to find the item or absolute index for the second + // index of a range. + // When no index given: "lp->ll_empty2" is true. + // Otherwise "lp->ll_n2" is set to the second index. + if (lp->ll_range && !lp->ll_empty2) { + lp->ll_n2 = (int)tv_get_number(var2); // Is number or string. + if (tv_list_check_range_index_two(lp->ll_list, + &lp->ll_n1, lp->ll_li, + &lp->ll_n2, quiet) == FAIL) { + return FAIL; + } + } + + lp->ll_tv = TV_LIST_ITEM_TV(lp->ll_li); + + return OK; +} + /// Get the lval of a list/dict/blob subitem starting at "p". Loop /// until no more [idx] or .key is following. /// +/// If "rettv" is not NULL it points to the value to be assigned. +/// "unlet" is true for ":unlet". +/// /// @param[in] flags @see GetLvalFlags. /// /// @return A pointer to the character after the subscript on success or NULL on /// failure. static char *get_lval_subscript(lval_T *lp, char *p, char *name, typval_T *rettv, hashtab_T *ht, - dictitem_T *v, int unlet, int flags) + dictitem_T *v, bool unlet, int flags) { - int quiet = flags & GLV_QUIET; + bool quiet = flags & GLV_QUIET; typval_T var1; var1.v_type = VAR_UNKNOWN; typval_T var2; var2.v_type = VAR_UNKNOWN; bool empty1 = false; + int rc = FAIL; // Loop until no more [idx] or .key is following. while (*p == '[' || (*p == '.' && p[1] != '=' && p[1] != '.')) { @@ -1427,7 +1702,7 @@ static char *get_lval_subscript(lval_T *lp, char *p, char *name, typval_T *rettv if (!quiet) { emsg(_("E708: [:] must come last")); } - return NULL; + goto done; } int len = -1; @@ -1451,12 +1726,11 @@ static char *get_lval_subscript(lval_T *lp, char *p, char *name, typval_T *rettv } else { empty1 = false; if (eval1(&p, &var1, &EVALARG_EVALUATE) == FAIL) { // Recursive! - return NULL; + goto done; } if (!tv_check_str(&var1)) { // Not a number or string. - tv_clear(&var1); - return NULL; + goto done; } p = skipwhite(p); } @@ -1467,8 +1741,7 @@ static char *get_lval_subscript(lval_T *lp, char *p, char *name, typval_T *rettv if (!quiet) { emsg(_(e_cannot_slice_dictionary)); } - tv_clear(&var1); - return NULL; + goto done; } if (rettv != NULL && !(rettv->v_type == VAR_LIST && rettv->vval.v_list != NULL) @@ -1476,8 +1749,7 @@ static char *get_lval_subscript(lval_T *lp, char *p, char *name, typval_T *rettv if (!quiet) { emsg(_("E709: [:] requires a List or Blob value")); } - tv_clear(&var1); - return NULL; + goto done; } p = skipwhite(p + 1); if (*p == ']') { @@ -1486,14 +1758,11 @@ static char *get_lval_subscript(lval_T *lp, char *p, char *name, typval_T *rettv lp->ll_empty2 = false; // Recursive! if (eval1(&p, &var2, &EVALARG_EVALUATE) == FAIL) { - tv_clear(&var1); - return NULL; + goto done; } if (!tv_check_str(&var2)) { // Not a number or string. - tv_clear(&var1); - tv_clear(&var2); - return NULL; + goto done; } } lp->ll_range = true; @@ -1505,9 +1774,7 @@ static char *get_lval_subscript(lval_T *lp, char *p, char *name, typval_T *rettv if (!quiet) { emsg(_(e_missbrac)); } - tv_clear(&var1); - tv_clear(&var2); - return NULL; + goto done; } // Skip to past ']'. @@ -1515,142 +1782,37 @@ static char *get_lval_subscript(lval_T *lp, char *p, char *name, typval_T *rettv } if (lp->ll_tv->v_type == VAR_DICT) { - if (len == -1) { - // "[key]": get key from "var1" - key = (char *)tv_get_string(&var1); // is number or string - } - lp->ll_list = NULL; - lp->ll_dict = lp->ll_tv->vval.v_dict; - lp->ll_di = tv_dict_find(lp->ll_dict, key, len); - - // When assigning to a scope dictionary check that a function and - // variable name is valid (only variable name unless it is l: or - // g: dictionary). Disallow overwriting a builtin function. - if (rettv != NULL && lp->ll_dict->dv_scope != 0) { - char prevval; - if (len != -1) { - prevval = key[len]; - key[len] = NUL; - } else { - prevval = 0; // Avoid compiler warning. - } - bool wrong = ((lp->ll_dict->dv_scope == VAR_DEF_SCOPE - && tv_is_func(*rettv) - && var_wrong_func_name(key, lp->ll_di == NULL)) - || !valid_varname(key)); - if (len != -1) { - key[len] = prevval; - } - if (wrong) { - tv_clear(&var1); - return NULL; - } - } - - if (lp->ll_di != NULL && tv_is_luafunc(&lp->ll_di->di_tv) - && len == -1 && rettv == NULL) { - tv_clear(&var1); - semsg(e_illvar, "v:['lua']"); - return NULL; + 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; } - - if (lp->ll_di == NULL) { - // Can't add "v:" or "a:" variable. - if (lp->ll_dict == &vimvardict - || &lp->ll_dict->dv_hashtab == get_funccal_args_ht()) { - semsg(_(e_illvar), name); - tv_clear(&var1); - return NULL; - } - - // Key does not exist in dict: may need to add it. - if (*p == '[' || *p == '.' || unlet) { - if (!quiet) { - semsg(_(e_dictkey), key); - } - tv_clear(&var1); - return NULL; - } - if (len == -1) { - lp->ll_newkey = xstrdup(key); - } else { - lp->ll_newkey = xmemdupz(key, (size_t)len); - } - tv_clear(&var1); + if (glv_status == GLV_STOP) { break; - // existing variable, need to check if it can be changed - } else if (!(flags & GLV_READ_ONLY) - && (var_check_ro(lp->ll_di->di_flags, name, (size_t)(p - name)) - || var_check_lock(lp->ll_di->di_flags, name, (size_t)(p - name)))) { - tv_clear(&var1); - return NULL; } - - tv_clear(&var1); - lp->ll_tv = &lp->ll_di->di_tv; } else if (lp->ll_tv->v_type == VAR_BLOB) { - // Get the number and item for the only or first index of the List. - if (empty1) { - lp->ll_n1 = 0; - } else { - // Is number or string. - lp->ll_n1 = (int)tv_get_number(&var1); + if (get_lval_blob(lp, &var1, &var2, empty1, quiet) == FAIL) { + goto done; } - tv_clear(&var1); - - const int bloblen = tv_blob_len(lp->ll_tv->vval.v_blob); - if (tv_blob_check_index(bloblen, lp->ll_n1, quiet) == FAIL) { - tv_clear(&var2); - return NULL; - } - if (lp->ll_range && !lp->ll_empty2) { - lp->ll_n2 = (int)tv_get_number(&var2); - tv_clear(&var2); - if (tv_blob_check_range(bloblen, lp->ll_n1, lp->ll_n2, quiet) == FAIL) { - return NULL; - } - } - lp->ll_blob = lp->ll_tv->vval.v_blob; - lp->ll_tv = NULL; break; } else { - // Get the number and item for the only or first index of the List. - if (empty1) { - lp->ll_n1 = 0; - } else { - // Is number or string. - lp->ll_n1 = (int)tv_get_number(&var1); - } - tv_clear(&var1); - - lp->ll_dict = NULL; - lp->ll_list = lp->ll_tv->vval.v_list; - lp->ll_li = tv_list_check_range_index_one(lp->ll_list, &lp->ll_n1, quiet); - if (lp->ll_li == NULL) { - tv_clear(&var2); - return NULL; - } - - // May need to find the item or absolute index for the second - // index of a range. - // When no index given: "lp->ll_empty2" is true. - // Otherwise "lp->ll_n2" is set to the second index. - if (lp->ll_range && !lp->ll_empty2) { - lp->ll_n2 = (int)tv_get_number(&var2); // Is number or string. - tv_clear(&var2); - if (tv_list_check_range_index_two(lp->ll_list, - &lp->ll_n1, lp->ll_li, - &lp->ll_n2, quiet) == FAIL) { - return NULL; - } + if (get_lval_list(lp, &var1, &var2, empty1, flags, quiet) == FAIL) { + goto done; } - - lp->ll_tv = TV_LIST_ITEM_TV(lp->ll_li); } + + tv_clear(&var1); + tv_clear(&var2); + var1.v_type = VAR_UNKNOWN; + var2.v_type = VAR_UNKNOWN; } + rc = OK; + +done: tv_clear(&var1); - return p; + tv_clear(&var2); + return rc == OK ? p : NULL; } /// Get an lvalue @@ -2381,9 +2543,10 @@ void clear_evalarg(evalarg_T *evalarg, exarg_T *eap) } } -/// The "evaluate" argument: When false, the argument is only parsed but not -/// executed. The function may return OK, but the rettv will be of type -/// VAR_UNKNOWN. The function still returns FAIL for a syntax error. +/// The "eval" functions have an "evalarg" argument: When NULL or +/// "evalarg->eval_flags" does not have EVAL_EVALUATE, then the argument is only +/// parsed but not executed. The functions may return OK, but the rettv will be +/// of type VAR_UNKNOWN. The functions still returns FAIL for a syntax error. /// Handle zero level expression. /// This calls eval1() and handles error message and nextcmd. @@ -2442,6 +2605,42 @@ int eval0(char *arg, typval_T *rettv, exarg_T *eap, evalarg_T *const evalarg) return ret; } +/// If "arg" is a simple function call without arguments then call it and return +/// the result. Otherwise return NOTDONE. +static int may_call_simple_func(const char *arg, typval_T *rettv) +{ + const char *parens = strstr(arg, "()"); + int r = NOTDONE; + + // If the expression is "FuncName()" then we can skip a lot of overhead. + if (parens != NULL && *skipwhite(parens + 2) == NUL) { + if (strnequal(arg, "v:lua.", 6)) { + const char *p = arg + 6; + if (p != parens && skip_luafunc_name(p) == parens) { + r = call_simple_luafunc(p, (size_t)(parens - p), rettv); + } + } else { + const char *p = strncmp(arg, "<SNR>", 5) == 0 ? skipdigits(arg + 5) : arg; + if (to_name_end(p, true) == parens) { + r = call_simple_func(arg, (size_t)(parens - arg), rettv); + } + } + } + return r; +} + +/// Handle zero level expression with optimization for a simple function call. +/// Same arguments and return value as eval0(). +static int eval0_simple_funccal(char *arg, typval_T *rettv, exarg_T *eap, evalarg_T *const evalarg) +{ + int r = may_call_simple_func(arg, rettv); + + if (r == NOTDONE) { + r = eval0(arg, rettv, eap, evalarg); + } + return r; +} + /// Handle top level expression: /// expr2 ? expr1 : expr1 /// expr2 ?? expr1 @@ -2832,6 +3031,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; |