Patch 7.4.2120
Problem: User defined functions can't be a closure.
Solution: Add the "closure" argument. Allow using :unlet on a bound
variable. (Yasuhiro Matsumoto, Ken Takata)
Files: runtime/doc/eval.txt, src/testdir/test_lambda.vim, src/userfunc.c,
src/eval.c src/proto/userfunc.pro
*** ../vim-7.4.2119/runtime/doc/eval.txt 2016-07-29 22:14:39.031998331
+0200
--- runtime/doc/eval.txt 2016-07-29 22:25:24.133969732 +0200
***************
*** 1231,1236 ****
--- 1240,1246 ----
:let Bar = Foo(4)
:echo Bar(6)
< 5
+ See also |:func-closure|.
Examples for using a lambda expression with |sort()|, |map()| and |filter()|:
>
:echo map([1, 2, 3], {idx, val -> val + 1})
***************
*** 8086,8096 ****
See |:verbose-cmd| for more information.
*E124* *E125* *E853* *E884*
! :fu[nction][!] {name}([arguments]) [range] [abort] [dict]
Define a new function by the name {name}. The name
must be made of alphanumeric characters and '_', and
must start with a capital or "s:" (see above). Note
! that using "b:" or "g:" is not allowed.
{name} can also be a |Dictionary| entry that is a
|Funcref|: >
--- 8218,8231 ----
See |:verbose-cmd| for more information.
*E124* *E125* *E853* *E884*
! :fu[nction][!] {name}([arguments]) [range] [abort] [dict] [closure]
Define a new function by the name {name}. The name
must be made of alphanumeric characters and '_', and
must start with a capital or "s:" (see above). Note
! that using "b:" or "g:" is not allowed. (since patch
! 7.4.260 E884 is given if the function name has a colon
! in the name, e.g. for "foo:bar()". Before that patch
! no error was given).
{name} can also be a |Dictionary| entry that is a
|Funcref|: >
***************
*** 8109,8115 ****
For the {arguments} see |function-argument|.
! *a:firstline* *a:lastline*
When the [range] argument is added, the function is
expected to take care of a range itself. The range is
passed as "a:firstline" and "a:lastline". If [range]
--- 8244,8250 ----
For the {arguments} see |function-argument|.
! *:func-range* *a:firstline* *a:lastline*
When the [range] argument is added, the function is
expected to take care of a range itself. The range is
passed as "a:firstline" and "a:lastline". If [range]
***************
*** 8118,8131 ****
of each line. See |function-range-example|.
The cursor is still moved to the first line of the
range, as is the case with all Ex commands.
!
When the [abort] argument is added, the function will
abort as soon as an error is detected.
!
When the [dict] argument is added, the function must
be invoked through an entry in a |Dictionary|. The
local variable "self" will then be set to the
dictionary. See |Dictionary-function|.
*function-search-undo*
The last used search pattern and the redo command "."
--- 8253,8288 ----
of each line. See |function-range-example|.
The cursor is still moved to the first line of the
range, as is the case with all Ex commands.
! *:func-abort*
When the [abort] argument is added, the function will
abort as soon as an error is detected.
! *:func-dict*
When the [dict] argument is added, the function must
be invoked through an entry in a |Dictionary|. The
local variable "self" will then be set to the
dictionary. See |Dictionary-function|.
+ *:func-closure* *E932*
+ When the [closure] argument is added, the function
+ can access variables and arguments from the outer
+ scope. This is usually called a closure. In this
+ example Bar() uses "x" from the scope of Foo(). It
+ remains referenced even after Foo() returns: >
+ :function! Foo()
+ : let x = 0
+ : function! Bar() closure
+ : let x += 1
+ : return x
+ : endfunction
+ : return function('Bar')
+ :endfunction
+
+ :let F = Foo()
+ :echo F()
+ < 1 >
+ :echo F()
+ < 2 >
+ :echo F()
+ < 3
*function-search-undo*
The last used search pattern and the redo command "."
*** ../vim-7.4.2119/src/testdir/test_lambda.vim 2016-07-29 22:14:39.035998293
+0200
--- src/testdir/test_lambda.vim 2016-07-29 22:26:55.925119254 +0200
***************
*** 1,3 ****
--- 1,5 ----
+ " Test for lambda and closure
+
function! Test_lambda_with_filter()
let s:x = 2
call assert_equal([2, 3], filter([1, 2, 3], {i, v -> v >= s:x}))
***************
*** 100,106 ****
call assert_equal('no', l:F[1]())
endfunction
! function! Test_lambda_closure()
function! s:foo()
let x = 0
return {-> [execute("let x += 1"), x][-1]}
--- 102,108 ----
call assert_equal('no', l:F[1]())
endfunction
! function! Test_lambda_closure_counter()
function! s:foo()
let x = 0
return {-> [execute("let x += 1"), x][-1]}
***************
*** 209,211 ****
--- 211,245 ----
let Fact = {f -> {x -> x == 0 ? 1 : x * f(x - 1)}}
call assert_equal(120, Z(Fact)(5))
endfunction
+
+ function! Test_closure_counter()
+ function! s:foo()
+ let x = 0
+ function! s:bar() closure
+ let x += 1
+ return x
+ endfunction
+ return function('s:bar')
+ endfunction
+
+ let l:F = s:foo()
+ call test_garbagecollect_now()
+ 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_closure_unlet()
+ function! s:foo()
+ let x = 1
+ function! s:bar() closure
+ unlet x
+ endfunction
+ call s:bar()
+ return l:
+ endfunction
+
+ call assert_false(has_key(s:foo(), 'x'))
+ call test_garbagecollect_now()
+ endfunction
*** ../vim-7.4.2119/src/userfunc.c 2016-07-29 22:14:39.035998293 +0200
--- src/userfunc.c 2016-07-29 22:29:11.151865988 +0200
***************
*** 59,64 ****
--- 59,65 ----
#define FC_ABORT 1 /* abort function on error */
#define FC_RANGE 2 /* function accepts range */
#define FC_DICT 4 /* Dict function, uses "self" */
+ #define FC_CLOSURE 8 /* closure, uses outer scope variables */
/* From user function to hashitem and back. */
#define UF2HIKEY(fp) ((fp)->uf_name)
***************
*** 312,318 ****
if (evaluate)
{
! int len;
char_u *p;
sprintf((char*)name, "<lambda>%d", ++lambda_no);
--- 313,319 ----
if (evaluate)
{
! int len, flags = 0;
char_u *p;
sprintf((char*)name, "<lambda>%d", ++lambda_no);
***************
*** 341,346 ****
--- 342,348 ----
fp->uf_lines = newlines;
if (current_funccal != NULL && eval_lavars)
{
+ flags |= FC_CLOSURE;
fp->uf_scoped = current_funccal;
current_funccal->fc_refcount++;
if (ga_grow(¤t_funccal->fc_funcs, 1) == FAIL)
***************
*** 361,367 ****
func_do_profile(fp);
#endif
fp->uf_varargs = TRUE;
! fp->uf_flags = 0;
fp->uf_calls = 0;
fp->uf_script_ID = current_SID;
--- 363,369 ----
func_do_profile(fp);
#endif
fp->uf_varargs = TRUE;
! fp->uf_flags = flags;
fp->uf_calls = 0;
fp->uf_script_ID = current_SID;
***************
*** 1487,1492 ****
--- 1489,1496 ----
MSG_PUTS(" range");
if (fp->uf_flags & FC_DICT)
MSG_PUTS(" dict");
+ if (fp->uf_flags & FC_CLOSURE)
+ MSG_PUTS(" closure");
msg_clr_eos();
if (p_verbose > 0)
last_set_msg(fp->uf_script_ID);
***************
*** 1948,1954 ****
if (get_function_args(&p, ')', &newargs, &varargs, eap->skip) == FAIL)
goto errret_2;
! /* find extra arguments "range", "dict" and "abort" */
for (;;)
{
p = skipwhite(p);
--- 1952,1958 ----
if (get_function_args(&p, ')', &newargs, &varargs, eap->skip) == FAIL)
goto errret_2;
! /* find extra arguments "range", "dict", "abort" and "closure" */
for (;;)
{
p = skipwhite(p);
***************
*** 1967,1972 ****
--- 1971,1981 ----
flags |= FC_ABORT;
p += 5;
}
+ else if (STRNCMP(p, "closure", 7) == 0)
+ {
+ flags |= FC_CLOSURE;
+ p += 7;
+ }
else
break;
}
***************
*** 2299,2305 ****
}
fp->uf_args = newargs;
fp->uf_lines = newlines;
! fp->uf_scoped = NULL;
#ifdef FEAT_PROFILE
fp->uf_tml_count = NULL;
fp->uf_tml_total = NULL;
--- 2308,2332 ----
}
fp->uf_args = newargs;
fp->uf_lines = newlines;
! if ((flags & FC_CLOSURE) != 0)
! {
! if (current_funccal == NULL)
! {
! emsg_funcname(N_("E932 Closure function should not be at top level:
%s"),
! name);
! goto erret;
! }
! fp->uf_scoped = current_funccal;
! current_funccal->fc_refcount++;
! if (ga_grow(¤t_funccal->fc_funcs, 1) == FAIL)
! goto erret;
! ((ufunc_T **)current_funccal->fc_funcs.ga_data)
! [current_funccal->fc_funcs.ga_len++] = fp;
! func_ref(current_funccal->func->uf_name);
! }
! else
! fp->uf_scoped = NULL;
!
#ifdef FEAT_PROFILE
fp->uf_tml_count = NULL;
fp->uf_tml_total = NULL;
***************
*** 3536,3541 ****
--- 3563,3604 ----
}
/*
+ * Search hashitem in parent scope.
+ */
+ hashitem_T *
+ find_hi_in_scoped_ht(char_u *name, char_u **varname, hashtab_T **pht)
+ {
+ funccall_T *old_current_funccal = current_funccal;
+ hashtab_T *ht;
+ hashitem_T *hi = NULL;
+
+ if (current_funccal == NULL || current_funccal->func->uf_scoped == NULL)
+ return NULL;
+
+ /* 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);
+ if (ht != NULL && **varname != NUL)
+ {
+ hi = hash_find(ht, *varname);
+ if (!HASHITEM_EMPTY(hi))
+ {
+ *pht = ht;
+ break;
+ }
+ }
+ if (current_funccal == current_funccal->func->uf_scoped)
+ break;
+ current_funccal = current_funccal->func->uf_scoped;
+ }
+ current_funccal = old_current_funccal;
+
+ return hi;
+ }
+
+ /*
* Search variable in parent scope.
*/
dictitem_T *
*** ../vim-7.4.2119/src/eval.c 2016-07-29 22:14:39.035998293 +0200
--- src/eval.c 2016-07-29 22:26:55.925119254 +0200
***************
*** 2837,2843 ****
}
}
hi = hash_find(ht, varname);
! if (!HASHITEM_EMPTY(hi))
{
di = HI2DI(hi);
if (var_check_fixed(di->di_flags, name, FALSE)
--- 2837,2845 ----
}
}
hi = hash_find(ht, varname);
! if (HASHITEM_EMPTY(hi))
! hi = find_hi_in_scoped_ht(name, &varname, &ht);
! if (hi != NULL && !HASHITEM_EMPTY(hi))
{
di = HI2DI(hi);
if (var_check_fixed(di->di_flags, name, FALSE)
*** ../vim-7.4.2119/src/proto/userfunc.pro 2016-07-29 22:14:39.035998293
+0200
--- src/proto/userfunc.pro 2016-07-29 22:26:55.925119254 +0200
***************
*** 46,51 ****
--- 46,52 ----
void restore_current_funccal(void *f);
void list_func_vars(int *first);
dict_T *get_current_funccal_dict(hashtab_T *ht);
+ hashitem_T *find_hi_in_scoped_ht(char_u *name, char_u **varname, hashtab_T
**pht);
dictitem_T *find_var_in_scoped_ht(char_u *name, char_u **varname, int
no_autoload);
int set_ref_in_previous_funccal(int copyID);
int set_ref_in_call_stack(int copyID);
*** ../vim-7.4.2119/src/version.c 2016-07-29 22:14:39.035998293 +0200
--- src/version.c 2016-07-29 22:19:50.353073117 +0200
***************
*** 760,761 ****
--- 760,763 ----
{ /* Add new patch number below this line */
+ /**/
+ 2120,
/**/
--
There are only two hard things in programming: Cache invalidation,
naming things and off-by-one errors.
/// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net \\\
/// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\ an exciting new programming language -- http://www.Zimbu.org ///
\\\ help me help AIDS victims -- http://ICCF-Holland.org ///
--
--
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 [email protected].
For more options, visit https://groups.google.com/d/optout.