} forinfo_T;
static int tv_op(typval_T *tv1, typval_T *tv2, char_u *op);
-static int eval2(char_u **arg, typval_T *rettv, int evaluate);
-static int eval3(char_u **arg, typval_T *rettv, int evaluate);
-static int eval4(char_u **arg, typval_T *rettv, int evaluate);
-static int eval5(char_u **arg, typval_T *rettv, int evaluate);
-static int eval6(char_u **arg, typval_T *rettv, int evaluate, int want_string);
-static int eval7(char_u **arg, typval_T *rettv, int evaluate, int want_string);
+static int eval2(char_u **arg, typval_T *rettv, int flags);
+static int eval3(char_u **arg, typval_T *rettv, int flags);
+static int eval4(char_u **arg, typval_T *rettv, int flags);
+static int eval5(char_u **arg, typval_T *rettv, int flags);
+static int eval6(char_u **arg, typval_T *rettv, int flags, int want_string);
+static int eval7(char_u **arg, typval_T *rettv, int flags, int want_string);
static int eval7_leader(typval_T *rettv, char_u *start_leader, char_u **end_leaderp);
static int free_unref_items(int copyID);
if (skip)
++emsg_skip;
- if (eval0(arg, &tv, nextcmd, !skip) == FAIL)
+ if (eval0(arg, &tv, nextcmd, skip ? 0 : EVAL_EVALUATE) == FAIL)
*error = TRUE;
else
{
int did_emsg_before = did_emsg;
int called_emsg_before = called_emsg;
- ret = eval1(arg, rettv, evaluate);
+ ret = eval1(arg, rettv, evaluate ? EVAL_EVALUATE : 0);
if (ret == FAIL)
{
// Report the invalid expression unless the expression evaluation has
if (skip)
++emsg_skip;
- if (eval0(arg, &tv, nextcmd, !skip) == FAIL || skip)
+ if (eval0(arg, &tv, nextcmd, skip ? 0 : EVAL_EVALUATE) == FAIL || skip)
retval = NULL;
else
{
typval_T rettv;
*pp = skipwhite(*pp);
- return eval1(pp, &rettv, FALSE);
+ return eval1(pp, &rettv, 0);
}
/*
char_u numbuf[NUMBUFLEN];
#endif
- if (eval0(arg, &tv, nextcmd, TRUE) == FAIL)
+ if (eval0(arg, &tv, nextcmd, EVAL_EVALUATE) == FAIL)
retval = NULL;
else
{
++emsg_off;
- if (eval1(&p, &rettv, TRUE) == FAIL)
+ if (eval1(&p, &rettv, EVAL_EVALUATE) == FAIL)
retval = -1;
else
{
typval_T *tv;
tv = ALLOC_ONE(typval_T);
- if (tv != NULL && eval0(arg, tv, nextcmd, TRUE) == FAIL)
+ if (tv != NULL && eval0(arg, tv, nextcmd, EVAL_EVALUATE) == FAIL)
VIM_CLEAR(tv);
return tv;
++sandbox;
++textwinlock;
*cp = NUL;
- if (eval0(arg, &tv, NULL, TRUE) == FAIL)
+ if (eval0(arg, &tv, NULL, EVAL_EVALUATE) == FAIL)
retval = 0;
else
{
else
{
empty1 = FALSE;
- if (eval1(&p, &var1, TRUE) == FAIL) // recursive!
+ if (eval1(&p, &var1, EVAL_EVALUATE) == FAIL) // recursive!
return NULL;
if (tv_get_string_chk(&var1) == NULL)
{
else
{
lp->ll_empty2 = FALSE;
- if (eval1(&p, &var2, TRUE) == FAIL) // recursive!
+ // recursive!
+ if (eval1(&p, &var2, EVAL_EVALUATE) == FAIL)
{
clear_tv(&var1);
return NULL;
if (skip)
++emsg_skip;
- if (eval0(skipwhite(expr + 2), &tv, nextcmdp, !skip) == OK)
+ if (eval0(skipwhite(expr + 2), &tv, nextcmdp, skip ? 0 : EVAL_EVALUATE)
+ == OK)
{
*errp = FALSE;
if (!skip)
char_u *name,
int name_len,
typval_T *rettv,
- int evaluate,
+ int flags,
typval_T *basetv) // "expr" for "expr->name(arg)"
{
+ int evaluate = flags & EVAL_EVALUATE;
char_u *s = name;
int len = name_len;
partial_T *partial;
// Need to make a copy, in case evaluating the arguments makes
// the name invalid.
s = vim_strsave(s);
- if (s == NULL)
+ if (s == NULL || (flags & EVAL_CONSTANT))
ret = FAIL;
else
{
* This calls eval1() and handles error message and nextcmd.
* Put the result in "rettv" when returning OK and "evaluate" is TRUE.
* Note: "rettv.v_lock" is not set.
+ * "flags" has EVAL_EVALUATE and similar flags.
* Return OK or FAIL.
*/
int
char_u *arg,
typval_T *rettv,
char_u **nextcmd,
- int evaluate)
+ int flags)
{
int ret;
char_u *p;
int called_emsg_before = called_emsg;
p = skipwhite(arg);
- ret = eval1(&p, rettv, evaluate);
+ ret = eval1(&p, rettv, flags);
if (ret == FAIL || !ends_excmd2(arg, p))
{
if (ret != FAIL)
* exception, or we already gave a more specific error.
* Also check called_emsg for when using assert_fails().
*/
- if (!aborting() && did_emsg == did_emsg_before
- && called_emsg == called_emsg_before)
+ if (!aborting()
+ && did_emsg == did_emsg_before
+ && called_emsg == called_emsg_before
+ && (flags & EVAL_CONSTANT) == 0)
semsg(_(e_invexpr2), arg);
ret = FAIL;
}
* Return OK or FAIL.
*/
int
-eval1(char_u **arg, typval_T *rettv, int evaluate)
+eval1(char_u **arg, typval_T *rettv, int flags)
{
int result;
typval_T var2;
/*
* Get the first variable.
*/
- if (eval2(arg, rettv, evaluate) == FAIL)
+ if (eval2(arg, rettv, flags) == FAIL)
return FAIL;
if ((*arg)[0] == '?')
{
+ int evaluate = flags & EVAL_EVALUATE;
+
result = FALSE;
- if (evaluate)
+ if (flags & EVAL_EVALUATE)
{
int error = FALSE;
}
/*
- * Get the second variable.
+ * Get the second variable. Recursive!
*/
*arg = skipwhite(*arg + 1);
- if (eval1(arg, rettv, evaluate && result) == FAIL) // recursive!
+ if (eval1(arg, rettv, result ? flags : flags & ~EVAL_EVALUATE) == FAIL)
return FAIL;
/*
}
/*
- * Get the third variable.
+ * Get the third variable. Recursive!
*/
*arg = skipwhite(*arg + 1);
- if (eval1(arg, &var2, evaluate && !result) == FAIL) // recursive!
+ if (eval1(arg, &var2, !result ? flags : flags & ~EVAL_EVALUATE) == FAIL)
{
if (evaluate && result)
clear_tv(rettv);
* Return OK or FAIL.
*/
static int
-eval2(char_u **arg, typval_T *rettv, int evaluate)
+eval2(char_u **arg, typval_T *rettv, int flags)
{
typval_T var2;
long result;
/*
* Get the first variable.
*/
- if (eval3(arg, rettv, evaluate) == FAIL)
+ if (eval3(arg, rettv, flags) == FAIL)
return FAIL;
/*
result = FALSE;
while ((*arg)[0] == '|' && (*arg)[1] == '|')
{
+ int evaluate = flags & EVAL_EVALUATE;
+
if (evaluate && first)
{
if (tv_get_number_chk(rettv, &error) != 0)
* Get the second variable.
*/
*arg = skipwhite(*arg + 2);
- if (eval3(arg, &var2, evaluate && !result) == FAIL)
+ if (eval3(arg, &var2, !result ? flags : flags & ~EVAL_EVALUATE)
+ == FAIL)
return FAIL;
/*
* Return OK or FAIL.
*/
static int
-eval3(char_u **arg, typval_T *rettv, int evaluate)
+eval3(char_u **arg, typval_T *rettv, int flags)
{
typval_T var2;
long result;
/*
* Get the first variable.
*/
- if (eval4(arg, rettv, evaluate) == FAIL)
+ if (eval4(arg, rettv, flags) == FAIL)
return FAIL;
/*
result = TRUE;
while ((*arg)[0] == '&' && (*arg)[1] == '&')
{
+ int evaluate = flags & EVAL_EVALUATE;
+
if (evaluate && first)
{
if (tv_get_number_chk(rettv, &error) == 0)
* Get the second variable.
*/
*arg = skipwhite(*arg + 2);
- if (eval4(arg, &var2, evaluate && result) == FAIL)
+ if (eval4(arg, &var2, result ? flags : flags & ~EVAL_EVALUATE) == FAIL)
return FAIL;
/*
* Return OK or FAIL.
*/
static int
-eval4(char_u **arg, typval_T *rettv, int evaluate)
+eval4(char_u **arg, typval_T *rettv, int flags)
{
typval_T var2;
char_u *p;
/*
* Get the first variable.
*/
- if (eval5(arg, rettv, evaluate) == FAIL)
+ if (eval5(arg, rettv, flags) == FAIL)
return FAIL;
p = *arg;
* Get the second variable.
*/
*arg = skipwhite(p + len);
- if (eval5(arg, &var2, evaluate) == FAIL)
+ if (eval5(arg, &var2, flags) == FAIL)
{
clear_tv(rettv);
return FAIL;
}
- if (evaluate)
+ if (flags & EVAL_EVALUATE)
{
int ret = typval_compare(rettv, &var2, type, ic);
* Return OK or FAIL.
*/
static int
-eval5(char_u **arg, typval_T *rettv, int evaluate)
+eval5(char_u **arg, typval_T *rettv, int flags)
{
typval_T var2;
int op;
/*
* Get the first variable.
*/
- if (eval6(arg, rettv, evaluate, FALSE) == FAIL)
+ if (eval6(arg, rettv, flags, FALSE) == FAIL)
return FAIL;
/*
// we know that the first operand needs to be a string or number
// without evaluating the 2nd operand. So check before to avoid
// side effects after an error.
- if (evaluate && tv_get_string_chk(rettv) == NULL)
+ if ((flags & EVAL_EVALUATE) && tv_get_string_chk(rettv) == NULL)
{
clear_tv(rettv);
return FAIL;
if (op == '.' && *(*arg + 1) == '.') // .. string concatenation
++*arg;
*arg = skipwhite(*arg + 1);
- if (eval6(arg, &var2, evaluate, op == '.') == FAIL)
+ if (eval6(arg, &var2, flags, op == '.') == FAIL)
{
clear_tv(rettv);
return FAIL;
}
- if (evaluate)
+ if (flags & EVAL_EVALUATE)
{
/*
* Compute the result.
eval6(
char_u **arg,
typval_T *rettv,
- int evaluate,
+ int flags,
int want_string) // after "." operator
{
typval_T var2;
/*
* Get the first variable.
*/
- if (eval7(arg, rettv, evaluate, want_string) == FAIL)
+ if (eval7(arg, rettv, flags, want_string) == FAIL)
return FAIL;
/*
if (op != '*' && op != '/' && op != '%')
break;
- if (evaluate)
+ if (flags & EVAL_EVALUATE)
{
#ifdef FEAT_FLOAT
if (rettv->v_type == VAR_FLOAT)
* Get the second variable.
*/
*arg = skipwhite(*arg + 1);
- if (eval7(arg, &var2, evaluate, FALSE) == FAIL)
+ if (eval7(arg, &var2, flags, FALSE) == FAIL)
return FAIL;
- if (evaluate)
+ if (flags & EVAL_EVALUATE)
{
#ifdef FEAT_FLOAT
if (var2.v_type == VAR_FLOAT)
eval7(
char_u **arg,
typval_T *rettv,
- int evaluate,
+ int flags,
int want_string) // after "." operator
{
+ int evaluate = flags & EVAL_EVALUATE;
int len;
char_u *s;
char_u *start_leader, *end_leader;
/*
* List: [expr, expr]
*/
- case '[': ret = get_list_tv(arg, rettv, evaluate, TRUE);
+ case '[': ret = get_list_tv(arg, rettv, flags, TRUE);
break;
/*
case '#': if ((*arg)[1] == '{')
{
++*arg;
- ret = eval_dict(arg, rettv, evaluate, TRUE);
+ ret = eval_dict(arg, rettv, flags, TRUE);
}
else
ret = NOTDONE;
*/
case '{': ret = get_lambda_tv(arg, rettv, evaluate);
if (ret == NOTDONE)
- ret = eval_dict(arg, rettv, evaluate, FALSE);
+ ret = eval_dict(arg, rettv, flags, FALSE);
break;
/*
* nested expression: (expression).
*/
case '(': *arg = skipwhite(*arg + 1);
- ret = eval1(arg, rettv, evaluate); // recursive!
+ ret = eval1(arg, rettv, flags); // recursive!
if (**arg == ')')
++*arg;
else if (ret == OK)
else
{
if (**arg == '(') // recursive!
- ret = eval_func(arg, s, len, rettv, evaluate, NULL);
+ ret = eval_func(arg, s, len, rettv, flags, NULL);
else if (evaluate)
ret = get_var_tv(s, len, rettv, NULL, TRUE, FALSE);
else
// Handle following '[', '(' and '.' for expr[expr], expr.name,
// expr(expr), expr->name(expr)
if (ret == OK)
- ret = handle_subscript(arg, rettv, evaluate, TRUE,
+ ret = handle_subscript(arg, rettv, flags, TRUE,
start_leader, &end_leader);
/*
ret = FAIL;
}
else
- ret = eval_func(arg, name, len, rettv, evaluate, &base);
+ ret = eval_func(arg, name, len, rettv,
+ evaluate ? EVAL_EVALUATE : 0, &base);
}
// Clear the funcref afterwards, so that deleting it while
eval_index(
char_u **arg,
typval_T *rettv,
- int evaluate,
+ int flags,
int verbose) // give error messages
{
+ int evaluate = flags & EVAL_EVALUATE;
int empty1 = FALSE, empty2 = FALSE;
typval_T var1, var2;
long i;
*arg = skipwhite(*arg + 1);
if (**arg == ':')
empty1 = TRUE;
- else if (eval1(arg, &var1, evaluate) == FAIL) // recursive!
+ else if (eval1(arg, &var1, flags) == FAIL) // recursive!
return FAIL;
else if (evaluate && tv_get_string_chk(&var1) == NULL)
{
*arg = skipwhite(*arg + 1);
if (**arg == ']')
empty2 = TRUE;
- else if (eval1(arg, &var2, evaluate) == FAIL) // recursive!
+ else if (eval1(arg, &var2, flags) == FAIL) // recursive!
{
if (!empty1)
clear_tv(&var1);
handle_subscript(
char_u **arg,
typval_T *rettv,
- int evaluate, // do more than finding the end
+ int flags, // do more than finding the end
int verbose, // give error messages
char_u *start_leader, // start of '!' and '-' prefixes
char_u **end_leaderp) // end of '!' and '-' prefixes
{
+ int evaluate = flags & EVAL_EVALUATE;
int ret = OK;
dict_T *selfdict = NULL;
}
else
selfdict = NULL;
- if (eval_index(arg, rettv, evaluate, verbose) == FAIL)
+ if (eval_index(arg, rettv, flags, verbose) == FAIL)
{
clear_tv(rettv);
ret = FAIL;
need_clr_eos = needclr;
p = arg;
- if (eval1(&arg, &rettv, !eap->skip) == FAIL)
+ if (eval1(&arg, &rettv, eap->skip ? 0 : EVAL_EVALUATE) == FAIL)
{
/*
* Report the invalid expression unless the expression evaluation
if (p_verbose == 0)
++emsg_off;
- if (eval1(&p, &rettv, TRUE) == OK)
+ if (eval1(&p, &rettv, EVAL_EVALUATE) == OK)
{
if (rettv.v_type != VAR_LIST)
clear_tv(&rettv);
}
/*
- * When "redefine" is TRUE the command will be executed again, redefining the
- * variable is OK then.
+ * When "discovery" is TRUE the ":let" or ":const" is encountered during the
+ * discovery phase of vim9script:
+ * - The command will be executed again, redefining the variable is OK then.
+ * - The expresion argument must be a constant.
+ * - If no constant expression a type must be specified.
*/
void
-ex_let_const(exarg_T *eap, int redefine)
+ex_let_const(exarg_T *eap, int discovery)
{
char_u *arg = eap->arg;
char_u *expr = NULL;
char_u *argend;
int first = TRUE;
int concat;
+ int has_assign;
int flags = eap->cmdidx == CMD_const ? LET_IS_CONST : 0;
// detect Vim9 assignment without ":let" or ":const"
if (eap->arg == eap->cmd)
flags |= LET_NO_COMMAND;
- if (redefine)
- flags |= LET_REDEFINE;
+ if (discovery)
+ flags |= LET_DISCOVERY;
argend = skip_var_list(arg, TRUE, &var_count, &semicolon);
if (argend == NULL)
concat = expr[0] == '.'
&& ((expr[1] == '=' && current_sctx.sc_version < 2)
|| (expr[1] == '.' && expr[2] == '='));
- if (*expr != '=' && !((vim_strchr((char_u *)"+-*/%", *expr) != NULL
- && expr[1] == '=') || concat))
+ has_assign = *expr == '=' || (vim_strchr((char_u *)"+-*/%", *expr) != NULL
+ && expr[1] == '=');
+ if (!has_assign && !concat && !discovery)
{
// ":let" without "=": list variables
if (*arg == '[')
}
else
{
- op[0] = '=';
- op[1] = NUL;
- if (*expr != '=')
+ int eval_flags;
+ int save_called_emsg = called_emsg;
+
+ rettv.v_type = VAR_UNKNOWN;
+ i = FAIL;
+ if (has_assign || concat)
{
- if (vim_strchr((char_u *)"+-*/%.", *expr) != NULL)
+ op[0] = '=';
+ op[1] = NUL;
+ if (*expr != '=')
{
- op[0] = *expr; // +=, -=, *=, /=, %= or .=
- if (expr[0] == '.' && expr[1] == '.') // ..=
- ++expr;
+ if (vim_strchr((char_u *)"+-*/%.", *expr) != NULL)
+ {
+ op[0] = *expr; // +=, -=, *=, /=, %= or .=
+ if (expr[0] == '.' && expr[1] == '.') // ..=
+ ++expr;
+ }
+ expr = skipwhite(expr + 2);
}
- expr = skipwhite(expr + 2);
+ else
+ expr = skipwhite(expr + 1);
+
+ if (eap->skip)
+ ++emsg_skip;
+ eval_flags = eap->skip ? 0 : EVAL_EVALUATE;
+ if (discovery)
+ eval_flags |= EVAL_CONSTANT;
+ i = eval0(expr, &rettv, &eap->nextcmd, eval_flags);
}
- else
- expr = skipwhite(expr + 1);
-
- if (eap->skip)
- ++emsg_skip;
- i = eval0(expr, &rettv, &eap->nextcmd, !eap->skip);
if (eap->skip)
{
if (i != FAIL)
clear_tv(&rettv);
--emsg_skip;
}
- else if (i != FAIL)
+ else if (i != FAIL || (discovery && save_called_emsg == called_emsg))
{
+ // In Vim9 script discovery "let v: bool = Func()" fails but is
+ // still a valid declaration.
(void)ex_let_vars(eap->arg, &rettv, FALSE, semicolon, var_count,
flags, op);
clear_tv(&rettv);
{
// handle d.key, l[idx], f(expr)
arg_subsc = arg;
- if (handle_subscript(&arg, &tv, TRUE, TRUE,
+ if (handle_subscript(&arg, &tv, EVAL_EVALUATE, TRUE,
name, &name) == FAIL)
error = TRUE;
else
lval_T lv;
p = get_lval(arg, tv, &lv, FALSE, FALSE, 0, FNE_CHECK_START);
- if (p != NULL && lv.ll_name != NULL)
+ if ((flags & LET_DISCOVERY) && tv->v_type == VAR_UNKNOWN
+ && lv.ll_type == NULL)
+ {
+ semsg(_("E1091: type missing for %s"), arg);
+ }
+ else if (p != NULL && lv.ll_name != NULL)
{
if (endchars != NULL && vim_strchr(endchars,
*skipwhite(lv.ll_name_end)) == NULL)
if (flags & LET_IS_CONST)
di->di_tv.v_lock |= VAR_LOCKED;
- if (flags & LET_REDEFINE)
+ if (flags & LET_DISCOVERY)
di->di_flags |= DI_FLAGS_RELOAD;
}
if (n)
{
// handle d.key, l[idx], f(expr)
- n = (handle_subscript(&var, &tv, TRUE, FALSE, name, &name) == OK);
+ n = (handle_subscript(&var, &tv, EVAL_EVALUATE,
+ FALSE, name, &name) == OK);
if (n)
clear_tv(&tv);
}