patch 8.2.1071: Vim9: no line break allowed inside a lambda
Problem: Vim9: no line break allowed inside a lambda.
Solution: Handle line break inside a lambda in Vim9 script.
diff --git a/src/eval.c b/src/eval.c
index 0c8ab49..e1a33df 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -325,8 +325,7 @@
if (skip)
++emsg_skip;
- if (eval0(arg, &tv, eap, skip ? NULL : &EVALARG_EVALUATE)
- == FAIL || skip)
+ if (eval0(arg, &tv, eap, skip ? NULL : &EVALARG_EVALUATE) == FAIL || skip)
retval = NULL;
else
{
@@ -353,6 +352,61 @@
}
/*
+ * Skip over an expression at "*pp".
+ * If in Vim9 script and line breaks are encountered, the lines are
+ * concatenated. "evalarg->eval_tofree" will be set accordingly.
+ * Return FAIL for an error, OK otherwise.
+ */
+ int
+skip_expr_concatenate(char_u **start, char_u **end, evalarg_T *evalarg)
+{
+ typval_T rettv;
+ int res;
+ int vim9script = current_sctx.sc_version == SCRIPT_VERSION_VIM9;
+ garray_T *gap = &evalarg->eval_ga;
+ int save_flags = evalarg == NULL ? 0 : evalarg->eval_flags;
+
+ if (vim9script && evalarg->eval_cookie != NULL)
+ {
+ ga_init2(gap, sizeof(char_u *), 10);
+ if (ga_grow(gap, 1) == OK)
+ // leave room for "start"
+ ++gap->ga_len;
+ }
+
+ // Don't evaluate the expression.
+ if (evalarg != NULL)
+ evalarg->eval_flags &= ~EVAL_EVALUATE;
+ *end = skipwhite(*end);
+ res = eval1(end, &rettv, evalarg);
+ if (evalarg != NULL)
+ evalarg->eval_flags = save_flags;
+
+ if (vim9script && evalarg->eval_cookie != NULL
+ && evalarg->eval_ga.ga_len > 1)
+ {
+ char_u *p;
+ size_t endoff = STRLEN(*end);
+
+ // Line breaks encountered, concatenate all the lines.
+ *((char_u **)gap->ga_data) = *start;
+ p = ga_concat_strings(gap, "");
+ *((char_u **)gap->ga_data) = NULL;
+ ga_clear_strings(gap);
+ gap->ga_itemsize = 0;
+ if (p == NULL)
+ return FAIL;
+ *start = p;
+ vim_free(evalarg->eval_tofree);
+ evalarg->eval_tofree = p;
+ // Compute "end" relative to the end.
+ *end = *start + STRLEN(*start) - endoff;
+ }
+
+ return res;
+}
+
+/*
* Top level evaluation function, returning a string.
* When "convert" is TRUE convert a List into a sequence of lines and convert
* a Float to a String.
@@ -1794,14 +1848,27 @@
}
/*
- * To be called when eval_next_non_blank() sets "getnext" to TRUE.
+ * To be called after eval_next_non_blank() sets "getnext" to TRUE.
*/
char_u *
eval_next_line(evalarg_T *evalarg)
{
- vim_free(evalarg->eval_tofree);
- evalarg->eval_tofree = getsourceline(0, evalarg->eval_cookie, 0, TRUE);
- return skipwhite(evalarg->eval_tofree);
+ garray_T *gap = &evalarg->eval_ga;
+ char_u *line;
+
+ line = getsourceline(0, evalarg->eval_cookie, 0, TRUE);
+ if (gap->ga_itemsize > 0 && ga_grow(gap, 1) == OK)
+ {
+ // Going to concatenate the lines after parsing.
+ ((char_u **)gap->ga_data)[gap->ga_len] = line;
+ ++gap->ga_len;
+ }
+ else
+ {
+ vim_free(evalarg->eval_tofree);
+ evalarg->eval_tofree = line;
+ }
+ return skipwhite(line);
}
/*
@@ -1831,8 +1898,6 @@
int called_emsg_before = called_emsg;
int flags = evalarg == NULL ? 0 : evalarg->eval_flags;
- if (evalarg != NULL)
- evalarg->eval_tofree = NULL;
p = skipwhite(arg);
ret = eval1(&p, rettv, evalarg);
@@ -1857,22 +1922,15 @@
if (eap != NULL)
eap->nextcmd = check_nextcmd(p);
- if (evalarg != NULL)
+ if (evalarg != NULL && eap != NULL && evalarg->eval_tofree != NULL)
{
- if (eap != NULL)
- {
- if (evalarg->eval_tofree != NULL)
- {
- // We may need to keep the original command line, e.g. for
- // ":let" it has the variable names. But we may also need the
- // new one, "nextcmd" points into it. Keep both.
- vim_free(eap->cmdline_tofree);
- eap->cmdline_tofree = *eap->cmdlinep;
- *eap->cmdlinep = evalarg->eval_tofree;
- }
- }
- else
- vim_free(evalarg->eval_tofree);
+ // We may need to keep the original command line, e.g. for
+ // ":let" it has the variable names. But we may also need the
+ // new one, "nextcmd" points into it. Keep both.
+ vim_free(eap->cmdline_tofree);
+ eap->cmdline_tofree = *eap->cmdlinep;
+ *eap->cmdlinep = evalarg->eval_tofree;
+ evalarg->eval_tofree = NULL;
}
return ret;
@@ -2797,7 +2855,7 @@
* Lambda: {arg, arg -> expr}
* Dictionary: {'key': val, 'key': val}
*/
- case '{': ret = get_lambda_tv(arg, rettv, evaluate);
+ case '{': ret = get_lambda_tv(arg, rettv, evalarg);
if (ret == NOTDONE)
ret = eval_dict(arg, rettv, evalarg, FALSE);
break;
@@ -2884,7 +2942,7 @@
// Handle following '[', '(' and '.' for expr[expr], expr.name,
// expr(expr), expr->name(expr)
if (ret == OK)
- ret = handle_subscript(arg, rettv, flags, TRUE);
+ ret = handle_subscript(arg, rettv, evalarg, TRUE);
/*
* Apply logical NOT and unary '-', from right to left, ignore '+'.
@@ -3031,9 +3089,11 @@
eval_lambda(
char_u **arg,
typval_T *rettv,
- int evaluate,
+ evalarg_T *evalarg,
int verbose) // give error messages
{
+ int evaluate = evalarg != NULL
+ && (evalarg->eval_flags & EVAL_EVALUATE);
typval_T base = *rettv;
int ret;
@@ -3041,7 +3101,7 @@
*arg += 2;
rettv->v_type = VAR_UNKNOWN;
- ret = get_lambda_tv(arg, rettv, evaluate);
+ ret = get_lambda_tv(arg, rettv, evalarg);
if (ret != OK)
return FAIL;
else if (**arg != '(')
@@ -3136,10 +3196,11 @@
eval_index(
char_u **arg,
typval_T *rettv,
- int flags,
+ evalarg_T *evalarg,
int verbose) // give error messages
{
- int evaluate = flags & EVAL_EVALUATE;
+ int evaluate = evalarg != NULL
+ && (evalarg->eval_flags & EVAL_EVALUATE);
int empty1 = FALSE, empty2 = FALSE;
typval_T var1, var2;
long i;
@@ -3200,11 +3261,6 @@
}
else
{
- evalarg_T evalarg;
-
- CLEAR_FIELD(evalarg);
- evalarg.eval_flags = flags;
-
/*
* something[idx]
*
@@ -3213,7 +3269,7 @@
*arg = skipwhite(*arg + 1);
if (**arg == ':')
empty1 = TRUE;
- else if (eval1(arg, &var1, &evalarg) == FAIL) // recursive!
+ else if (eval1(arg, &var1, evalarg) == FAIL) // recursive!
return FAIL;
else if (evaluate && tv_get_string_chk(&var1) == NULL)
{
@@ -3231,7 +3287,7 @@
*arg = skipwhite(*arg + 1);
if (**arg == ']')
empty2 = TRUE;
- else if (eval1(arg, &var2, &evalarg) == FAIL) // recursive!
+ else if (eval1(arg, &var2, evalarg) == FAIL) // recursive!
{
if (!empty1)
clear_tv(&var1);
@@ -4884,10 +4940,11 @@
handle_subscript(
char_u **arg,
typval_T *rettv,
- int flags, // do more than finding the end
+ evalarg_T *evalarg,
int verbose) // give error messages
{
- int evaluate = flags & EVAL_EVALUATE;
+ int evaluate = evalarg != NULL
+ && (evalarg->eval_flags & EVAL_EVALUATE);
int ret = OK;
dict_T *selfdict = NULL;
@@ -4926,7 +4983,7 @@
{
if ((*arg)[2] == '{')
// expr->{lambda}()
- ret = eval_lambda(arg, rettv, evaluate, verbose);
+ ret = eval_lambda(arg, rettv, evalarg, verbose);
else
// expr->name()
ret = eval_method(arg, rettv, evaluate, verbose);
@@ -4943,7 +5000,7 @@
}
else
selfdict = NULL;
- if (eval_index(arg, rettv, flags, verbose) == FAIL)
+ if (eval_index(arg, rettv, evalarg, verbose) == FAIL)
{
clear_tv(rettv);
ret = FAIL;