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(&current_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(&current_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.

Raspunde prin e-mail lui