aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/eval
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/eval')
-rw-r--r--src/nvim/eval/funcs.c4
-rw-r--r--src/nvim/eval/typval.c31
-rw-r--r--src/nvim/eval/userfunc.c72
-rw-r--r--src/nvim/eval/userfunc.h1
-rw-r--r--src/nvim/eval/vars.c102
5 files changed, 137 insertions, 73 deletions
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 7f224f371c..23d0242ce4 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -1550,7 +1550,7 @@ static void f_eval(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
const char *const expr_start = s;
- if (s == NULL || eval1((char **)&s, rettv, true) == FAIL) {
+ if (s == NULL || eval1((char **)&s, rettv, &EVALARG_EVALUATE) == FAIL) {
if (expr_start != NULL && !aborting()) {
semsg(_(e_invexpr2), expr_start);
}
@@ -1722,7 +1722,7 @@ static void f_exists(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
xfree(exp);
}
} else if (*p == '&' || *p == '+') { // Option.
- n = (get_option_tv(&p, NULL, true) == OK);
+ n = (eval_option(&p, NULL, true) == OK);
if (*skipwhite(p) != NUL) {
n = false; // Trailing garbage.
}
diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c
index 3e67571053..91be41751e 100644
--- a/src/nvim/eval/typval.c
+++ b/src/nvim/eval/typval.c
@@ -4201,3 +4201,34 @@ const char *tv_get_string_buf(const typval_T *const tv, char *const buf)
return res != NULL ? res : "";
}
+
+/// Return true when "tv" is not falsy: non-zero, non-empty string, non-empty
+/// list, etc. Mostly like what JavaScript does, except that empty list and
+/// empty dictionary are false.
+bool tv2bool(const typval_T *const tv)
+{
+ switch (tv->v_type) {
+ case VAR_NUMBER:
+ return tv->vval.v_number != 0;
+ case VAR_FLOAT:
+ return tv->vval.v_float != 0.0;
+ case VAR_PARTIAL:
+ return tv->vval.v_partial != NULL;
+ case VAR_FUNC:
+ case VAR_STRING:
+ return tv->vval.v_string != NULL && *tv->vval.v_string != NUL;
+ case VAR_LIST:
+ return tv->vval.v_list != NULL && tv->vval.v_list->lv_len > 0;
+ case VAR_DICT:
+ return tv->vval.v_dict != NULL && tv->vval.v_dict->dv_hashtab.ht_used > 0;
+ case VAR_BOOL:
+ return tv->vval.v_bool == kBoolVarTrue;
+ case VAR_SPECIAL:
+ return tv->vval.v_special == kSpecialVarNull;
+ case VAR_BLOB:
+ return tv->vval.v_blob != NULL && tv->vval.v_blob->bv_ga.ga_len > 0;
+ case VAR_UNKNOWN:
+ break;
+ }
+ return false;
+}
diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c
index f09da6b79b..178f9fd6b6 100644
--- a/src/nvim/eval/userfunc.c
+++ b/src/nvim/eval/userfunc.c
@@ -71,6 +71,8 @@ static const char *e_funcref = N_("E718: Funcref required");
static const char *e_nofunc = N_("E130: Unknown function: %s");
static const char e_no_white_space_allowed_before_str_str[]
= N_("E1068: No white space allowed before '%s': %s");
+static const char e_missing_heredoc_end_marker_str[]
+ = N_("E1145: Missing heredoc end marker: %s");
void func_init(void)
{
@@ -152,7 +154,7 @@ static int get_function_args(char **argp, char endchar, garray_T *newargs, int *
p = skipwhite(p) + 1;
p = skipwhite(p);
char *expr = p;
- if (eval1(&p, &rettv, false) != FAIL) {
+ if (eval1(&p, &rettv, NULL) != FAIL) {
ga_grow(default_args, 1);
// trim trailing whitespace
@@ -252,22 +254,23 @@ static void set_ufunc_name(ufunc_T *fp, char *name)
/// Parse a lambda expression and get a Funcref from "*arg".
///
/// @return OK or FAIL. Returns NOTDONE for dict or {expr}.
-int get_lambda_tv(char **arg, typval_T *rettv, bool evaluate)
+int get_lambda_tv(char **arg, typval_T *rettv, evalarg_T *evalarg)
{
+ const bool evaluate = evalarg != NULL && (evalarg->eval_flags & EVAL_EVALUATE);
garray_T newargs = GA_EMPTY_INIT_VALUE;
garray_T *pnewargs;
ufunc_T *fp = NULL;
partial_T *pt = NULL;
int varargs;
int ret;
- char *start = skipwhite(*arg + 1);
- char *s, *e;
bool *old_eval_lavars = eval_lavars_used;
bool eval_lavars = false;
+ char *tofree = NULL;
// First, check if this is a lambda expression. "->" must exists.
- ret = get_function_args(&start, '-', NULL, NULL, NULL, true);
- if (ret == FAIL || *start != '>') {
+ char *s = skipwhite(*arg + 1);
+ ret = get_function_args(&s, '-', NULL, NULL, NULL, true);
+ if (ret == FAIL || *s != '>') {
return NOTDONE;
}
@@ -290,12 +293,18 @@ int get_lambda_tv(char **arg, typval_T *rettv, bool evaluate)
// Get the start and the end of the expression.
*arg = skipwhite((*arg) + 1);
- s = *arg;
- ret = skip_expr(arg);
+ char *start = *arg;
+ ret = skip_expr(arg, evalarg);
+ char *end = *arg;
if (ret == FAIL) {
goto errret;
}
- e = *arg;
+ if (evalarg != NULL) {
+ // avoid that the expression gets freed when another line break follows
+ tofree = evalarg->eval_tofree;
+ evalarg->eval_tofree = NULL;
+ }
+
*arg = skipwhite(*arg);
if (**arg != '}') {
semsg(_("E451: Expected }: %s"), *arg);
@@ -317,11 +326,11 @@ int get_lambda_tv(char **arg, typval_T *rettv, bool evaluate)
ga_grow(&newlines, 1);
// Add "return " before the expression.
- size_t len = (size_t)(7 + e - s + 1);
+ size_t len = (size_t)(7 + end - start + 1);
p = xmalloc(len);
((char **)(newlines.ga_data))[newlines.ga_len++] = p;
STRCPY(p, "return ");
- xstrlcpy(p + 7, s, (size_t)(e - s) + 1);
+ xstrlcpy(p + 7, start, (size_t)(end - start) + 1);
if (strstr(p + 7, "a:") == NULL) {
// No a: variables are used for sure.
flags |= FC_NOARGS;
@@ -359,12 +368,22 @@ int get_lambda_tv(char **arg, typval_T *rettv, bool evaluate)
}
eval_lavars_used = old_eval_lavars;
+ if (evalarg != NULL && evalarg->eval_tofree == NULL) {
+ evalarg->eval_tofree = tofree;
+ } else {
+ xfree(tofree);
+ }
return OK;
errret:
ga_clear_strings(&newargs);
xfree(fp);
xfree(pt);
+ if (evalarg != NULL && evalarg->eval_tofree == NULL) {
+ evalarg->eval_tofree = tofree;
+ } else {
+ xfree(tofree);
+ }
eval_lavars_used = old_eval_lavars;
return FAIL;
}
@@ -448,7 +467,8 @@ void emsg_funcname(const char *errmsg, const char *name)
/// @param funcexe various values
///
/// @return OK or FAIL.
-int get_func_tv(const char *name, int len, typval_T *rettv, char **arg, funcexe_T *funcexe)
+int get_func_tv(const char *name, int len, typval_T *rettv, char **arg, evalarg_T *const evalarg,
+ funcexe_T *funcexe)
{
char *argp;
int ret = OK;
@@ -463,7 +483,7 @@ int get_func_tv(const char *name, int len, typval_T *rettv, char **arg, funcexe_
if (*argp == ')' || *argp == ',' || *argp == NUL) {
break;
}
- if (eval1(&argp, &argvars[argcount], funcexe->fe_evaluate) == FAIL) {
+ if (eval1(&argp, &argvars[argcount], evalarg) == FAIL) {
ret = FAIL;
break;
}
@@ -972,7 +992,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
default_expr = ((char **)(fp->uf_def_args.ga_data))
[ai + fp->uf_def_args.ga_len];
- if (eval1(&default_expr, &def_rettv, true) == FAIL) {
+ if (eval1(&default_expr, &def_rettv, &EVALARG_EVALUATE) == FAIL) {
default_arg_err = true;
break;
}
@@ -1109,7 +1129,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
// A Lambda always has the command "return {expr}". It is much faster
// to evaluate {expr} directly.
ex_nesting_level++;
- (void)eval1(&p, rettv, true);
+ (void)eval1(&p, rettv, &EVALARG_EVALUATE);
ex_nesting_level--;
} else {
// call do_cmdline() to execute the lines
@@ -2301,7 +2321,11 @@ void ex_function(exarg_T *eap)
lines_left = Rows - 1;
}
if (theline == NULL) {
- emsg(_("E126: Missing :endfunction"));
+ if (skip_until != NULL) {
+ semsg(_(e_missing_heredoc_end_marker_str), skip_until);
+ } else {
+ emsg(_("E126: Missing :endfunction"));
+ }
goto erret;
}
if (show_block) {
@@ -2947,13 +2971,15 @@ void ex_return(exarg_T *eap)
return;
}
+ evalarg_T evalarg = { .eval_flags = eap->skip ? 0 : EVAL_EVALUATE };
+
if (eap->skip) {
emsg_skip++;
}
eap->nextcmd = NULL;
if ((*arg != NUL && *arg != '|' && *arg != '\n')
- && eval0(arg, &rettv, &eap->nextcmd, !eap->skip) != FAIL) {
+ && eval0(arg, &rettv, eap, &evalarg) != FAIL) {
if (!eap->skip) {
returning = do_return(eap, false, true, &rettv);
} else {
@@ -2982,6 +3008,7 @@ void ex_return(exarg_T *eap)
if (eap->skip) {
emsg_skip--;
}
+ clear_evalarg(&evalarg, eap);
}
/// ":1,25call func(arg1, arg2)" function call.
@@ -2998,16 +3025,19 @@ void ex_call(exarg_T *eap)
bool failed = false;
funcdict_T fudi;
partial_T *partial = NULL;
+ evalarg_T evalarg;
+ fill_evalarg_from_eap(&evalarg, eap, eap->skip);
if (eap->skip) {
// trans_function_name() doesn't work well when skipping, use eval0()
// instead to skip to any following command, e.g. for:
// :if 0 | call dict.foo().bar() | endif.
emsg_skip++;
- if (eval0(eap->arg, &rettv, &eap->nextcmd, false) != FAIL) {
+ if (eval0(eap->arg, &rettv, eap, &evalarg) != FAIL) {
tv_clear(&rettv);
}
emsg_skip--;
+ clear_evalarg(&evalarg, eap);
return;
}
@@ -3065,14 +3095,13 @@ void ex_call(exarg_T *eap)
funcexe.fe_evaluate = true;
funcexe.fe_partial = partial;
funcexe.fe_selfdict = fudi.fd_dict;
- if (get_func_tv(name, -1, &rettv, &arg, &funcexe) == FAIL) {
+ if (get_func_tv(name, -1, &rettv, &arg, &evalarg, &funcexe) == FAIL) {
failed = true;
break;
}
// Handle a function returning a Funcref, Dictionary or List.
- if (handle_subscript((const char **)&arg, &rettv, true, true)
- == FAIL) {
+ if (handle_subscript((const char **)&arg, &rettv, &EVALARG_EVALUATE, true) == FAIL) {
failed = true;
break;
}
@@ -3104,6 +3133,7 @@ void ex_call(exarg_T *eap)
eap->nextcmd = check_nextcmd(arg);
}
}
+ clear_evalarg(&evalarg, eap);
end:
tv_dict_unref(fudi.fd_dict);
diff --git a/src/nvim/eval/userfunc.h b/src/nvim/eval/userfunc.h
index c8583f232c..f0e1f5dca0 100644
--- a/src/nvim/eval/userfunc.h
+++ b/src/nvim/eval/userfunc.h
@@ -4,6 +4,7 @@
#include <stdbool.h>
#include <stddef.h>
+#include "nvim/eval.h"
#include "nvim/eval/typval.h"
#include "nvim/eval/typval_defs.h"
#include "nvim/ex_cmds_defs.h"
diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c
index 701f190a06..b8a8f39437 100644
--- a/src/nvim/eval/vars.c
+++ b/src/nvim/eval/vars.c
@@ -175,25 +175,16 @@ static list_T *heredoc_get(exarg_T *eap, char *cmd)
/// ":let var ..= expr" assignment command.
/// ":let [var1, var2] = expr" unpack list.
/// ":let [name, ..., ; lastname] = expr" unpack list.
-void ex_let(exarg_T *eap)
-{
- ex_let_const(eap, false);
-}
-
+///
/// ":cons[t] var = expr1" define constant
/// ":cons[t] [name1, name2, ...] = expr1" define constants unpacking list
/// ":cons[t] [name, ..., ; lastname] = expr" define constants unpacking list
-void ex_const(exarg_T *eap)
-{
- ex_let_const(eap, true);
-}
-
-static void ex_let_const(exarg_T *eap, const bool is_const)
+void ex_let(exarg_T *eap)
{
+ const bool is_const = eap->cmdidx == CMD_const;
char *arg = eap->arg;
char *expr = NULL;
typval_T rettv;
- int i;
int var_count = 0;
int semicolon = 0;
char op[2];
@@ -208,8 +199,10 @@ static void ex_let_const(exarg_T *eap, const bool is_const)
argend--;
}
expr = skipwhite(argend);
- if (*expr != '=' && !((vim_strchr("+-*/%.", (uint8_t)(*expr)) != NULL
- && expr[1] == '=') || strncmp(expr, "..=", 3) == 0)) {
+ bool concat = strncmp(expr, "..=", 3) == 0;
+ bool has_assign = *expr == '=' || (vim_strchr("+-*/%.", (uint8_t)(*expr)) != NULL
+ && expr[1] == '=');
+ if (!has_assign && !concat) {
// ":let" without "=": list variables
if (*arg == '[') {
emsg(_(e_invarg));
@@ -227,7 +220,10 @@ static void ex_let_const(exarg_T *eap, const bool is_const)
list_vim_vars(&first);
}
eap->nextcmd = check_nextcmd(arg);
- } else if (expr[0] == '=' && expr[1] == '<' && expr[2] == '<') {
+ return;
+ }
+
+ if (expr[0] == '=' && expr[1] == '<' && expr[2] == '<') {
// HERE document
list_T *l = heredoc_get(eap, expr + 3);
if (l != NULL) {
@@ -239,36 +235,43 @@ static void ex_let_const(exarg_T *eap, const bool is_const)
}
tv_clear(&rettv);
}
- } else {
- op[0] = '=';
- op[1] = NUL;
- if (*expr != '=') {
- if (vim_strchr("+-*/%.", (uint8_t)(*expr)) != NULL) {
- op[0] = *expr; // +=, -=, *=, /=, %= or .=
- if (expr[0] == '.' && expr[1] == '.') { // ..=
- expr++;
- }
- }
- expr += 2;
- } else {
- expr += 1;
- }
+ return;
+ }
- expr = skipwhite(expr);
+ rettv.v_type = VAR_UNKNOWN;
- if (eap->skip) {
- emsg_skip++;
- }
- i = eval0(expr, &rettv, &eap->nextcmd, !eap->skip);
- if (eap->skip) {
- if (i != FAIL) {
- tv_clear(&rettv);
+ op[0] = '=';
+ op[1] = NUL;
+ if (*expr != '=') {
+ if (vim_strchr("+-*/%.", (uint8_t)(*expr)) != NULL) {
+ op[0] = *expr; // +=, -=, *=, /=, %= or .=
+ if (expr[0] == '.' && expr[1] == '.') { // ..=
+ expr++;
}
- emsg_skip--;
- } else if (i != FAIL) {
- (void)ex_let_vars(eap->arg, &rettv, false, semicolon, var_count, is_const, op);
- tv_clear(&rettv);
}
+ expr += 2;
+ } else {
+ expr += 1;
+ }
+
+ expr = skipwhite(expr);
+
+ if (eap->skip) {
+ emsg_skip++;
+ }
+ evalarg_T evalarg;
+ fill_evalarg_from_eap(&evalarg, eap, eap->skip);
+ int eval_res = eval0(expr, &rettv, eap, &evalarg);
+ if (eap->skip) {
+ emsg_skip--;
+ }
+ clear_evalarg(&evalarg, eap);
+
+ if (!eap->skip && eval_res != FAIL) {
+ (void)ex_let_vars(eap->arg, &rettv, false, semicolon, var_count, is_const, op);
+ }
+ if (eval_res != FAIL) {
+ tv_clear(&rettv);
}
}
@@ -500,13 +503,12 @@ static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first)
if (tofree != NULL) {
name = tofree;
}
- if (get_var_tv(name, len, &tv, NULL, true, false)
- == FAIL) {
+ if (eval_variable(name, len, &tv, NULL, true, false) == FAIL) {
error = true;
} else {
// handle d.key, l[idx], f(expr)
const char *const arg_subsc = arg;
- if (handle_subscript(&arg, &tv, true, true) == FAIL) {
+ if (handle_subscript(&arg, &tv, &EVALARG_EVALUATE, true) == FAIL) {
error = true;
} else {
if (arg == arg_subsc && len == 2 && name[1] == ':') {
@@ -1073,8 +1075,8 @@ static int do_lock_var(lval_T *lp, char *name_end FUNC_ATTR_UNUSED, exarg_T *eap
/// @param dip non-NULL when typval's dict item is needed
/// @param verbose may give error message
/// @param no_autoload do not use script autoloading
-int get_var_tv(const char *name, int len, typval_T *rettv, dictitem_T **dip, bool verbose,
- bool no_autoload)
+int eval_variable(const char *name, int len, typval_T *rettv, dictitem_T **dip, bool verbose,
+ bool no_autoload)
{
int ret = OK;
typval_T *tv = NULL;
@@ -1149,7 +1151,7 @@ void vars_clear_ext(hashtab_T *ht, int free_val)
}
}
hash_clear(ht);
- ht->ht_used = 0;
+ hash_init(ht);
}
/// Delete a variable from hashtab "ht" at item "hi".
@@ -1561,7 +1563,7 @@ static void get_var_from(const char *varname, typval_T *rettv, typval_T *deftv,
tv_dict_set_ret(rettv, opts);
done = true;
}
- } else if (get_option_tv(&varname, rettv, true) == OK) {
+ } else if (eval_option(&varname, rettv, true) == OK) {
// Local option
done = true;
}
@@ -1710,10 +1712,10 @@ bool var_exists(const char *var)
if (tofree != NULL) {
name = tofree;
}
- n = get_var_tv(name, len, &tv, NULL, false, true) == OK;
+ n = eval_variable(name, len, &tv, NULL, false, true) == OK;
if (n) {
// Handle d.key, l[idx], f(expr).
- n = handle_subscript(&var, &tv, true, false) == OK;
+ n = handle_subscript(&var, &tv, &EVALARG_EVALUATE, false) == OK;
if (n) {
tv_clear(&tv);
}