]> granicus.if.org Git - vim/commitdiff
patch 8.1.1834: cannot use a lambda as a method v8.1.1834
authorBram Moolenaar <Bram@vim.org>
Fri, 9 Aug 2019 21:25:08 +0000 (23:25 +0200)
committerBram Moolenaar <Bram@vim.org>
Fri, 9 Aug 2019 21:25:08 +0000 (23:25 +0200)
Problem:    Cannot use a lambda as a method.
Solution:   Implement ->{lambda}(). (closes #4768)

runtime/doc/eval.txt
src/eval.c
src/testdir/test_method.vim
src/version.c

index 2f35c7c992b9c2948e45fb0e965b75d1b9068e28..981121f8a19367b56e7523722553f6f7f4034a17 100644 (file)
@@ -1217,7 +1217,8 @@ expr8(expr1, ...) |Funcref| function call
 When expr8 is a |Funcref| type variable, invoke the function it refers to.
 
 
-expr8->name([args])    method call                     *method*
+expr8->name([args])    method call                     *method* *->*
+expr8->{lambda}([args])
 
 For methods that are also available as global functions this is the same as: >
        name(expr8 [, args])
@@ -1227,6 +1228,9 @@ This allows for chaining, passing the value that one method returns to the
 next method: >
        mylist->filter(filterexpr)->map(mapexpr)->sort()->join()
 <
+Example of using a lambda: >
+       GetPercentage->{x -> x * 100}()->printf('%d%%')
+
                                                        *E274*
 "->name(" must not contain white space.  There can be white space before the
 "->" and after the "(", thus you can split the lines like this: >
index 0f9c95fa0d876afbc85e4f03c0685bb88582ad9b..1967f9401c8cac86164c47894e298c661bf6ea7a 100644 (file)
@@ -32,6 +32,7 @@ static char *e_cannot_mod = N_("E995: Cannot modify existing variable");
 #ifdef FEAT_FLOAT
 static char *e_float_as_string = N_("E806: using Float as a String");
 #endif
+static char *e_nowhitespace = N_("E274: No white space allowed before parenthesis");
 
 #define NAMESPACE_CHAR (char_u *)"abglstvw"
 
@@ -3693,7 +3694,6 @@ eval_func(
        vim_memset(&funcexe, 0, sizeof(funcexe));
        funcexe.firstline = curwin->w_cursor.lnum;
        funcexe.lastline = curwin->w_cursor.lnum;
-       funcexe.doesrange = &len;
        funcexe.evaluate = evaluate;
        funcexe.partial = partial;
        funcexe.basetv = basetv;
@@ -4821,6 +4821,95 @@ eval7(
     return ret;
 }
 
+/*
+ * Call the function referred to in "rettv".
+ */
+    static int
+call_func_rettv(
+       char_u      **arg,
+       typval_T    *rettv,
+       int         evaluate,
+       dict_T      *selfdict,
+       typval_T    *basetv)
+{
+    partial_T  *pt = NULL;
+    funcexe_T  funcexe;
+    typval_T   functv;
+    char_u     *s;
+    int                ret;
+
+    // need to copy the funcref so that we can clear rettv
+    if (evaluate)
+    {
+       functv = *rettv;
+       rettv->v_type = VAR_UNKNOWN;
+
+       /* Invoke the function.  Recursive! */
+       if (functv.v_type == VAR_PARTIAL)
+       {
+           pt = functv.vval.v_partial;
+           s = partial_name(pt);
+       }
+       else
+           s = functv.vval.v_string;
+    }
+    else
+       s = (char_u *)"";
+
+    vim_memset(&funcexe, 0, sizeof(funcexe));
+    funcexe.firstline = curwin->w_cursor.lnum;
+    funcexe.lastline = curwin->w_cursor.lnum;
+    funcexe.evaluate = evaluate;
+    funcexe.partial = pt;
+    funcexe.selfdict = selfdict;
+    funcexe.basetv = basetv;
+    ret = get_func_tv(s, -1, rettv, arg, &funcexe);
+
+    /* Clear the funcref afterwards, so that deleting it while
+     * evaluating the arguments is possible (see test55). */
+    if (evaluate)
+       clear_tv(&functv);
+
+    return ret;
+}
+
+/*
+ * Evaluate "->method()".
+ * "*arg" points to the '-'.
+ * Returns FAIL or OK. "*arg" is advanced to after the ')'.
+ */
+    static int
+eval_lambda(
+    char_u     **arg,
+    typval_T   *rettv,
+    int                evaluate,
+    int                verbose)        /* give error messages */
+{
+    typval_T   base = *rettv;
+    int                ret;
+
+    // Skip over the ->.
+    *arg += 2;
+    rettv->v_type = VAR_UNKNOWN;
+
+    ret = get_lambda_tv(arg, rettv, evaluate);
+    if (ret == NOTDONE)
+       return FAIL;
+    else if (**arg != '(')
+    {
+       if (verbose)
+       {
+           if (*skipwhite(*arg) == '(')
+               semsg(_(e_nowhitespace));
+           else
+               semsg(_(e_missingparen), "lambda");
+       }
+       clear_tv(rettv);
+       return FAIL;
+    }
+    return call_func_rettv(arg, rettv, evaluate, NULL, &base);
+}
+
 /*
  * Evaluate "->method()".
  * "*arg" points to the '-'.
@@ -4865,15 +4954,15 @@ eval_method(
        else if (VIM_ISWHITE((*arg)[-1]))
        {
            if (verbose)
-               semsg(_("E274: No white space allowed before parenthesis"));
+               semsg(_(e_nowhitespace));
            ret = FAIL;
        }
        else
            ret = eval_func(arg, name, len, rettv, evaluate, &base);
     }
 
-    /* Clear the funcref afterwards, so that deleting it while
-     * evaluating the arguments is possible (see test55). */
+    // Clear the funcref afterwards, so that deleting it while
+    // evaluating the arguments is possible (see test55).
     if (evaluate)
        clear_tv(&base);
 
@@ -7455,8 +7544,6 @@ handle_subscript(
 {
     int                ret = OK;
     dict_T     *selfdict = NULL;
-    char_u     *s;
-    typval_T   functv;
 
     // "." is ".name" lookup when we found a dict or when evaluating and
     // scriptversion is at least 2, where string concatenation is "..".
@@ -7473,43 +7560,11 @@ handle_subscript(
     {
        if (**arg == '(')
        {
-           partial_T   *pt = NULL;
-           funcexe_T   funcexe;
-
-           /* need to copy the funcref so that we can clear rettv */
-           if (evaluate)
-           {
-               functv = *rettv;
-               rettv->v_type = VAR_UNKNOWN;
-
-               /* Invoke the function.  Recursive! */
-               if (functv.v_type == VAR_PARTIAL)
-               {
-                   pt = functv.vval.v_partial;
-                   s = partial_name(pt);
-               }
-               else
-                   s = functv.vval.v_string;
-           }
-           else
-               s = (char_u *)"";
-
-           vim_memset(&funcexe, 0, sizeof(funcexe));
-           funcexe.firstline = curwin->w_cursor.lnum;
-           funcexe.lastline = curwin->w_cursor.lnum;
-           funcexe.evaluate = evaluate;
-           funcexe.partial = pt;
-           funcexe.selfdict = selfdict;
-           ret = get_func_tv(s, -1, rettv, arg, &funcexe);
-
-           /* Clear the funcref afterwards, so that deleting it while
-            * evaluating the arguments is possible (see test55). */
-           if (evaluate)
-               clear_tv(&functv);
+           ret = call_func_rettv(arg, rettv, evaluate, selfdict, NULL);
 
-           /* Stop the expression evaluation when immediately aborting on
-            * error, or when an interrupt occurred or an exception was thrown
-            * but not caught. */
+           // Stop the expression evaluation when immediately aborting on
+           // error, or when an interrupt occurred or an exception was thrown
+           // but not caught.
            if (aborting())
            {
                if (ret == OK)
@@ -7521,11 +7576,12 @@ handle_subscript(
        }
        else if (**arg == '-')
        {
-           if (eval_method(arg, rettv, evaluate, verbose) == FAIL)
-           {
-               clear_tv(rettv);
-               ret = FAIL;
-           }
+           if ((*arg)[2] == '{')
+               // expr->{lambda}()
+               ret = eval_lambda(arg, rettv, evaluate, verbose);
+           else
+               // expr->name()
+               ret = eval_method(arg, rettv, evaluate, verbose);
        }
        else /* **arg == '[' || **arg == '.' */
        {
index 7780f087d4f5e404e86efc9251291c9397c6d01a..310b5f22c724eb40a7a518911ea4b5266c3b7998 100644 (file)
@@ -122,3 +122,13 @@ func Test_method_syntax()
   call assert_fails('eval [1, 2, 3]->sort ()', 'E274:')
   call assert_fails('eval [1, 2, 3]-> sort ()', 'E260:')
 endfunc
+
+func Test_method_lambda()
+  eval "text"->{x -> x .. " extended"}()->assert_equal('text extended')
+  eval "text"->{x, y -> x .. " extended " .. y}('more')->assert_equal('text extended more')
+
+  call assert_fails('eval "text"->{x -> x .. " extended"} ()', 'E274:')
+
+  " todo: lambda accepts more arguments than it consumes
+  " call assert_fails('eval "text"->{x -> x .. " extended"}("more")', 'E99:')
+endfunc
index 72dd651d2a9bfdfb8db46f12898b1a81f107c339..2d06c7773e42909a7d96977743a9b1beeac60886 100644 (file)
@@ -769,6 +769,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1834,
 /**/
     1833,
 /**/