Patch 8.2.0695
Problem:    Vim9: cannot define a function inside a function.
Solution:   Initial support for :def inside :def.
Files:      src/userfunc.c, src/proto/userfunc.pro, src/vim9compile.c,
            src/vim9execute.c, src/testdir/test_vim9_func.vim


*** ../vim-8.2.0694/src/userfunc.c      2020-05-03 15:38:12.983700666 +0200
--- src/userfunc.c      2020-05-04 23:13:42.067556908 +0200
***************
*** 329,334 ****
--- 329,347 ----
  }
  
  /*
+  * Get a name for a lambda.  Returned in static memory.
+  */
+     char_u *
+ get_lambda_name(void)
+ {
+     static char_u   name[30];
+     static int            lambda_no = 0;
+ 
+     sprintf((char*)name, "<lambda>%d", ++lambda_no);
+     return name;
+ }
+ 
+ /*
   * Parse a lambda expression and get a Funcref from "*arg".
   * Return OK or FAIL.  Returns NOTDONE for dict or {expr}.
   */
***************
*** 344,350 ****
      int               ret;
      char_u    *start = skipwhite(*arg + 1);
      char_u    *s, *e;
-     static int        lambda_no = 0;
      int               *old_eval_lavars = eval_lavars_used;
      int               eval_lavars = FALSE;
  
--- 357,362 ----
***************
*** 392,400 ****
      {
        int         len, flags = 0;
        char_u      *p;
!       char_u      name[20];
! 
!       sprintf((char*)name, "<lambda>%d", ++lambda_no);
  
        fp = alloc_clear(offsetof(ufunc_T, uf_name) + STRLEN(name) + 1);
        if (fp == NULL)
--- 404,410 ----
      {
        int         len, flags = 0;
        char_u      *p;
!       char_u      *name = get_lambda_name();
  
        fp = alloc_clear(offsetof(ufunc_T, uf_name) + STRLEN(name) + 1);
        if (fp == NULL)
***************
*** 2364,2373 ****
  }
  
  /*
!  * ":function"
   */
!     void
! ex_function(exarg_T *eap)
  {
      char_u    *theline;
      char_u    *line_to_free = NULL;
--- 2374,2384 ----
  }
  
  /*
!  * ":function" also supporting nested ":def".
!  * Returns a pointer to the function or NULL if no function defined.
   */
!     ufunc_T *
! def_function(exarg_T *eap, char_u *name_arg, void *context)
  {
      char_u    *theline;
      char_u    *line_to_free = NULL;
***************
*** 2375,2381 ****
      int               c;
      int               saved_did_emsg;
      int               saved_wait_return = need_wait_return;
!     char_u    *name = NULL;
      int               is_global = FALSE;
      char_u    *p;
      char_u    *arg;
--- 2386,2392 ----
      int               c;
      int               saved_did_emsg;
      int               saved_wait_return = need_wait_return;
!     char_u    *name = name_arg;
      int               is_global = FALSE;
      char_u    *p;
      char_u    *arg;
***************
*** 2387,2393 ****
      int               varargs = FALSE;
      int               flags = 0;
      char_u    *ret_type = NULL;
!     ufunc_T   *fp;
      int               overwrite = FALSE;
      int               indent;
      int               nesting;
--- 2398,2404 ----
      int               varargs = FALSE;
      int               flags = 0;
      char_u    *ret_type = NULL;
!     ufunc_T   *fp = NULL;
      int               overwrite = FALSE;
      int               indent;
      int               nesting;
***************
*** 2429,2435 ****
            }
        }
        eap->nextcmd = check_nextcmd(eap->arg);
!       return;
      }
  
      /*
--- 2440,2446 ----
            }
        }
        eap->nextcmd = check_nextcmd(eap->arg);
!       return NULL;
      }
  
      /*
***************
*** 2469,2475 ****
        if (*p == '/')
            ++p;
        eap->nextcmd = check_nextcmd(p);
!       return;
      }
  
      ga_init(&newargs);
--- 2480,2486 ----
        if (*p == '/')
            ++p;
        eap->nextcmd = check_nextcmd(p);
!       return NULL;
      }
  
      ga_init(&newargs);
***************
*** 2493,2517 ****
       * g:func     global function name, same as "func"
       */
      p = eap->arg;
!     name = trans_function_name(&p, &is_global, eap->skip,
!                                                TFN_NO_AUTOLOAD, &fudi, NULL);
!     paren = (vim_strchr(p, '(') != NULL);
!     if (name == NULL && (fudi.fd_dict == NULL || !paren) && !eap->skip)
      {
!       /*
!        * Return on an invalid expression in braces, unless the expression
!        * evaluation has been cancelled due to an aborting error, an
!        * interrupt, or an exception.
!        */
!       if (!aborting())
        {
!           if (!eap->skip && fudi.fd_newkey != NULL)
!               semsg(_(e_dictkey), fudi.fd_newkey);
!           vim_free(fudi.fd_newkey);
!           return;
        }
-       else
-           eap->skip = TRUE;
      }
  
      // An error in a function call during evaluation of an expression in magic
--- 2504,2537 ----
       * g:func     global function name, same as "func"
       */
      p = eap->arg;
!     if (name_arg != NULL)
      {
!       // nested function, argument is (args).
!       paren = TRUE;
!       CLEAR_FIELD(fudi);
!     }
!     else
!     {
!       name = trans_function_name(&p, &is_global, eap->skip,
!                                                TFN_NO_AUTOLOAD, &fudi, NULL);
!       paren = (vim_strchr(p, '(') != NULL);
!       if (name == NULL && (fudi.fd_dict == NULL || !paren) && !eap->skip)
        {
!           /*
!            * Return on an invalid expression in braces, unless the expression
!            * evaluation has been cancelled due to an aborting error, an
!            * interrupt, or an exception.
!            */
!           if (!aborting())
!           {
!               if (!eap->skip && fudi.fd_newkey != NULL)
!                   semsg(_(e_dictkey), fudi.fd_newkey);
!               vim_free(fudi.fd_newkey);
!               return NULL;
!           }
!           else
!               eap->skip = TRUE;
        }
      }
  
      // An error in a function call during evaluation of an expression in magic
***************
*** 2596,2602 ****
  
      ga_init2(&newlines, (int)sizeof(char_u *), 3);
  
!     if (!eap->skip)
      {
        // Check the name of the function.  Unless it's a dictionary function
        // (that we are overwriting).
--- 2616,2622 ----
  
      ga_init2(&newlines, (int)sizeof(char_u *), 3);
  
!     if (!eap->skip && name_arg == NULL)
      {
        // Check the name of the function.  Unless it's a dictionary function
        // (that we are overwriting).
***************
*** 3255,3261 ****
  
      // ":def Func()" needs to be compiled
      if (eap->cmdidx == CMD_def)
!       compile_def_function(fp, FALSE, NULL);
  
      goto ret_free;
  
--- 3275,3281 ----
  
      // ":def Func()" needs to be compiled
      if (eap->cmdidx == CMD_def)
!       compile_def_function(fp, FALSE, context);
  
      goto ret_free;
  
***************
*** 3269,3278 ****
      vim_free(skip_until);
      vim_free(line_to_free);
      vim_free(fudi.fd_newkey);
!     vim_free(name);
      vim_free(ret_type);
      did_emsg |= saved_did_emsg;
      need_wait_return |= saved_wait_return;
  }
  
  /*
--- 3289,3310 ----
      vim_free(skip_until);
      vim_free(line_to_free);
      vim_free(fudi.fd_newkey);
!     if (name != name_arg)
!       vim_free(name);
      vim_free(ret_type);
      did_emsg |= saved_did_emsg;
      need_wait_return |= saved_wait_return;
+ 
+     return fp;
+ }
+ 
+ /*
+  * ":function"
+  */
+     void
+ ex_function(exarg_T *eap)
+ {
+     def_function(eap, NULL, NULL);
  }
  
  /*
*** ../vim-8.2.0694/src/proto/userfunc.pro      2020-04-27 22:47:45.186176148 
+0200
--- src/proto/userfunc.pro      2020-05-04 22:44:39.358158835 +0200
***************
*** 2,7 ****
--- 2,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 *deref_func_name(char_u *name, int *lenp, partial_T **partialp, int 
no_autoload);
  void emsg_funcname(char *ermsg, char_u *name);
***************
*** 22,27 ****
--- 23,29 ----
  int call_func(char_u *funcname, int len, typval_T *rettv, int argcount_in, 
typval_T *argvars_in, funcexe_T *funcexe);
  char_u *trans_function_name(char_u **pp, int *is_global, int skip, int flags, 
funcdict_T *fdp, partial_T **partial);
  char_u *untrans_function_name(char_u *name);
+ ufunc_T *def_function(exarg_T *eap, char_u *name_arg, void *context);
  void ex_function(exarg_T *eap);
  int eval_fname_script(char_u *p);
  int translated_function_exists(char_u *name, int is_global);
*** ../vim-8.2.0694/src/vim9compile.c   2020-05-03 22:30:44.038682664 +0200
--- src/vim9compile.c   2020-05-04 23:23:22.838253954 +0200
***************
*** 101,106 ****
--- 101,107 ----
      int               lv_from_outer;  // when TRUE using ctx_outer scope
      int               lv_const;       // when TRUE cannot be assigned to
      int               lv_arg;         // when TRUE this is an argument
+     int               lv_func_idx;    // for nested function
  } lvar_T;
  
  /*
***************
*** 2615,2620 ****
--- 2616,2622 ----
      int               error = FCERR_NONE;
      ufunc_T   *ufunc;
      int               res = FAIL;
+     lvar_T    *lvar;
  
      if (varlen >= sizeof(namebuf))
      {
***************
*** 2641,2646 ****
--- 2643,2658 ----
        goto theend;
      }
  
+     // Check if the name is a nested function.
+     lvar = lookup_local(namebuf, varlen, cctx);
+     if (lvar != NULL && lvar->lv_func_idx > 0)
+     {
+       dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
+                                                          + lvar->lv_func_idx;
+       res = generate_CALL(cctx, dfunc->df_ufunc, argcount);
+       goto theend;
+     }
+ 
      // If we can find the function by name generate the right call.
      ufunc = find_func(name, FALSE, cctx);
      if (ufunc != NULL)
***************
*** 4049,4054 ****
--- 4061,4124 ----
  }
  
  /*
+  * Get a line from the compilation context, compatible with exarg_T getline().
+  * Return a pointer to the line in allocated memory.
+  * Return NULL for end-of-file or some error.
+  */
+     static char_u *
+ exarg_getline(
+       int c UNUSED,
+       void *cookie,
+       int indent UNUSED,
+       int do_concat UNUSED)
+ {
+     cctx_T  *cctx = (cctx_T *)cookie;
+ 
+     if (cctx->ctx_lnum == cctx->ctx_ufunc->uf_lines.ga_len)
+     {
+       iemsg("Heredoc got to end");
+       return NULL;
+     }
+     ++cctx->ctx_lnum;
+     return vim_strsave(((char_u **)cctx->ctx_ufunc->uf_lines.ga_data)
+                                                            [cctx->ctx_lnum]);
+ }
+ 
+ /*
+  * Compile a nested :def command.
+  */
+     static char_u *
+ compile_nested_function(exarg_T *eap, cctx_T *cctx)
+ {
+     char_u    *name_start = eap->arg;
+     char_u    *name_end = to_name_end(eap->arg, FALSE);
+     char_u    *name = get_lambda_name();
+     lvar_T    *lvar;
+     ufunc_T   *ufunc;
+ 
+     eap->arg = name_end;
+     eap->getline = exarg_getline;
+     eap->cookie = cctx;
+     eap->skip = cctx->ctx_skip == TRUE;
+     eap->forceit = FALSE;
+     ufunc = def_function(eap, name, cctx);
+ 
+     if (ufunc == NULL)
+       return NULL;
+ 
+     // Define a local variable for the function, but change the index to -1 to
+     // mark it as a function name.
+     lvar = reserve_local(cctx, name_start, name_end - name_start,
+                                                      TRUE, &t_func_unknown);
+     lvar->lv_idx = 0;
+     ++cctx->ctx_locals_count;  // doesn't count as a local variable
+     lvar->lv_func_idx = ufunc->uf_dfunc_idx;
+ 
+     // TODO: warning for trailing?
+     return (char_u *)"";
+ }
+ 
+ /*
   * Return the length of an assignment operator, or zero if there isn't one.
   */
      int
***************
*** 4077,4106 ****
      NULL
  };
  
- /*
-  * Get a line for "=<<".
-  * Return a pointer to the line in allocated memory.
-  * Return NULL for end-of-file or some error.
-  */
-     static char_u *
- heredoc_getline(
-       int c UNUSED,
-       void *cookie,
-       int indent UNUSED,
-       int do_concat UNUSED)
- {
-     cctx_T  *cctx = (cctx_T *)cookie;
- 
-     if (cctx->ctx_lnum == cctx->ctx_ufunc->uf_lines.ga_len)
-     {
-       iemsg("Heredoc got to end");
-       return NULL;
-     }
-     ++cctx->ctx_lnum;
-     return vim_strsave(((char_u **)cctx->ctx_ufunc->uf_lines.ga_data)
-                                                            [cctx->ctx_lnum]);
- }
- 
  typedef enum {
      dest_local,
      dest_option,
--- 4147,4152 ----
***************
*** 4394,4400 ****
        listitem_T *li;
  
        // [let] varname =<< [trim] {end}
!       eap->getline = heredoc_getline;
        eap->cookie = cctx;
        l = heredoc_get(eap, op + 3, FALSE);
  
--- 4440,4446 ----
        listitem_T *li;
  
        // [let] varname =<< [trim] {end}
!       eap->getline = exarg_getline;
        eap->cookie = cctx;
        l = heredoc_get(eap, op + 3, FALSE);
  
***************
*** 6299,6307 ****
        switch (ea.cmdidx)
        {
            case CMD_def:
            case CMD_function:
!                   // TODO: Nested function
!                   emsg("Nested function not implemented yet");
                    goto erret;
  
            case CMD_return:
--- 6345,6356 ----
        switch (ea.cmdidx)
        {
            case CMD_def:
+                   ea.arg = p;
+                   line = compile_nested_function(&ea, &cctx);
+                   break;
+ 
            case CMD_function:
!                   emsg(_("E1086: Cannot use :function inside :def"));
                    goto erret;
  
            case CMD_return:
*** ../vim-8.2.0694/src/vim9execute.c   2020-05-03 15:38:12.983700666 +0200
--- src/vim9execute.c   2020-05-04 22:03:56.702103704 +0200
***************
*** 206,211 ****
--- 206,216 ----
                       + dfunc->df_varcount + dfunc->df_closure_count) == FAIL)
        return FAIL;
  
+     // Closure may need the function context where it was defined.
+     // TODO: assuming current context.
+     ectx->ec_outer_stack = &ectx->ec_stack;
+     ectx->ec_outer_frame = ectx->ec_frame_idx;
+ 
      // Move the vararg-list to below the missing optional arguments.
      if (vararg_count > 0 && arg_to_add > 0)
        *STACK_TV_BOT(arg_to_add - 1) = *STACK_TV_BOT(-1);
*** ../vim-8.2.0694/src/testdir/test_vim9_func.vim      2020-05-03 
22:30:44.038682664 +0200
--- src/testdir/test_vim9_func.vim      2020-05-04 23:10:34.024230320 +0200
***************
*** 2,20 ****
  
  source check.vim
  source view_util.vim
! 
! " Check that "lines" inside ":def" results in an "error" message.
! func CheckDefFailure(lines, error)
!   call writefile(['def Func()'] + a:lines + ['enddef'], 'Xdef')
!   call assert_fails('so Xdef', a:error, a:lines)
!   call delete('Xdef')
! endfunc
! 
! func CheckScriptFailure(lines, error)
!   call writefile(a:lines, 'Xdef')
!   call assert_fails('so Xdef', a:error, a:lines)
!   call delete('Xdef')
! endfunc
  
  func Test_def_basic()
    def SomeFunc(): string
--- 2,8 ----
  
  source check.vim
  source view_util.vim
! source vim9.vim
  
  func Test_def_basic()
    def SomeFunc(): string
***************
*** 95,102 ****
    assert_equal('one', MyDefaultArgs('one'))
    assert_fails('call MyDefaultArgs("one", "two")', 'E118:')
  
!   call CheckScriptFailure(['def Func(arg: number = asdf)', 'enddef'], 
'E1001:')
!   call CheckScriptFailure(['def Func(arg: number = "text")', 'enddef'], 
'E1013: argument 1: type mismatch, expected number but got string')
  enddef
  
  func Test_call_default_args_from_func()
--- 83,99 ----
    assert_equal('one', MyDefaultArgs('one'))
    assert_fails('call MyDefaultArgs("one", "two")', 'E118:')
  
!   CheckScriptFailure(['def Func(arg: number = asdf)', 'enddef'], 'E1001:')
!   CheckScriptFailure(['def Func(arg: number = "text")', 'enddef'], 'E1013: 
argument 1: type mismatch, expected number but got string')
! enddef
! 
! def Test_nested_function()
!   def Nested(arg: string): string
!     return 'nested ' .. arg
!   enddef
!   assert_equal('nested function', Nested('function'))
! 
!   CheckDefFailure(['func Nested()', 'endfunc'], 'E1086:')
  enddef
  
  func Test_call_default_args_from_func()
***************
*** 721,725 ****
--- 718,730 ----
    unlet g:UseVararg
  enddef
  
+ def Test_nested_closure()
+   let local = 'text'
+   def Closure(arg: string): string
+     return local .. arg
+   enddef
+   assert_equal('text!!!', Closure('!!!'))
+ enddef
+ 
  
  " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
*** ../vim-8.2.0694/src/version.c       2020-05-03 22:57:26.973427368 +0200
--- src/version.c       2020-05-04 20:50:17.036641136 +0200
***************
*** 748,749 ****
--- 748,751 ----
  {   /* Add new patch number below this line */
+ /**/
+     695,
  /**/

-- 
hundred-and-one symptoms of being an internet addict:
43. You tell the kids they can't use the computer because "Daddy's got work to
    do" and you don't even have a job.

 /// 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].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/202005042125.044LPAW8008097%40masaka.moolenaar.net.

Raspunde prin e-mail lui