Hi mattn and all,

2016/5/27 Fri 22:38:34 UTC+9 Ken Takata wrote:
> Hi mattn,
> 
> 2016/4/18 Mon 0:30:19 UTC+9 mattn wrote:
> > Hi, Bram and list.
> > 
> > Updated lambda patch to be applied for latest code.
> > 
> > https://gist.github.com/mattn/5bc8ded21e1033c9c0ea8cd5ecbbce11
> > 
> > This include examples for timer on help file. I'm thinking lambda() have 
> > cooperative to work with job/timer/channel.
> > 
> > So I hope to add this into vim8. How do you think?
> > 
> > Thanks to k-takata, haya14busa, and all of members on vim-jp.
> 
> I have tested the lambda patch with the latest Vim and I found two problems.
> 
>   1. garbagecollect_for_testing() was renamed.
>   2. Test_lambda_with_timer() fails on Cygwin. It seems that we need margins
>      as we do in test_timers.vim.
> 
> Please check attached patch.
> 
> I hope that the lambda patch will be merged in Vim 8.0.
> This makes filter(), map() and sort() easy to use.
> It also works nicely with job/channel/timer features.

I have merged my patch into your patch, and also fixed that the function
declaration was not sorted in alphabetical order.
Please check the attached patch.

Regards,
Ken Takata

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to vim_dev+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
# HG changeset patch
# Parent  96b65a1b79647a0144a4926733a6ef48cc338108

diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -123,9 +123,10 @@ You will not get an error if you try to 
 
 1.2 Function references ~
 					*Funcref* *E695* *E718*
-A Funcref variable is obtained with the |function()| function.	It can be used
-in an expression in the place of a function name, before the parenthesis
-around the arguments, to invoke the function it refers to.  Example: >
+A Funcref variable is obtained with the |function()| function or created with
+the |lambda()| function.  It can be used in an expression in the place of a
+function name, before the parenthesis around the arguments, to invoke the
+function it refers to.  Example: >
 
 	:let Fn = function("MyFunc")
 	:echo Fn()
@@ -2057,6 +2058,7 @@ json_decode({string})		any	decode JSON
 json_encode({expr})		String	encode JSON
 keys({dict})			List	keys in {dict}
 len({expr})			Number	the length of {expr}
+lambda({expr})			Funcref	create a lambda function
 libcall({lib}, {func}, {arg})	String	call {func} in library {lib} with {arg}
 libcallnr({lib}, {func}, {arg})  Number  idem, but return a Number
 line({expr})			Number	line nr of cursor, last line or mark
@@ -3511,11 +3513,12 @@ filewritable({file})					*filewritable()
 		directory, and we can write to it, the result is 2.
 
 
-filter({expr}, {string})					*filter()*
-		{expr} must be a |List| or a |Dictionary|.
-		For each item in {expr} evaluate {string} and when the result
+filter({expr1}, {expr2})				*filter()*
+		{expr1} must be a |List| or a |Dictionary|.
+		For each item in {expr1} evaluate {expr2} and when the result
 		is zero remove the item from the |List| or |Dictionary|.
-		Inside {string} |v:val| has the value of the current item.
+		{expr2} must be a |string| or |Funcref|. If it is a |string|,
+		inside {expr2} |v:val| has the value of the current item.
 		For a |Dictionary| |v:key| has the key of the current item.
 		Examples: >
 			:call filter(mylist, 'v:val !~ "OLD"')
@@ -3525,7 +3528,7 @@ filter({expr}, {string})					*filter()*
 			:call filter(var, 0)
 <		Removes all the items, thus clears the |List| or |Dictionary|.
 
-		Note that {string} is the result of expression and is then
+		Note that {expr2} is the result of expression and is then
 		used as an expression again.  Often it is good to use a
 		|literal-string| to avoid having to double backslashes.
 
@@ -3533,9 +3536,13 @@ filter({expr}, {string})					*filter()*
 		|Dictionary| to remain unmodified make a copy first: >
 			:let l = filter(copy(mylist), 'v:val =~ "KEEP"')
 
-<		Returns {expr}, the |List| or |Dictionary| that was filtered.
-		When an error is encountered while evaluating {string} no
-		further items in {expr} are processed.
+<		Returns {expr1}, the |List| or |Dictionary| that was filtered.
+		When an error is encountered while evaluating {expr2} no
+		further items in {expr1} are processed.
+
+		{expr2} is possible to be given as |Funcref|. The function
+		should return TRUE if the item should be kept. The value is
+		given as "a:1".
 
 
 finddir({name}[, {path}[, {count}]])				*finddir()*
@@ -4878,6 +4885,72 @@ len({expr})	The result is a Number, whic
 		|Dictionary| is returned.
 		Otherwise an error is given.
 
+							*lambda()*
+lambda({expr})
+		Create a new lambda function which constructed with {expr}.
+		The result is a |Funcref|. So you can call it just like a
+		normal function.
+		Example: >
+		    :let F = lambda("return 1 + 2")
+		    :echo F()
+<		    3
+
+		Lambda function can take variadic arguments.
+		Example: >
+		    :let F = lambda("return a:1 + 2")
+		    :echo F(2)
+<		    4
+
+		|sort()|, |map()| and |filter()| can be used with |lambda()|.
+		Examples: >
+		    :echo map([1, 2, 3], lambda("return a:1 + 1"))
+<		    [2, 3, 4] >
+		    :echo sort([3,7,2,1,4], lambda("return a:1 - a:2"))
+<		    [1, 2, 3, 4, 7]
+
+		Channel, job and timer can also be used with |lambda()|.
+		Example: >
+		    :let timer = timer_start(500,
+				\ lambda("echo 'Handler called'"),
+				\ {'repeat': 3})
+<
+		Lambda function can reference the variables in the defined
+		scope.
+		Examples: >
+		    :let s:x = 2
+		    :echo filter([1, 2, 3], lambda("return a:1 >= s:x"))
+<		    [2, 3] >
+
+		    :function! Foo()
+		    :  let x = 1
+		    :  return lambda("return x")
+		    :endfunction
+		    :echo Foo()()
+<		    1
+
+		And if let new variable, it will be stored in the lambda's
+		scope.
+		Example: >
+		    :function! Foo()
+		    :  call lambda("let x = 1")()
+		    :  echo x | " Should be error
+		    :endfunction
+<
+		For example, you can create a counter generator. >
+		    :function! s:counter(x)
+		    :  let x = a:x
+		    :  return lambda("
+		    :  \ let x += 1 \n
+		    :  \ return x
+		    :  \")
+		    :endfunction
+
+		    :let F = s:counter(0)
+		    :echo F() | " 1
+		    :echo F() | " 2
+		    :echo F() | " 3
+		    :echo F() | " 4
+<
 						*libcall()* *E364* *E368*
 libcall({libname}, {funcname}, {argument})
 		Call function {funcname} in the run-time library {libname}
@@ -5024,18 +5097,19 @@ luaeval({expr}[, {expr}])					*luaeval()
 		See |lua-luaeval| for more details.
 		{only available when compiled with the |+lua| feature}
 
-map({expr}, {string})					*map()*
-		{expr} must be a |List| or a |Dictionary|.
-		Replace each item in {expr} with the result of evaluating
-		{string}.
-		Inside {string} |v:val| has the value of the current item.
+map({expr1}, {expr2})					*map()*
+		{expr1} must be a |List| or a |Dictionary|.
+		Replace each item in {expr1} with the result of evaluating
+		{expr2}.
+		{expr2} must be a |string| or |Funcref|. If it is a |string|,
+		inside {expr2} |v:val| has the value of the current item.
 		For a |Dictionary| |v:key| has the key of the current item
 		and for a |List| |v:key| has the index of the current item.
 		Example: >
 			:call map(mylist, '"> " . v:val . " <"')
 <		This puts "> " before and " <" after each item in "mylist".
 
-		Note that {string} is the result of an expression and is then
+		Note that {expr2} is the result of an expression and is then
 		used as an expression again.  Often it is good to use a
 		|literal-string| to avoid having to double backslashes.  You
 		still have to double ' quotes
@@ -5044,10 +5118,12 @@ map({expr}, {string})					*map()*
 		|Dictionary| to remain unmodified make a copy first: >
 			:let tlist = map(copy(mylist), ' v:val . "\t"')
 
-<		Returns {expr}, the |List| or |Dictionary| that was filtered.
-		When an error is encountered while evaluating {string} no
-		further items in {expr} are processed.
-
+<		Returns {expr1}, the |List| or |Dictionary| that was filtered.
+		When an error is encountered while evaluating {expr2} no
+		further items in {expr1} are processed.
+
+		{expr2} is possible to be given as |Funcref|. The function
+		should return value proceeded from "a:1".
 
 maparg({name}[, {mode} [, {abbr} [, {dict}]]])			*maparg()*
 		When {dict} is omitted or zero: Return the rhs of mapping
diff --git a/src/eval.c b/src/eval.c
--- a/src/eval.c
+++ b/src/eval.c
@@ -166,6 +166,7 @@ static int echo_attr = 0;   /* attribute
  * Structure to hold info for a user function.
  */
 typedef struct ufunc ufunc_T;
+typedef struct funccall_S funccall_T;
 
 struct ufunc
 {
@@ -194,6 +195,7 @@ struct ufunc
     scid_T	uf_script_ID;	/* ID of script where function was defined,
 				   used for s: variables */
     int		uf_refcount;	/* for numbered function: reference count */
+    funccall_T	*uf_scoped;	/* l: local function variables */
     char_u	uf_name[1];	/* name of function (actually longer); can
 				   start with <SNR>123_ (<SNR> is K_SPECIAL
 				   KS_EXTRA KE_SNR) */
@@ -232,7 +234,6 @@ static ufunc_T dumuf;
 #define FIXVAR_CNT	12	/* number of fixed variables */
 
 /* structure to hold info for a function that is currently being executed. */
-typedef struct funccall_S funccall_T;
 
 struct funccall_S
 {
@@ -258,6 +259,10 @@ struct funccall_S
     proftime_T	prof_child;	/* time spent in a child */
 #endif
     funccall_T	*caller;	/* calling function or NULL */
+
+    /* for lambda */
+    int		ref_by_lambda;
+    int		lambda_copyID;	/* for garbage collection */
 };
 
 /*
@@ -659,6 +664,7 @@ static void f_js_encode(typval_T *argvar
 static void f_json_decode(typval_T *argvars, typval_T *rettv);
 static void f_json_encode(typval_T *argvars, typval_T *rettv);
 static void f_keys(typval_T *argvars, typval_T *rettv);
+static void f_lambda(typval_T *argvars, typval_T *rettv);
 static void f_last_buffer_nr(typval_T *argvars, typval_T *rettv);
 static void f_len(typval_T *argvars, typval_T *rettv);
 static void f_libcall(typval_T *argvars, typval_T *rettv);
@@ -3786,6 +3792,7 @@ do_unlet(char_u *name, int forceit)
     char_u	*varname;
     dict_T	*d;
     dictitem_T	*di;
+    funccall_T	*old_current_funccal;
 
     ht = find_var_ht(name, &varname);
     if (ht != NULL && *varname != NUL)
@@ -3808,6 +3815,26 @@ do_unlet(char_u *name, int forceit)
 	    return FAIL;
 	}
 	hi = hash_find(ht, varname);
+
+	if (HASHITEM_EMPTY(hi) && current_funccal != NULL &&
+		current_funccal->func->uf_scoped != NULL)
+	{
+	    /* Search in parent scope for lambda */
+	    old_current_funccal = current_funccal;
+	    current_funccal = current_funccal->func->uf_scoped;
+	    while (current_funccal)
+	    {
+		ht = find_var_ht(name, &varname);
+		if (ht != NULL && *varname != NUL)
+		{
+		    hi = hash_find(ht, varname);
+		    if (!HASHITEM_EMPTY(hi))
+			break;
+		}
+		current_funccal = current_funccal->func->uf_scoped;
+	    }
+	    current_funccal = old_current_funccal;
+	}
 	if (!HASHITEM_EMPTY(hi))
 	{
 	    di = HI2DI(hi);
@@ -7053,6 +7080,7 @@ garbage_collect(int testing)
      * the item is referenced elsewhere the funccal must not be freed. */
     for (fc = previous_funccal; fc != NULL; fc = fc->caller)
     {
+	fc->lambda_copyID = copyID + 1;
 	abort = abort || set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID + 1,
 									NULL);
 	abort = abort || set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID + 1,
@@ -7437,6 +7465,25 @@ set_ref_in_item(
 							ht_stack, list_stack);
 	}
     }
+    else if (tv->v_type == VAR_FUNC)
+    {
+	ufunc_T	*fp;
+	funccall_T	*fc;
+
+	fp = find_func(tv->vval.v_string);
+	if (fp != NULL)
+	{
+	    for (fc = fp->uf_scoped; fc != NULL; fc = fc->func->uf_scoped)
+	    {
+		if (fc->lambda_copyID != copyID)
+		{
+		    fc->lambda_copyID = copyID;
+		    set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID, NULL);
+		    set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID, NULL);
+		}
+	    }
+	}
+    }
 #ifdef FEAT_JOB_CHANNEL
     else if (tv->v_type == VAR_JOB)
     {
@@ -8652,6 +8699,7 @@ static struct fst
     {"json_decode",	1, 1, f_json_decode},
     {"json_encode",	1, 1, f_json_encode},
     {"keys",		1, 1, f_keys},
+    {"lambda",		1, 1, f_lambda},
     {"last_buffer_nr",	0, 0, f_last_buffer_nr},/* obsolete */
     {"len",		1, 1, f_len},
     {"libcall",		3, 3, f_libcall},
@@ -9236,7 +9284,7 @@ call_func(
 	rettv->vval.v_number = 0;
 	error = ERROR_UNKNOWN;
 
-	if (!builtin_function(rfname, -1))
+	if (!builtin_function(rfname, -1) || !STRNICMP(rfname, "<LAMBDA>", 8))
 	{
 	    /*
 	     * User defined function.
@@ -11841,7 +11889,7 @@ findfilendir(
 }
 
 static void filter_map(typval_T *argvars, typval_T *rettv, int map);
-static int filter_map_one(typval_T *tv, char_u *expr, int map, int *remp);
+static int filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp);
 
 /*
  * Implementation of map() and filter().
@@ -11849,8 +11897,7 @@ static int filter_map_one(typval_T *tv, 
     static void
 filter_map(typval_T *argvars, typval_T *rettv, int map)
 {
-    char_u	buf[NUMBUFLEN];
-    char_u	*expr;
+    typval_T	*expr;
     listitem_T	*li, *nli;
     list_T	*l = NULL;
     dictitem_T	*di;
@@ -11885,14 +11932,13 @@ filter_map(typval_T *argvars, typval_T *
 	return;
     }
 
-    expr = get_tv_string_buf_chk(&argvars[1], buf);
+    expr = &argvars[1];
     /* On type errors, the preceding call has already displayed an error
      * message.  Avoid a misleading error message for an empty string that
      * was not passed as argument. */
-    if (expr != NULL)
+    if (expr->v_type != VAR_UNKNOWN)
     {
 	prepare_vimvar(VV_VAL, &save_val);
-	expr = skipwhite(expr);
 
 	/* We reset "did_emsg" to be able to detect whether an error
 	 * occurred during evaluation of the expression. */
@@ -11964,21 +12010,31 @@ filter_map(typval_T *argvars, typval_T *
 }
 
     static int
-filter_map_one(typval_T *tv, char_u *expr, int map, int *remp)
+filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp)
 {
     typval_T	rettv;
     char_u	*s;
     int		retval = FAIL;
+    int		dummy;
 
     copy_tv(tv, &vimvars[VV_VAL].vv_tv);
-    s = expr;
-    if (eval1(&s, &rettv, TRUE) == FAIL)
-	goto theend;
-    if (*s != NUL)  /* check for trailing chars after expr */
-    {
-	EMSG2(_(e_invexpr2), s);
-	clear_tv(&rettv);
-	goto theend;
+    s = expr->vval.v_string;
+    if (expr->v_type == VAR_FUNC)
+    {
+	if (call_func(s, (int)STRLEN(s),
+		    &rettv, 1, tv, 0L, 0L, &dummy, TRUE, NULL, NULL) == FALSE)
+	    goto theend;
+    }
+    else
+    {
+	s = skipwhite(s);
+	if (eval1(&s, &rettv, TRUE) == FAIL)
+	    goto theend;
+	if (*s != NUL)  /* check for trailing chars after expr */
+	{
+	    EMSG2(_(e_invexpr2), s);
+	    goto theend;
+	}
     }
     if (map)
     {
@@ -15385,6 +15441,82 @@ f_keys(typval_T *argvars, typval_T *rett
 }
 
 /*
+ * "lambda()" function.
+ */
+    static void
+f_lambda(argvars, rettv)
+    typval_T	*argvars UNUSED;
+    typval_T	*rettv;
+{
+    char_u	*s, *e;
+    garray_T	newargs;
+    garray_T	newlines;
+    ufunc_T	*fp;
+    char_u	name[20];
+    static int	lambda_no = 0;
+
+    if (check_secure())
+	return;
+
+    s = get_tv_string_chk(&argvars[0]);
+    if (s == NULL)
+        goto erret;
+
+    fp = (ufunc_T *)alloc((unsigned)(sizeof(ufunc_T) + 20));
+    if (fp == NULL)
+	goto erret;
+
+    sprintf((char*)name, "<LAMBDA>%d", ++lambda_no);
+    rettv->vval.v_string = vim_strsave(name);
+
+    ga_init2(&newargs, (int)sizeof(char_u *), 1);
+    ga_init2(&newlines, (int)sizeof(char_u *), 1);
+
+    while (*s)
+    {
+	s = skipwhite(s);
+	e = s;
+	while (*e && *e != '\n')
+	    e++;
+        if (ga_grow(&newlines, 1) == FAIL)
+	    goto erret;
+        ((char_u **)(newlines.ga_data))[newlines.ga_len++] = vim_strnsave(s, e - s);
+	s = *e == 0 ? e : e + 1;
+    }
+
+    fp->uf_refcount = 1;
+    STRCPY(fp->uf_name, name);
+    hash_add(&func_hashtab, UF2HIKEY(fp));
+    fp->uf_args = newargs;
+    fp->uf_lines = newlines;
+#ifdef FEAT_PROFILE
+    fp->uf_tml_count = NULL;
+    fp->uf_tml_total = NULL;
+    fp->uf_tml_self = NULL;
+    fp->uf_profiling = FALSE;
+    if (prof_def_func())
+	func_do_profile(fp);
+#endif
+    fp->uf_varargs = TRUE;
+    fp->uf_flags = 0;
+    fp->uf_calls = 0;
+    fp->uf_script_ID = current_SID;
+    if (current_funccal)
+    {
+	fp->uf_scoped = current_funccal;
+	current_funccal->ref_by_lambda = TRUE;
+    }
+    else
+	fp->uf_scoped = NULL;
+    rettv->v_type = VAR_FUNC;
+    return;
+
+erret:
+    ga_clear_strings(&newargs);
+    ga_clear_strings(&newlines);
+}
+
+/*
  * "last_buffer_nr()" function.
  */
     static void
@@ -20917,8 +21049,15 @@ get_callback(typval_T *arg, partial_T **
 	return (*pp)->pt_name;
     }
     *pp = NULL;
-    if (arg->v_type == VAR_FUNC || arg->v_type == VAR_STRING)
+    if (arg->v_type == VAR_FUNC)
+    {
+	func_ref(arg->vval.v_string);
 	return arg->vval.v_string;
+    }
+    if (arg->v_type == VAR_STRING)
+    {
+	return arg->vval.v_string;
+    }
     if (arg->v_type == VAR_NUMBER && arg->vval.v_number == 0)
 	return (char_u *)"";
     EMSG(_("E921: Invalid callback argument"));
@@ -23001,6 +23140,37 @@ get_tv_string_buf_chk(typval_T *varp, ch
     return NULL;
 }
 
+    static dictitem_T*
+find_var_in_scoped_ht(name, varname, no_autoload)
+    char_u	*name;
+    char_u	**varname;
+    int		no_autoload;
+{
+    dictitem_T	*v = NULL;
+    funccall_T	*old_current_funccal = current_funccal;
+    hashtab_T	*ht;
+
+    /* Search in parent scope which is possible to reference from lambda */
+    current_funccal = current_funccal->func->uf_scoped;
+    while (current_funccal)
+    {
+	ht = find_var_ht(name, varname ? &(*varname) : NULL);
+	if (ht != NULL)
+	{
+	    v = find_var_in_ht(ht, *name,
+		    varname ? *varname : NULL, no_autoload);
+	    if (v != NULL)
+		break;
+	}
+	if (current_funccal == current_funccal->func->uf_scoped)
+	    break;
+	current_funccal = current_funccal->func->uf_scoped;
+    }
+    current_funccal = old_current_funccal;
+
+    return v;
+}
+
 /*
  * Find variable "name" in the list of variables.
  * Return a pointer to it if found, NULL if not found.
@@ -23013,13 +23183,23 @@ find_var(char_u *name, hashtab_T **htp, 
 {
     char_u	*varname;
     hashtab_T	*ht;
+    dictitem_T	*ret = NULL;
 
     ht = find_var_ht(name, &varname);
     if (htp != NULL)
 	*htp = ht;
     if (ht == NULL)
 	return NULL;
-    return find_var_in_ht(ht, *name, varname, no_autoload || htp != NULL);
+    ret = find_var_in_ht(ht, *name, varname, no_autoload || htp != NULL);
+    if (ret != NULL)
+	return ret;
+
+    /* Search in parent scope for lambda */
+    if (current_funccal != NULL && current_funccal->func->uf_scoped != NULL)
+	return find_var_in_scoped_ht(name, varname ? &varname : NULL,
+		no_autoload || htp != NULL);
+
+    return NULL;
 }
 
 /*
@@ -23382,6 +23562,24 @@ set_var(
     }
     v = find_var_in_ht(ht, 0, varname, TRUE);
 
+    /* Search in parent scope which is possible to reference from lambda */
+    if (v == NULL && current_funccal != NULL &&
+	    current_funccal->func->uf_scoped != NULL)
+    {
+	v = find_var_in_scoped_ht(name, varname ? &varname : NULL, TRUE);
+
+	/* When the variable is not found, let scope should be parent of the
+	 * lambda.
+	 */
+	if (v == NULL)
+	{
+	    hashtab_T	*ht_tmp;
+	    ht_tmp = find_var_ht(name, &varname);
+	    if (ht_tmp != NULL)
+		ht = ht_tmp;
+	}
+    }
+
     if ((tv->v_type == VAR_FUNC || tv->v_type == VAR_PARTIAL)
 				      && var_check_func_name(name, v == NULL))
 	return;
@@ -24634,6 +24832,7 @@ ex_function(exarg_T *eap)
     fp->uf_flags = flags;
     fp->uf_calls = 0;
     fp->uf_script_ID = current_SID;
+    fp->uf_scoped = NULL;
     goto ret_free;
 
 erret:
@@ -25452,7 +25651,9 @@ func_unref(char_u *name)
 {
     ufunc_T *fp;
 
-    if (name != NULL && isdigit(*name))
+    if (name == NULL)
+	return;
+    else if (isdigit(*name))
     {
 	fp = find_func(name);
 	if (fp == NULL)
@@ -25470,6 +25671,18 @@ func_unref(char_u *name)
 		func_free(fp);
 	}
     }
+    else if (!STRNICMP(name, "<LAMBDA>", 8))
+    {
+	/* fail silently, when lambda function isn't found. */
+	fp = find_func(name);
+	if (fp != NULL && --fp->uf_refcount <= 0)
+	{
+	    /* Only delete it when it's not being used.  Otherwise it's done
+	     * when "uf_calls" becomes zero. */
+	    if (fp->uf_calls == 0)
+		func_free(fp);
+	}
+    }
 }
 
 /*
@@ -25480,7 +25693,9 @@ func_ref(char_u *name)
 {
     ufunc_T *fp;
 
-    if (name != NULL && isdigit(*name))
+    if (name == NULL)
+	return;
+    else if (isdigit(*name))
     {
 	fp = find_func(name);
 	if (fp == NULL)
@@ -25488,6 +25703,13 @@ func_ref(char_u *name)
 	else
 	    ++fp->uf_refcount;
     }
+    else if (!STRNICMP(name, "<LAMBDA>", 8))
+    {
+	/* fail silently, when lambda function isn't found. */
+	fp = find_func(name);
+	if (fp != NULL)
+	    ++fp->uf_refcount;
+    }
 }
 
 /*
@@ -25545,6 +25767,9 @@ call_user_func(
     /* Check if this function has a breakpoint. */
     fc->breakpoint = dbg_find_breakpoint(FALSE, fp->uf_name, (linenr_T)0);
     fc->dbg_tick = debug_tick;
+    /* Set up fields for lambda. */
+    fc->ref_by_lambda = FALSE;
+    fc->lambda_copyID = current_copyID;
 
     /*
      * Note about using fc->fixvar[]: This is an array of FIXVAR_CNT variables
@@ -25828,7 +26053,8 @@ call_user_func(
      * free the funccall_T and what's in it. */
     if (fc->l_varlist.lv_refcount == DO_NOT_FREE_CNT
 	    && fc->l_vars.dv_refcount == DO_NOT_FREE_CNT
-	    && fc->l_avars.dv_refcount == DO_NOT_FREE_CNT)
+	    && fc->l_avars.dv_refcount == DO_NOT_FREE_CNT
+	    && !fc->ref_by_lambda)
     {
 	free_funccal(fc, FALSE);
     }
@@ -25871,7 +26097,8 @@ can_free_funccal(funccall_T *fc, int cop
 {
     return (fc->l_varlist.lv_copyID != copyID
 	    && fc->l_vars.dv_copyID != copyID
-	    && fc->l_avars.dv_copyID != copyID);
+	    && fc->l_avars.dv_copyID != copyID
+	    && (!fc->ref_by_lambda && fc->lambda_copyID != copyID));
 }
 
 /*
diff --git a/src/ex_cmds2.c b/src/ex_cmds2.c
--- a/src/ex_cmds2.c
+++ b/src/ex_cmds2.c
@@ -1121,8 +1121,11 @@ remove_timer(timer_T *timer)
     static void
 free_timer(timer_T *timer)
 {
+    if (timer->tr_partial == NULL)
+	func_unref(timer->tr_callback);
+    else
+	partial_unref(timer->tr_partial);
     vim_free(timer->tr_callback);
-    partial_unref(timer->tr_partial);
     vim_free(timer);
 }
 
diff --git a/src/testdir/test_alot.vim b/src/testdir/test_alot.vim
--- a/src/testdir/test_alot.vim
+++ b/src/testdir/test_alot.vim
@@ -16,6 +16,7 @@ source test_glob2regpat.vim
 source test_goto.vim
 source test_help_tagjump.vim
 source test_join.vim
+source test_lambda.vim
 source test_lispwords.vim
 source test_matchstrpos.vim
 source test_menu.vim
diff --git a/src/testdir/test_channel.vim b/src/testdir/test_channel.vim
--- a/src/testdir/test_channel.vim
+++ b/src/testdir/test_channel.vim
@@ -182,6 +182,19 @@ func s:communicate(port)
   endif
   call assert_equal('got it', s:responseMsg)
 
+  if exists('*lambda')
+    let s:responseMsg = ''
+    call ch_sendexpr(handle, 'hello!', {'callback': lambda(':return s:RequestHandler(a:1, a:2)')})
+    call s:waitFor('exists("s:responseHandle")')
+    if !exists('s:responseHandle')
+      call assert_false(1, 's:responseHandle was not set')
+    else
+      call assert_equal(handle, s:responseHandle)
+      unlet s:responseHandle
+    endif
+    call assert_equal('got it', s:responseMsg)
+  endif
+
   " Collect garbage, tests that our handle isn't collected.
   call test_garbagecollect_now()
 
@@ -1134,6 +1147,32 @@ func Test_read_in_close_cb()
   endtry
 endfunc
 
+func Test_out_cb_lambda()
+  if !has('job') || !exists('*lambda')
+    return
+  endif
+  call ch_log('Test_out_cb_lambda()')
+
+  let job = job_start(s:python . " test_channel_pipe.py",
+  \ {'out_cb': lambda(":let s:outmsg = 'lambda: ' . a:2"),
+  \ 'out_mode': 'json',
+  \ 'err_cb': lambda(":let s:errmsg = 'lambda: ' . a:2"),
+  \ 'err_mode': 'json'})
+  call assert_equal("run", job_status(job))
+  try
+    let s:outmsg = ''
+    let s:errmsg = ''
+    call ch_sendraw(job, "echo [0, \"hello\"]\n")
+    call ch_sendraw(job, "echoerr [0, \"there\"]\n")
+    call s:waitFor('s:outmsg != ""')
+    call assert_equal("lambda: hello", s:outmsg)
+    call s:waitFor('s:errmsg != ""')
+    call assert_equal("lambda: there", s:errmsg)
+  finally
+    call job_stop(job)
+  endtry
+endfunc
+
 """"""""""
 
 let s:unletResponse = ''
@@ -1350,6 +1389,28 @@ func Test_collapse_buffers()
   bwipe!
 endfunc
 
+function s:test_close_lambda(port)
+  if !exists('*lambda')
+    return
+  endif
+
+  let handle = ch_open('localhost:' . a:port, s:chopt)
+  if ch_status(handle) == "fail"
+    call assert_false(1, "Can't open channel")
+    return
+  endif
+  let s:close_ret = ''
+  call ch_setoptions(handle, {'close_cb': lambda("let s:close_ret = 'closed'")})
+
+  call assert_equal('', ch_evalexpr(handle, 'close me'))
+  call s:waitFor('"closed" == s:close_ret')
+  call assert_equal('closed', s:close_ret)
+endfunc
+
+func Test_close_lambda()
+  call ch_log('Test_close_lambda()')
+  call s:run_server('s:test_close_lambda')
+endfunc
 
 " Uncomment this to see what happens, output is in src/testdir/channellog.
 " call ch_logfile('channellog', 'w')
diff --git a/src/testdir/test_lambda.vim b/src/testdir/test_lambda.vim
new file mode 100644
--- /dev/null
+++ b/src/testdir/test_lambda.vim
@@ -0,0 +1,252 @@
+function! Test_lambda_with_filter()
+  let s:x = 2
+  call assert_equal([2, 3], filter([1, 2, 3], lambda('return a:1 >= s:x')))
+endfunction
+
+function! Test_lambda_with_map()
+  let s:x = 1
+  call assert_equal([2, 3, 4], map([1, 2, 3], lambda('return a:1 + s:x')))
+endfunction
+
+function! Test_lambda_with_sort()
+  call assert_equal([1, 2, 3, 4, 7], sort([3,7,2,1,4], lambda('return a:1 - a:2')))
+endfunction
+
+function! Test_lambda_in_local_variable()
+  let l:X = lambda("let x = 1 | return x + a:1")
+  call assert_equal(2, l:X(1))
+  call assert_equal(3, l:X(2))
+endfunction
+
+function! Test_lambda_capture_by_reference()
+  let v = 1
+  let l:F = lambda('return a:1 + v')
+  let v = 2
+  call assert_equal(12, l:F(10))
+endfunction
+
+function! Test_lambda_side_effect()
+  function! s:update_and_return(arr)
+    let a:arr[1] = 5
+    return a:arr
+  endfunction
+
+  function! s:foo(arr)
+    return lambda('return s:update_and_return(a:arr)')
+  endfunction
+
+  let arr = [3,2,1]
+  call assert_equal([3, 5, 1], s:foo(arr)())
+endfunction
+
+function! Test_lambda_refer_local_variable_from_other_scope()
+  function! s:foo(X)
+    return a:X() " refer l:x in s:bar()
+  endfunction
+
+  function! s:bar()
+    let x = 123
+    return s:foo(lambda('return x'))
+  endfunction
+
+  call assert_equal(123, s:bar())
+endfunction
+
+function! Test_lambda_do_not_share_local_variable()
+  function! s:define_funcs()
+    let l:One = lambda('let a = 111 | return a')
+    let l:Two = lambda('return exists("a") ? a : "no"')
+    return [l:One, l:Two]
+  endfunction
+
+  let l:F = s:define_funcs()
+
+  call assert_equal('no', l:F[1]())
+  call assert_equal(111, l:F[0]())
+  call assert_equal('no', l:F[1]())
+endfunction
+
+function! Test_lambda_closure()
+  function! s:foo()
+    let x = 0
+    return lambda("let x += 1 | return x")
+  endfunction
+
+  let l:F = s:foo()
+  call assert_equal(1, l:F())
+  call assert_equal(2, l:F())
+  call assert_equal(3, l:F())
+  call assert_equal(4, l:F())
+endfunction
+
+function! Test_lambda_with_a_var()
+  function! s:foo()
+    let x = 2
+    return lambda('return a:000 + [x]')
+  endfunction
+  function! s:bar()
+    return s:foo()(1)
+  endfunction
+
+  call assert_equal([1, 2], s:bar())
+endfunction
+
+function! Test_lambda_in_lambda()
+  let l:Counter_generator = lambda(':let init = a:1 | return lambda("let init += 1 | return init")')
+  let l:Counter = l:Counter_generator(0)
+  let l:Counter2 = l:Counter_generator(9)
+
+  call assert_equal(1, l:Counter())
+  call assert_equal(2, l:Counter())
+  call assert_equal(3, l:Counter())
+
+  call assert_equal(10, l:Counter2())
+  call assert_equal(11, l:Counter2())
+  call assert_equal(12, l:Counter2())
+endfunction
+
+function! Test_lambda_unlet()
+  function! s:foo()
+    let x = 1
+    call lambda('unlet x')()
+    return l:
+  endfunction
+
+  call assert_false(has_key(s:foo(), 'x'))
+endfunction
+
+function! Test_lambda_call_lambda_from_lambda()
+  function! s:foo(x)
+    let l:F1 = lambda('
+    \ return lambda("return a:x")')
+    return lambda('return l:F1()')
+  endfunction
+
+  let l:F = s:foo(1)
+  call assert_equal(1, l:F()())
+endfunction
+
+function! Test_lambda_garbage_collection()
+  function! s:new_counter()
+    let c = 0
+    return lambda('let c += 1 | return c')
+  endfunction
+
+  let l:C = s:new_counter()
+  call garbagecollect()
+  call assert_equal(1, l:C())
+  call assert_equal(2, l:C())
+  call assert_equal(3, l:C())
+  call assert_equal(4, l:C())
+endfunction
+
+function! Test_lambda_delfunc()
+  function! s:gen()
+    let pl = l:
+    let l:Hoge = lambda('return get(pl, "Hoge", get(pl, "Fuga", lambda("")))')
+    let l:Fuga = l:Hoge
+    delfunction l:Hoge
+    return l:Fuga
+  endfunction
+
+  let l:F = s:gen()
+  call assert_fails(':call l:F()', 'E117:')
+endfunction
+
+function! Test_lambda_gen_lambda_from_funcdef()
+  function! s:GetFuncDef(fr)
+    redir => str
+    silent execute 'function a:fr'
+    redir END
+    delfunction a:fr
+    let lines = split(str, '\n')
+    let lines = map(lines, 'substitute(v:val, "\\\m^\\\d*\\\s*  ", "", "")')
+    let lines = lines[1:-2]
+    return join(lines, "\n")
+  endfunction
+
+  function! s:NewCounter()
+    let n = 0
+    function! s:Countup()
+      let n += 1
+      return n
+    endfunction
+    return lambda(s:GetFuncDef(function('s:Countup')))
+  endfunction
+
+  let l:C = s:NewCounter()
+  let l:D = s:NewCounter()
+
+  call assert_equal(1, l:C())
+  call assert_equal(2, l:C())
+  call assert_equal(3, l:C())
+  call assert_equal(1, l:D())
+  call assert_equal(2, l:D())
+  call assert_equal(3, l:D())
+endfunction
+
+function! Test_lambda_scope()
+  function! s:NewCounter()
+    let c = 0
+    return lambda('let c += 1 | return c')
+  endfunction
+
+  function! s:NewCounter2()
+    return lambda('let c += 100 | return c')
+  endfunction
+
+  let l:C = s:NewCounter()
+  let l:D = s:NewCounter2()
+
+  call assert_equal(1, l:C())
+  call assert_fails(':call l:D()', 'E15:') " E121: then E15:
+  call assert_equal(2, l:C())
+endfunction
+
+function! Test_lambdas_share_scope()
+  function! s:New()
+    let c = 0
+    let l:Inc0 = lambda('let c += 1 | return c')
+    let l:Dec0 = lambda('let c -= 1 | return c')
+    return [l:Inc0, l:Dec0]
+  endfunction
+
+  let [l:Inc, l:Dec] = s:New()
+
+  call assert_equal(1, l:Inc())
+  call assert_equal(2, l:Inc())
+  call assert_equal(1, l:Dec())
+endfunction
+
+function! Test_lambda_in_sandbox()
+  call assert_fails(':sandbox call lambda("")', 'E48:')
+endfunction
+
+function! Test_lambda_with_timer()
+  if !has('timers')
+    return
+  endif
+
+  let s:n = 0
+  let s:timer_id = 0
+  function! s:Foo()
+    let n = 0
+    let s:timer_id = timer_start(50, lambda("let n += 1 | let s:n = n | echo n"), {"repeat": -1})
+  endfunction
+
+  call s:Foo()
+  sleep 200ms
+  " do not collect lambda nor l:n in lambda
+  call test_garbagecollect_now()
+  let m = s:n
+  sleep 200ms
+  call timer_stop(s:timer_id)
+  call assert_true(m > 1)
+  call assert_true(s:n > m + 1)
+  call assert_true(s:n < 9)
+endfunction
+
+function! Test_lambda_with_partial()
+  let l:Cb = function(lambda(":return ['zero', a:1, a:2, a:3]"), ['one', 'two'])
+  call assert_equal(['zero', 'one', 'two', 'three'], l:Cb('three'))
+endfunction

Raspunde prin e-mail lui