summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/eval.c137
-rw-r--r--src/evalvars.c6
-rw-r--r--src/ex_eval.c1
-rw-r--r--src/globals.h6
-rw-r--r--src/popupwin.c2
-rw-r--r--src/proto/eval.pro3
-rw-r--r--src/proto/userfunc.pro4
-rw-r--r--src/structs.h5
-rw-r--r--src/testdir/test_vim9_expr.vim12
-rw-r--r--src/userfunc.c32
-rw-r--r--src/version.c2
-rw-r--r--src/vim9compile.c6
12 files changed, 160 insertions, 56 deletions
diff --git a/src/eval.c b/src/eval.c
index 0c8ab49c7..e1a33df60 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -325,8 +325,7 @@ eval_to_string_skip(
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_expr(char_u **pp)
}
/*
+ * 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 @@ eval_next_non_blank(char_u *arg, evalarg_T *evalarg, int *getnext)
}
/*
- * 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 @@ eval0(
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 @@ eval0(
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 @@ eval7(
* 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 @@ eval7(
// 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 @@ call_func_rettv(
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 @@ eval_lambda(
*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_method(
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 @@ eval_index(
}
else
{
- evalarg_T evalarg;
-
- CLEAR_FIELD(evalarg);
- evalarg.eval_flags = flags;
-
/*
* something[idx]
*
@@ -3213,7 +3269,7 @@ eval_index(
*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 @@ eval_index(
*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 @@ eval_isnamec1(int c)
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 @@ handle_subscript(
{
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 @@ handle_subscript(
}
else
selfdict = NULL;
- if (eval_index(arg, rettv, flags, verbose) == FAIL)
+ if (eval_index(arg, rettv, evalarg, verbose) == FAIL)
{
clear_tv(rettv);
ret = FAIL;
diff --git a/src/evalvars.c b/src/evalvars.c
index 7de7c1d1e..0acbd7bcf 100644
--- a/src/evalvars.c
+++ b/src/evalvars.c
@@ -797,12 +797,14 @@ ex_let(exarg_T *eap)
if (eap->skip)
++emsg_skip;
+ CLEAR_FIELD(evalarg);
evalarg.eval_flags = eap->skip ? 0 : EVAL_EVALUATE;
evalarg.eval_cookie = eap->getline == getsourceline
? eap->cookie : NULL;
i = eval0(expr, &rettv, eap, &evalarg);
if (eap->skip)
--emsg_skip;
+ vim_free(evalarg.eval_tofree);
}
if (eap->skip)
{
@@ -1125,7 +1127,7 @@ list_arg_vars(exarg_T *eap, char_u *arg, int *first)
{
// handle d.key, l[idx], f(expr)
arg_subsc = arg;
- if (handle_subscript(&arg, &tv, EVAL_EVALUATE, TRUE)
+ if (handle_subscript(&arg, &tv, &EVALARG_EVALUATE, TRUE)
== FAIL)
error = TRUE;
else
@@ -3341,7 +3343,7 @@ var_exists(char_u *var)
if (n)
{
// handle d.key, l[idx], f(expr)
- n = (handle_subscript(&var, &tv, EVAL_EVALUATE, FALSE) == OK);
+ n = (handle_subscript(&var, &tv, &EVALARG_EVALUATE, FALSE) == OK);
if (n)
clear_tv(&tv);
}
diff --git a/src/ex_eval.c b/src/ex_eval.c
index 8b8a25695..6f6b8c218 100644
--- a/src/ex_eval.c
+++ b/src/ex_eval.c
@@ -897,6 +897,7 @@ ex_eval(exarg_T *eap)
typval_T tv;
evalarg_T evalarg;
+ CLEAR_FIELD(evalarg);
evalarg.eval_flags = eap->skip ? 0 : EVAL_EVALUATE;
evalarg.eval_cookie = eap->getline == getsourceline ? eap->cookie : NULL;
diff --git a/src/globals.h b/src/globals.h
index 03a693756..81903818b 100644
--- a/src/globals.h
+++ b/src/globals.h
@@ -1883,7 +1883,11 @@ EXTERN char windowsVersion[20] INIT(= {0});
EXTERN listitem_T range_list_item;
// Passed to an eval() function to enable evaluation.
-EXTERN evalarg_T EVALARG_EVALUATE INIT3(EVAL_EVALUATE, NULL, NULL);
+EXTERN evalarg_T EVALARG_EVALUATE
+# ifdef DO_INIT
+ = {EVAL_EVALUATE, NULL, {0, 0, 0, 0, NULL}, NULL}
+# endif
+ ;
#endif
#ifdef MSWIN
diff --git a/src/popupwin.c b/src/popupwin.c
index d144be73f..3461313ec 100644
--- a/src/popupwin.c
+++ b/src/popupwin.c
@@ -384,7 +384,7 @@ popup_add_timeout(win_T *wp, int time)
vim_snprintf((char *)cbbuf, sizeof(cbbuf),
"{_ -> popup_close(%d)}", wp->w_id);
- if (get_lambda_tv(&ptr, &tv, TRUE) == OK)
+ if (get_lambda_tv(&ptr, &tv, &EVALARG_EVALUATE) == OK)
{
wp->w_popup_timer = create_timer(time, 0);
wp->w_popup_timer->tr_callback = get_callback(&tv);
diff --git a/src/proto/eval.pro b/src/proto/eval.pro
index 16ba95b82..c66e331ce 100644
--- a/src/proto/eval.pro
+++ b/src/proto/eval.pro
@@ -9,6 +9,7 @@ int eval_expr_typval(typval_T *expr, typval_T *argv, int argc, typval_T *rettv);
int eval_expr_to_bool(typval_T *expr, int *error);
char_u *eval_to_string_skip(char_u *arg, exarg_T *eap, int skip);
int skip_expr(char_u **pp);
+int skip_expr_concatenate(char_u **start, char_u **end, evalarg_T *evalarg);
char_u *eval_to_string(char_u *arg, int convert);
char_u *eval_to_string_safe(char_u *arg, int use_sandbox);
varnumber_T eval_to_number(char_u *expr);
@@ -53,7 +54,7 @@ int get_name_len(char_u **arg, char_u **alias, int evaluate, int verbose);
char_u *find_name_end(char_u *arg, char_u **expr_start, char_u **expr_end, int flags);
int eval_isnamec(int c);
int eval_isnamec1(int c);
-int handle_subscript(char_u **arg, typval_T *rettv, int flags, int verbose);
+int handle_subscript(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int verbose);
int item_copy(typval_T *from, typval_T *to, int deep, int copyID);
void echo_one(typval_T *rettv, int with_space, int *atstart, int *needclr);
void ex_echo(exarg_T *eap);
diff --git a/src/proto/userfunc.pro b/src/proto/userfunc.pro
index 340ef57f1..f69f77d6a 100644
--- a/src/proto/userfunc.pro
+++ b/src/proto/userfunc.pro
@@ -3,8 +3,8 @@ void func_init(void);
hashtab_T *func_tbl_get(void);
int get_function_args(char_u **argp, char_u endchar, garray_T *newargs, garray_T *argtypes, int *varargs, garray_T *default_args, int skip, exarg_T *eap, char_u **line_to_free);
char_u *get_lambda_name(void);
-int get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate);
-char_u *register_cfunc(cfunc_T cb, cfunc_free_T free_cb, void *state);
+char_u *register_cfunc(cfunc_T cb, cfunc_free_T cb_free, void *state);
+int get_lambda_tv(char_u **arg, typval_T *rettv, evalarg_T *evalarg);
char_u *deref_func_name(char_u *name, int *lenp, partial_T **partialp, int no_autoload);
void emsg_funcname(char *ermsg, char_u *name);
int get_func_tv(char_u *name, int len, typval_T *rettv, char_u **arg, funcexe_T *funcexe);
diff --git a/src/structs.h b/src/structs.h
index e308ff448..d68bc0184 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -1763,6 +1763,11 @@ typedef struct {
// copied from exarg_T when "getline" is "getsourceline". Can be NULL.
void *eval_cookie; // argument for getline()
+ // Used to collect lines while parsing them, so that they can be
+ // concatenated later. Used when "eval_ga.ga_itemsize" is not zero.
+ // "eval_ga.ga_data" is a list of pointers to lines.
+ garray_T eval_ga;
+
// pointer to the line obtained with getsourceline()
char_u *eval_tofree;
} evalarg_T;
diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim
index 30cf1b995..0a883f930 100644
--- a/src/testdir/test_vim9_expr.vim
+++ b/src/testdir/test_vim9_expr.vim
@@ -1017,6 +1017,18 @@ def Test_expr7_lambda()
assert_equal([1, 3, 5], [1, 2, 3]->map({key, val -> key + val}))
enddef
+def Test_expr7_lambda_vim9script()
+ let lines =<< trim END
+ vim9script
+ let v = 10->{a ->
+ a
+ + 2
+ }()
+ assert_equal(12, v)
+ END
+ CheckScriptSuccess(lines)
+enddef
+
def Test_expr7_dict()
" dictionary
assert_equal(g:dict_empty, {})
diff --git a/src/userfunc.c b/src/userfunc.c
index 537c9ccd0..b358ecb38 100644
--- a/src/userfunc.c
+++ b/src/userfunc.c
@@ -391,8 +391,10 @@ errret:
* Return OK or FAIL. Returns NOTDONE for dict or {expr}.
*/
int
-get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate)
+get_lambda_tv(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
{
+ int evaluate = evalarg != NULL
+ && (evalarg->eval_flags & EVAL_EVALUATE);
garray_T newargs;
garray_T newlines;
garray_T *pnewargs;
@@ -404,6 +406,8 @@ get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate)
char_u *s, *e;
int *old_eval_lavars = eval_lavars_used;
int eval_lavars = FALSE;
+ int getnext;
+ char_u *tofree = NULL;
ga_init(&newargs);
ga_init(&newlines);
@@ -432,12 +436,25 @@ get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate)
// Get the start and the end of the expression.
*arg = skipwhite(*arg + 1);
+ eval_next_non_blank(*arg, evalarg, &getnext);
+ if (getnext)
+ *arg = eval_next_line(evalarg);
s = *arg;
- ret = skip_expr(arg);
+ ret = skip_expr_concatenate(&s, arg, evalarg);
if (ret == FAIL)
goto errret;
+ if (evalarg != NULL)
+ {
+ // avoid that the expression gets freed when another line break follows
+ tofree = evalarg->eval_tofree;
+ evalarg->eval_tofree = NULL;
+ }
+
e = *arg;
*arg = skipwhite(*arg);
+ eval_next_non_blank(*arg, evalarg, &getnext);
+ if (getnext)
+ *arg = eval_next_line(evalarg);
if (**arg != '}')
{
semsg(_("E451: Expected }: %s"), *arg);
@@ -447,7 +464,8 @@ get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate)
if (evaluate)
{
- int len, flags = 0;
+ int len;
+ int flags = 0;
char_u *p;
char_u *name = get_lambda_name();
@@ -464,7 +482,7 @@ get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate)
goto errret;
// Add "return " before the expression.
- len = 7 + e - s + 1;
+ len = 7 + (int)(e - s) + 1;
p = alloc(len);
if (p == NULL)
goto errret;
@@ -510,6 +528,7 @@ get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate)
}
eval_lavars_used = old_eval_lavars;
+ vim_free(tofree);
return OK;
errret:
@@ -517,6 +536,7 @@ errret:
ga_clear_strings(&newlines);
vim_free(fp);
vim_free(pt);
+ vim_free(tofree);
eval_lavars_used = old_eval_lavars;
return FAIL;
}
@@ -3925,8 +3945,8 @@ ex_call(exarg_T *eap)
dbg_check_breakpoint(eap);
// Handle a function returning a Funcref, Dictionary or List.
- if (handle_subscript(&arg, &rettv, eap->skip ? 0 : EVAL_EVALUATE,
- TRUE) == FAIL)
+ if (handle_subscript(&arg, &rettv,
+ eap->skip ? NULL : &EVALARG_EVALUATE, TRUE) == FAIL)
{
failed = TRUE;
break;
diff --git a/src/version.c b/src/version.c
index a2fadfed6..6c8e5e041 100644
--- a/src/version.c
+++ b/src/version.c
@@ -755,6 +755,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 1071,
+/**/
1070,
/**/
1069,
diff --git a/src/vim9compile.c b/src/vim9compile.c
index 5d03e0f92..d37c5c95d 100644
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -3001,7 +3001,7 @@ to_name_const_end(char_u *arg)
}
else if (p == arg && *arg == '{')
{
- int ret = get_lambda_tv(&p, &rettv, FALSE);
+ int ret = get_lambda_tv(&p, &rettv, NULL);
// Can be "{x -> ret}()".
// Can be "{'a': 1}->Func()".
@@ -3065,7 +3065,7 @@ compile_lambda(char_u **arg, cctx_T *cctx)
ufunc_T *ufunc;
// Get the funcref in "rettv".
- if (get_lambda_tv(arg, &rettv, TRUE) != OK)
+ if (get_lambda_tv(arg, &rettv, &EVALARG_EVALUATE) != OK)
return FAIL;
ufunc = rettv.vval.v_partial->pt_func;
@@ -3095,7 +3095,7 @@ compile_lambda_call(char_u **arg, cctx_T *cctx)
int ret = FAIL;
// Get the funcref in "rettv".
- if (get_lambda_tv(arg, &rettv, TRUE) == FAIL)
+ if (get_lambda_tv(arg, &rettv, &EVALARG_EVALUATE) == FAIL)
return FAIL;
if (**arg != '(')