]> granicus.if.org Git - vim/commitdiff
patch 8.2.1071: Vim9: no line break allowed inside a lambda v8.2.1071
authorBram Moolenaar <Bram@vim.org>
Sat, 27 Jun 2020 16:06:45 +0000 (18:06 +0200)
committerBram Moolenaar <Bram@vim.org>
Sat, 27 Jun 2020 16:06:45 +0000 (18:06 +0200)
Problem:    Vim9: no line break allowed inside a lambda.
Solution:   Handle line break inside a lambda in Vim9 script.

12 files changed:
src/eval.c
src/evalvars.c
src/ex_eval.c
src/globals.h
src/popupwin.c
src/proto/eval.pro
src/proto/userfunc.pro
src/structs.h
src/testdir/test_vim9_expr.vim
src/userfunc.c
src/version.c
src/vim9compile.c

index 0c8ab49c7fa639beaaad36bb9e75495edc7ef793..e1a33df60802830e39718085f53bac3a226b17cd 100644 (file)
@@ -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
     {
@@ -352,6 +351,61 @@ skip_expr(char_u **pp)
     return eval1(pp, &rettv, NULL);
 }
 
+/*
+ * 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
@@ -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;
index 7de7c1d1ef8805b32d32a0fbd2e08ef3bdd8f2d6..0acbd7bcf1a0b7b7ba29b6e20b804dc7cdf80873 100644 (file)
@@ -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);
        }
index 8b8a2569579d68ad5a1879e1fa21446e76ce7060..6f6b8c21894ddb866411d2dda027323cb909885a 100644 (file)
@@ -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;
 
index 03a6937561b291cf33adbf7cab0274644a27eabc..81903818b0c7a323edf4428f161258a1ea7ad3db 100644 (file)
@@ -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
index d144be73fe1c22c39da001a0741f76aa34d8fc3c..3461313ec02fd202b81467d73d72814f0b6e656f 100644 (file)
@@ -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);
index 16ba95b823e2ad6de266052a97660c66d0bbdacf..c66e331ce2dd58018e81717a8ca23fcf0add2374 100644 (file)
@@ -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);
index 340ef57f1c258e88e853008c42f02dfa0badea67..f69f77d6a4a57851117f0a023a67b53386493cb7 100644 (file)
@@ -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);
index e308ff448d13f4357a9d39b6eb52b072f678dbae..d68bc0184e25efb32851c53e0f7f536f67a2b4e0 100644 (file)
@@ -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;
index 30cf1b995be8fb50aa8ea7ca79345ed18aec9c0b..0a883f930667fcb013b8b42a9ad31f9469b575da 100644 (file)
@@ -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, {})
index 537c9ccd0f52d5d28c27d5893797335f28952294..b358ecb381a24f060ed8c792c1d3656fd4b4e18a 100644 (file)
@@ -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;
index a2fadfed64b9b24b8aae496fa2759f57ccc08e68..6c8e5e0418669b5abf483b2ef44fc86608aaa3c8 100644 (file)
@@ -754,6 +754,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1071,
 /**/
     1070,
 /**/
index 5d03e0f92c83d56622d393a382a3894b01b2ebf9..d37c5c95d0e2da8b224ca5b2517e79731499dee7 100644 (file)
@@ -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 != '(')