Patch 8.2.2635
Problem:    Vim9: cannot define an inline function.
Solution:   Make an inline function mostly work.
Files:      src/userfunc.c, src/errors.h, src/vim9compile.c, src/misc2.c,
            src/proto/vim9compile.pro, src/testdir/test_vim9_expr.vim


*** ../vim-8.2.2634/src/userfunc.c      2021-03-17 15:02:52.170478171 +0100
--- src/userfunc.c      2021-03-21 20:35:45.960329836 +0100
***************
*** 397,402 ****
--- 397,421 ----
      return OK;
  }
  
+     static int
+ parse_return_type(ufunc_T *fp, char_u *ret_type)
+ {
+     if (ret_type == NULL)
+       fp->uf_ret_type = &t_void;
+     else
+     {
+       char_u *p = ret_type;
+ 
+       fp->uf_ret_type = parse_type(&p, &fp->uf_type_list, TRUE);
+       if (fp->uf_ret_type == NULL)
+       {
+           fp->uf_ret_type = &t_void;
+           return FAIL;
+       }
+     }
+     return OK;
+ }
+ 
  /*
   * Register function "fp" as using "current_funccal" as its scope.
   */
***************
*** 536,542 ****
  }
  
  /*
!  * Parse a lambda expression and get a Funcref from "*arg".
   * "arg" points to the { in "{arg -> expr}" or the ( in "(arg) => expr"
   * When "types_optional" is TRUE optionally take argument types.
   * Return OK or FAIL.  Returns NOTDONE for dict or {expr}.
--- 555,1055 ----
  }
  
  /*
!  * Check if "*cmd" points to a function command and if so advance "*cmd" and
!  * return TRUE.
!  * Otherwise return FALSE;
!  * Do not consider "function(" to be a command.
!  */
!     static int
! is_function_cmd(char_u **cmd)
! {
!     char_u *p = *cmd;
! 
!     if (checkforcmd(&p, "function", 2))
!     {
!       if (*p == '(')
!           return FALSE;
!       *cmd = p;
!       return TRUE;
!     }
!     return FALSE;
! }
! 
! /*
!  * Read the body of a function, put every line in "newlines".
!  * "newlines" must already have been initialized.
!  * "eap->cmdidx" is CMD_function, CMD_def or CMD_block;
!  */
!     static int
! get_function_body(
!       exarg_T     *eap,
!       garray_T    *newlines,
!       char_u      *line_arg_in,
!       char_u      **line_to_free)
! {
!     linenr_T  sourcing_lnum_top = SOURCING_LNUM;
!     linenr_T  sourcing_lnum_off;
!     int               saved_wait_return = need_wait_return;
!     char_u    *line_arg = line_arg_in;
!     int               vim9_function = eap->cmdidx == CMD_def
!                                                  || eap->cmdidx == CMD_block;
! #define MAX_FUNC_NESTING 50
!     char      nesting_def[MAX_FUNC_NESTING];
!     int               nesting = 0;
!     getline_opt_T getline_options;
!     int               indent = 2;
!     char_u    *skip_until = NULL;
!     int               ret = FAIL;
!     int               is_heredoc = FALSE;
!     char_u    *heredoc_trimmed = NULL;
! 
!     // Detect having skipped over comment lines to find the return
!     // type.  Add NULL lines to keep the line count correct.
!     sourcing_lnum_off = get_sourced_lnum(eap->getline, eap->cookie);
!     if (SOURCING_LNUM < sourcing_lnum_off)
!     {
!       sourcing_lnum_off -= SOURCING_LNUM;
!       if (ga_grow(newlines, sourcing_lnum_off) == FAIL)
!           goto theend;
!       while (sourcing_lnum_off-- > 0)
!           ((char_u **)(newlines->ga_data))[newlines->ga_len++] = NULL;
!     }
! 
!     nesting_def[nesting] = vim9_function;
!     getline_options = vim9_function
!                               ? GETLINE_CONCAT_CONTBAR : GETLINE_CONCAT_CONT;
!     for (;;)
!     {
!       char_u  *theline;
!       char_u  *p;
!       char_u  *arg;
! 
!       if (KeyTyped)
!       {
!           msg_scroll = TRUE;
!           saved_wait_return = FALSE;
!       }
!       need_wait_return = FALSE;
! 
!       if (line_arg != NULL)
!       {
!           // Use eap->arg, split up in parts by line breaks.
!           theline = line_arg;
!           p = vim_strchr(theline, '\n');
!           if (p == NULL)
!               line_arg += STRLEN(line_arg);
!           else
!           {
!               *p = NUL;
!               line_arg = p + 1;
!           }
!       }
!       else
!       {
!           vim_free(*line_to_free);
!           if (eap->getline == NULL)
!               theline = getcmdline(':', 0L, indent, getline_options);
!           else
!               theline = eap->getline(':', eap->cookie, indent,
!                                                             getline_options);
!           *line_to_free = theline;
!       }
!       if (KeyTyped)
!           lines_left = Rows - 1;
!       if (theline == NULL)
!       {
!           // Use the start of the function for the line number.
!           SOURCING_LNUM = sourcing_lnum_top;
!           if (skip_until != NULL)
!               semsg(_(e_missing_heredoc_end_marker_str), skip_until);
!           else if (eap->cmdidx == CMD_def)
!               emsg(_(e_missing_enddef));
!           else if (eap->cmdidx == CMD_block)
!               emsg(_(e_missing_end_block));
!           else
!               emsg(_("E126: Missing :endfunction"));
!           goto theend;
!       }
! 
!       // Detect line continuation: SOURCING_LNUM increased more than one.
!       sourcing_lnum_off = get_sourced_lnum(eap->getline, eap->cookie);
!       if (SOURCING_LNUM < sourcing_lnum_off)
!           sourcing_lnum_off -= SOURCING_LNUM;
!       else
!           sourcing_lnum_off = 0;
! 
!       if (skip_until != NULL)
!       {
!           // Don't check for ":endfunc"/":enddef" between
!           // * ":append" and "."
!           // * ":python <<EOF" and "EOF"
!           // * ":let {var-name} =<< [trim] {marker}" and "{marker}"
!           if (heredoc_trimmed == NULL
!                   || (is_heredoc && skipwhite(theline) == theline)
!                   || STRNCMP(theline, heredoc_trimmed,
!                                                STRLEN(heredoc_trimmed)) == 0)
!           {
!               if (heredoc_trimmed == NULL)
!                   p = theline;
!               else if (is_heredoc)
!                   p = skipwhite(theline) == theline
!                                ? theline : theline + STRLEN(heredoc_trimmed);
!               else
!                   p = theline + STRLEN(heredoc_trimmed);
!               if (STRCMP(p, skip_until) == 0)
!               {
!                   VIM_CLEAR(skip_until);
!                   VIM_CLEAR(heredoc_trimmed);
!                   getline_options = vim9_function
!                               ? GETLINE_CONCAT_CONTBAR : GETLINE_CONCAT_CONT;
!                   is_heredoc = FALSE;
!               }
!           }
!       }
!       else
!       {
!           int c;
! 
!           // skip ':' and blanks
!           for (p = theline; VIM_ISWHITE(*p) || *p == ':'; ++p)
!               ;
! 
!           // Check for "endfunction", "enddef" or "}".
!           // When a ":" follows it must be a dict key; "enddef: value,"
!           if ((nesting == 0 && eap->cmdidx == CMD_block)
!                   ? *p == '}'
!                   : (checkforcmd(&p, nesting_def[nesting]
!                                               ? "enddef" : "endfunction", 4)
!                       && *p != ':'))
!           {
!               if (nesting-- == 0)
!               {
!                   char_u *nextcmd = NULL;
! 
!                   if (*p == '|' || *p == '}')
!                       nextcmd = p + 1;
!                   else if (line_arg != NULL && *skipwhite(line_arg) != NUL)
!                       nextcmd = line_arg;
!                   else if (*p != NUL && *p != (vim9_function ? '#' : '"')
!                                       && p_verbose > 0
!                                       && eap->cmdidx != CMD_block)
!                       give_warning2(eap->cmdidx == CMD_def
!                           ? (char_u *)_("W1001: Text found after :enddef: %s")
!                           : (char_u *)_("W22: Text found after :endfunction: 
%s"),
!                            p, TRUE);
!                   if (nextcmd != NULL)
!                   {
!                       // Another command follows. If the line came from "eap"
!                       // we can simply point into it, otherwise we need to
!                       // change "eap->cmdlinep".
!                       eap->nextcmd = nextcmd;
!                       if (*line_to_free != NULL)
!                       {
!                           vim_free(*eap->cmdlinep);
!                           *eap->cmdlinep = *line_to_free;
!                           *line_to_free = NULL;
!                       }
!                   }
!                   break;
!               }
!           }
! 
!           // Check for mismatched "endfunc" or "enddef".
!           // We don't check for "def" inside "func" thus we also can't check
!           // for "enddef".
!           // We continue to find the end of the function, although we might
!           // not find it.
!           else if (nesting_def[nesting])
!           {
!               if (checkforcmd(&p, "endfunction", 4) && *p != ':')
!                   emsg(_(e_mismatched_endfunction));
!           }
!           else if (eap->cmdidx == CMD_def && checkforcmd(&p, "enddef", 4))
!               emsg(_(e_mismatched_enddef));
! 
!           // Increase indent inside "if", "while", "for" and "try", decrease
!           // at "end".
!           if (indent > 2 && (*p == '}' || STRNCMP(p, "end", 3) == 0))
!               indent -= 2;
!           else if (STRNCMP(p, "if", 2) == 0
!                   || STRNCMP(p, "wh", 2) == 0
!                   || STRNCMP(p, "for", 3) == 0
!                   || STRNCMP(p, "try", 3) == 0)
!               indent += 2;
! 
!           // Check for defining a function inside this function.
!           // Only recognize "def" inside "def", not inside "function",
!           // For backwards compatibility, see Test_function_python().
!           c = *p;
!           if (is_function_cmd(&p)
!                   || (eap->cmdidx == CMD_def && checkforcmd(&p, "def", 3)))
!           {
!               if (*p == '!')
!                   p = skipwhite(p + 1);
!               p += eval_fname_script(p);
!               vim_free(trans_function_name(&p, NULL, TRUE, 0, NULL,
!                                                                 NULL, NULL));
!               if (*skipwhite(p) == '(')
!               {
!                   if (nesting == MAX_FUNC_NESTING - 1)
!                       emsg(_(e_function_nesting_too_deep));
!                   else
!                   {
!                       ++nesting;
!                       nesting_def[nesting] = (c == 'd');
!                       indent += 2;
!                   }
!               }
!           }
! 
!           // Check for ":append", ":change", ":insert".  Not for :def.
!           p = skip_range(p, FALSE, NULL);
!           if (!vim9_function
!               && ((p[0] == 'a' && (!ASCII_ISALPHA(p[1]) || p[1] == 'p'))
!                   || (p[0] == 'c'
!                       && (!ASCII_ISALPHA(p[1]) || (p[1] == 'h'
!                               && (!ASCII_ISALPHA(p[2]) || (p[2] == 'a'
!                                       && (STRNCMP(&p[3], "nge", 3) != 0
!                                           || !ASCII_ISALPHA(p[6])))))))
!                   || (p[0] == 'i'
!                       && (!ASCII_ISALPHA(p[1]) || (p[1] == 'n'
!                               && (!ASCII_ISALPHA(p[2])
!                                   || (p[2] == 's'
!                                       && (!ASCII_ISALPHA(p[3])
!                                               || p[3] == 'e'))))))))
!               skip_until = vim_strsave((char_u *)".");
! 
!           // Check for ":python <<EOF", ":tcl <<EOF", etc.
!           arg = skipwhite(skiptowhite(p));
!           if (arg[0] == '<' && arg[1] =='<'
!                   && ((p[0] == 'p' && p[1] == 'y'
!                                   && (!ASCII_ISALNUM(p[2]) || p[2] == 't'
!                                       || ((p[2] == '3' || p[2] == 'x')
!                                                  && !ASCII_ISALPHA(p[3]))))
!                       || (p[0] == 'p' && p[1] == 'e'
!                                   && (!ASCII_ISALPHA(p[2]) || p[2] == 'r'))
!                       || (p[0] == 't' && p[1] == 'c'
!                                   && (!ASCII_ISALPHA(p[2]) || p[2] == 'l'))
!                       || (p[0] == 'l' && p[1] == 'u' && p[2] == 'a'
!                                   && !ASCII_ISALPHA(p[3]))
!                       || (p[0] == 'r' && p[1] == 'u' && p[2] == 'b'
!                                   && (!ASCII_ISALPHA(p[3]) || p[3] == 'y'))
!                       || (p[0] == 'm' && p[1] == 'z'
!                                   && (!ASCII_ISALPHA(p[2]) || p[2] == 's'))
!                       ))
!           {
!               // ":python <<" continues until a dot, like ":append"
!               p = skipwhite(arg + 2);
!               if (STRNCMP(p, "trim", 4) == 0)
!               {
!                   // Ignore leading white space.
!                   p = skipwhite(p + 4);
!                   heredoc_trimmed = vim_strnsave(theline,
!                                                skipwhite(theline) - theline);
!               }
!               if (*p == NUL)
!                   skip_until = vim_strsave((char_u *)".");
!               else
!                   skip_until = vim_strnsave(p, skiptowhite(p) - p);
!               getline_options = GETLINE_NONE;
!               is_heredoc = TRUE;
!           }
! 
!           // Check for ":cmd v =<< [trim] EOF"
!           //       and ":cmd [a, b] =<< [trim] EOF"
!           //       and "lines =<< [trim] EOF" for Vim9
!           // Where "cmd" can be "let", "var", "final" or "const".
!           arg = skipwhite(skiptowhite(p));
!           if (*arg == '[')
!               arg = vim_strchr(arg, ']');
!           if (arg != NULL)
!           {
!               int found = (eap->cmdidx == CMD_def && arg[0] == '='
!                                            && arg[1] == '<' && arg[2] =='<');
! 
!               if (!found)
!                   // skip over the argument after "cmd"
!                   arg = skipwhite(skiptowhite(arg));
!               if (found || (arg[0] == '=' && arg[1] == '<' && arg[2] =='<'
!                       && (checkforcmd(&p, "let", 2)
!                           || checkforcmd(&p, "var", 3)
!                           || checkforcmd(&p, "final", 5)
!                           || checkforcmd(&p, "const", 5))))
!               {
!                   p = skipwhite(arg + 3);
!                   if (STRNCMP(p, "trim", 4) == 0)
!                   {
!                       // Ignore leading white space.
!                       p = skipwhite(p + 4);
!                       heredoc_trimmed = vim_strnsave(theline,
!                                                skipwhite(theline) - theline);
!                   }
!                   skip_until = vim_strnsave(p, skiptowhite(p) - p);
!                   getline_options = GETLINE_NONE;
!                   is_heredoc = TRUE;
!               }
!           }
!       }
! 
!       // Add the line to the function.
!       if (ga_grow(newlines, 1 + sourcing_lnum_off) == FAIL)
!           goto theend;
! 
!       // Copy the line to newly allocated memory.  get_one_sourceline()
!       // allocates 250 bytes per line, this saves 80% on average.  The cost
!       // is an extra alloc/free.
!       p = vim_strsave(theline);
!       if (p == NULL)
!           goto theend;
!       ((char_u **)(newlines->ga_data))[newlines->ga_len++] = p;
! 
!       // Add NULL lines for continuation lines, so that the line count is
!       // equal to the index in the growarray.
!       while (sourcing_lnum_off-- > 0)
!           ((char_u **)(newlines->ga_data))[newlines->ga_len++] = NULL;
! 
!       // Check for end of eap->arg.
!       if (line_arg != NULL && *line_arg == NUL)
!           line_arg = NULL;
!     }
! 
!     // Don't define the function when skipping commands or when an error was
!     // detected.
!     if (!eap->skip && !did_emsg)
!       ret = OK;
! 
! theend:
!     vim_free(skip_until);
!     vim_free(heredoc_trimmed);
!     need_wait_return |= saved_wait_return;
!     return ret;
! }
! 
! /*
!  * Handle the body of a lambda.  *arg points to the "{", process statements
!  * until the matching "}".
!  * When successful "rettv" is set to a funcref.
!  */
!     static int
! lambda_function_body(
!       char_u      **arg,
!       typval_T    *rettv,
!       evalarg_T   *evalarg,
!       garray_T    *newargs,
!       garray_T    *argtypes,
!       int         varargs,
!       garray_T    *default_args,
!       char_u      *ret_type)
! {
!     int               evaluate = evalarg != NULL
!                                     && (evalarg->eval_flags & EVAL_EVALUATE);
!     ufunc_T   *ufunc;
!     exarg_T   eap;
!     garray_T  newlines;
!     char_u    *cmdline = NULL;
!     int               ret = FAIL;
!     char_u    *line_to_free = NULL;
!     partial_T *pt;
!     char_u    *name;
!     int               lnum_save = -1;
!     linenr_T  sourcing_lnum_top = SOURCING_LNUM;
! 
!     CLEAR_FIELD(eap);
!     eap.cmdidx = CMD_block;
!     eap.forceit = FALSE;
!     eap.arg = *arg + 1;
!     eap.cmdlinep = &cmdline;
!     eap.skip = !evaluate;
!     if (evalarg->eval_cctx != NULL)
!       fill_exarg_from_cctx(&eap, evalarg->eval_cctx);
!     else
!     {
!       eap.getline = evalarg->eval_getline;
!       eap.cookie = evalarg->eval_cookie;
!     }
! 
!     ga_init2(&newlines, (int)sizeof(char_u *), 10);
!     if (get_function_body(&eap, &newlines, NULL, &line_to_free) == FAIL)
!       goto erret;
!     if (cmdline != NULL)
!     {
!       // Something comes after the "}".
!       *arg = eap.nextcmd;
!       if (evalarg->eval_cctx == NULL)
!       {
!           // Need to keep the line and free it/ later.
!           vim_free(evalarg->eval_tofree_lambda);
!           evalarg->eval_tofree_lambda = cmdline;
!       }
!     }
!     else
!       *arg = (char_u *)"";
! 
!     name = get_lambda_name();
!     ufunc = alloc_clear(offsetof(ufunc_T, uf_name) + STRLEN(name) + 1);
!     if (ufunc == NULL)
!       goto erret;
!     set_ufunc_name(ufunc, name);
!     if (hash_add(&func_hashtab, UF2HIKEY(ufunc)) == FAIL)
!     {
!       vim_free(ufunc);
!       goto erret;
!     }
!     ufunc->uf_refcount = 1;
!     ufunc->uf_args = *newargs;
!     newargs->ga_data = NULL;
!     ufunc->uf_def_args = *default_args;
!     default_args->ga_data = NULL;
!     ufunc->uf_func_type = &t_func_any;
! 
!     // error messages are for the first function line
!     lnum_save = SOURCING_LNUM;
!     SOURCING_LNUM = sourcing_lnum_top;
! 
!     // parse argument types
!     if (parse_argument_types(ufunc, argtypes, varargs) == FAIL)
!     {
!       SOURCING_LNUM = lnum_save;
!       goto erret;
!     }
! 
!     // parse the return type, if any
!     if (parse_return_type(ufunc, ret_type) == FAIL)
!       goto erret;
! 
!     pt = ALLOC_CLEAR_ONE(partial_T);
!     if (pt == NULL)
!       goto erret;
!     pt->pt_func = ufunc;
!     pt->pt_refcount = 1;
! 
!     ufunc->uf_lines = newlines;
!     newlines.ga_data = NULL;
!     if (sandbox)
!       ufunc->uf_flags |= FC_SANDBOX;
!     if (!ASCII_ISUPPER(*ufunc->uf_name))
!       ufunc->uf_flags |= FC_VIM9;
!     ufunc->uf_script_ctx = current_sctx;
!     ufunc->uf_script_ctx_version = current_sctx.sc_version;
!     ufunc->uf_script_ctx.sc_lnum += sourcing_lnum_top;
!     set_function_type(ufunc);
! 
!     rettv->vval.v_partial = pt;
!     rettv->v_type = VAR_PARTIAL;
!     ret = OK;
! 
! erret:
!     if (lnum_save >= 0)
!       SOURCING_LNUM = lnum_save;
!     vim_free(line_to_free);
!     ga_clear_strings(&newlines);
!     ga_clear_strings(newargs);
!     ga_clear_strings(default_args);
!     return ret;
! }
! 
! /*
!  * Parse a lambda expression and get a Funcref from "*arg" into "rettv".
   * "arg" points to the { in "{arg -> expr}" or the ( in "(arg) => expr"
   * When "types_optional" is TRUE optionally take argument types.
   * Return OK or FAIL.  Returns NOTDONE for dict or {expr}.
***************
*** 554,559 ****
--- 1067,1073 ----
      garray_T  newlines;
      garray_T  *pnewargs;
      garray_T  argtypes;
+     garray_T  default_args;
      ufunc_T   *fp = NULL;
      partial_T   *pt = NULL;
      int               varargs;
***************
*** 596,602 ****
      *arg += 1;
      ret = get_function_args(arg, equal_arrow ? ')' : '-', pnewargs,
            types_optional ? &argtypes : NULL, types_optional, evalarg,
!                                           &varargs, NULL, FALSE, NULL, NULL);
      if (ret == FAIL
                  || (s = skip_arrow(*arg, equal_arrow, &ret_type,
                equal_arrow || in_vim9script() ? &white_error : NULL)) == NULL)
--- 1110,1117 ----
      *arg += 1;
      ret = get_function_args(arg, equal_arrow ? ')' : '-', pnewargs,
            types_optional ? &argtypes : NULL, types_optional, evalarg,
!                                           &varargs, &default_args,
!                                           FALSE, NULL, NULL);
      if (ret == FAIL
                  || (s = skip_arrow(*arg, equal_arrow, &ret_type,
                equal_arrow || in_vim9script() ? &white_error : NULL)) == NULL)
***************
*** 624,632 ****
      // Recognize "{" as the start of a function body.
      if (equal_arrow && **arg == '{')
      {
!       // TODO: process the function body upto the "}".
!       // Return type is required then.
!       emsg("Lambda function body not supported yet");
        goto errret;
      }
  
--- 1139,1153 ----
      // Recognize "{" as the start of a function body.
      if (equal_arrow && **arg == '{')
      {
!       if (lambda_function_body(arg, rettv, evalarg, pnewargs,
!                          types_optional ? &argtypes : NULL, varargs,
!                          &default_args, ret_type) == FAIL)
!           goto errret;
!       goto theend;
!     }
!     if (default_args.ga_len > 0)
!     {
!       emsg(_(e_cannot_use_default_values_in_lambda));
        goto errret;
      }
  
***************
*** 732,737 ****
--- 1253,1259 ----
        hash_add(&func_hashtab, UF2HIKEY(fp));
      }
  
+ theend:
      eval_lavars_used = old_eval_lavars;
      if (evalarg != NULL && evalarg->eval_tofree == NULL)
        evalarg->eval_tofree = tofree1;
***************
*** 745,750 ****
--- 1267,1273 ----
  errret:
      ga_clear_strings(&newargs);
      ga_clear_strings(&newlines);
+     ga_clear_strings(&default_args);
      if (types_optional)
        ga_clear_strings(&argtypes);
      vim_free(fp);
***************
*** 2459,2465 ****
      {
        // Check that the argument types are OK for the types of the funcref.
        if (check_argument_types(funcexe->check_type, argvars, argcount,
!                                                                name) == FAIL)
            error = FCERR_OTHER;
      }
  
--- 2982,2988 ----
      {
        // Check that the argument types are OK for the types of the funcref.
        if (check_argument_types(funcexe->check_type, argvars, argcount,
!                                    (name != NULL) ? name : funcname) == FAIL)
            error = FCERR_OTHER;
      }
  
***************
*** 3006,3032 ****
  }
  
  /*
-  * Check if "*cmd" points to a function command and if so advance "*cmd" and
-  * return TRUE.
-  * Otherwise return FALSE;
-  * Do not consider "function(" to be a command.
-  */
-     static int
- is_function_cmd(char_u **cmd)
- {
-     char_u *p = *cmd;
- 
-     if (checkforcmd(&p, "function", 2))
-     {
-       if (*p == '(')
-           return FALSE;
-       *cmd = p;
-       return TRUE;
-     }
-     return FALSE;
- }
- 
- /*
   * ":function" also supporting nested ":def".
   * When "name_arg" is not NULL this is a nested function, using "name_arg" for
   * the function name.
--- 3529,3534 ----
***************
*** 3035,3046 ****
      ufunc_T *
  define_function(exarg_T *eap, char_u *name_arg)
  {
-     char_u    *theline;
      char_u    *line_to_free = NULL;
      int               j;
      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;
--- 3537,3546 ----
***************
*** 3056,3076 ****
      char_u    *ret_type = NULL;
      ufunc_T   *fp = NULL;
      int               overwrite = FALSE;
-     int               indent;
-     int               nesting;
- #define MAX_FUNC_NESTING 50
-     char      nesting_def[MAX_FUNC_NESTING];
      dictitem_T        *v;
      funcdict_T        fudi;
      static int        func_nr = 0;        // number for nameless function
      int               paren;
      hashitem_T        *hi;
-     getline_opt_T getline_options;
-     linenr_T  sourcing_lnum_off;
      linenr_T  sourcing_lnum_top;
-     int               is_heredoc = FALSE;
-     char_u    *skip_until = NULL;
-     char_u    *heredoc_trimmed = NULL;
      int               vim9script = in_vim9script();
      imported_T        *import = NULL;
  
--- 3556,3567 ----
***************
*** 3263,3269 ****
        goto ret_free;
      }
  
!     ga_init2(&newlines, (int)sizeof(char_u *), 3);
  
      if (!eap->skip && name_arg == NULL)
      {
--- 3754,3760 ----
        goto ret_free;
      }
  
!     ga_init2(&newlines, (int)sizeof(char_u *), 10);
  
      if (!eap->skip && name_arg == NULL)
      {
***************
*** 3399,3707 ****
      // Save the starting line number.
      sourcing_lnum_top = SOURCING_LNUM;
  
!     // Detect having skipped over comment lines to find the return
!     // type.  Add NULL lines to keep the line count correct.
!     sourcing_lnum_off = get_sourced_lnum(eap->getline, eap->cookie);
!     if (SOURCING_LNUM < sourcing_lnum_off)
!     {
!       sourcing_lnum_off -= SOURCING_LNUM;
!       if (ga_grow(&newlines, sourcing_lnum_off) == FAIL)
!           goto erret;
!       while (sourcing_lnum_off-- > 0)
!           ((char_u **)(newlines.ga_data))[newlines.ga_len++] = NULL;
!     }
! 
!     indent = 2;
!     nesting = 0;
!     nesting_def[nesting] = (eap->cmdidx == CMD_def);
!     getline_options = eap->cmdidx == CMD_def
!                               ? GETLINE_CONCAT_CONTBAR : GETLINE_CONCAT_CONT;
!     for (;;)
!     {
!       if (KeyTyped)
!       {
!           msg_scroll = TRUE;
!           saved_wait_return = FALSE;
!       }
!       need_wait_return = FALSE;
! 
!       if (line_arg != NULL)
!       {
!           // Use eap->arg, split up in parts by line breaks.
!           theline = line_arg;
!           p = vim_strchr(theline, '\n');
!           if (p == NULL)
!               line_arg += STRLEN(line_arg);
!           else
!           {
!               *p = NUL;
!               line_arg = p + 1;
!           }
!       }
!       else
!       {
!           vim_free(line_to_free);
!           if (eap->getline == NULL)
!               theline = getcmdline(':', 0L, indent, getline_options);
!           else
!               theline = eap->getline(':', eap->cookie, indent,
!                                                             getline_options);
!           line_to_free = theline;
!       }
!       if (KeyTyped)
!           lines_left = Rows - 1;
!       if (theline == NULL)
!       {
!           // Use the start of the function for the line number.
!           SOURCING_LNUM = sourcing_lnum_top;
!           if (skip_until != NULL)
!               semsg(_(e_missing_heredoc_end_marker_str), skip_until);
!           else if (eap->cmdidx == CMD_def)
!               emsg(_(e_missing_enddef));
!           else
!               emsg(_("E126: Missing :endfunction"));
!           goto erret;
!       }
! 
!       // Detect line continuation: SOURCING_LNUM increased more than one.
!       sourcing_lnum_off = get_sourced_lnum(eap->getline, eap->cookie);
!       if (SOURCING_LNUM < sourcing_lnum_off)
!           sourcing_lnum_off -= SOURCING_LNUM;
!       else
!           sourcing_lnum_off = 0;
! 
!       if (skip_until != NULL)
!       {
!           // Don't check for ":endfunc"/":enddef" between
!           // * ":append" and "."
!           // * ":python <<EOF" and "EOF"
!           // * ":let {var-name} =<< [trim] {marker}" and "{marker}"
!           if (heredoc_trimmed == NULL
!                   || (is_heredoc && skipwhite(theline) == theline)
!                   || STRNCMP(theline, heredoc_trimmed,
!                                                STRLEN(heredoc_trimmed)) == 0)
!           {
!               if (heredoc_trimmed == NULL)
!                   p = theline;
!               else if (is_heredoc)
!                   p = skipwhite(theline) == theline
!                                ? theline : theline + STRLEN(heredoc_trimmed);
!               else
!                   p = theline + STRLEN(heredoc_trimmed);
!               if (STRCMP(p, skip_until) == 0)
!               {
!                   VIM_CLEAR(skip_until);
!                   VIM_CLEAR(heredoc_trimmed);
!                   getline_options = eap->cmdidx == CMD_def
!                               ? GETLINE_CONCAT_CONTBAR : GETLINE_CONCAT_CONT;
!                   is_heredoc = FALSE;
!               }
!           }
!       }
!       else
!       {
!           // skip ':' and blanks
!           for (p = theline; VIM_ISWHITE(*p) || *p == ':'; ++p)
!               ;
! 
!           // Check for "endfunction" or "enddef".
!           // When a ":" follows it must be a dict key; "enddef: value,"
!           if (checkforcmd(&p, nesting_def[nesting]
!                                               ? "enddef" : "endfunction", 4)
!                   && *p != ':')
!           {
!               if (nesting-- == 0)
!               {
!                   char_u *nextcmd = NULL;
! 
!                   if (*p == '|')
!                       nextcmd = p + 1;
!                   else if (line_arg != NULL && *skipwhite(line_arg) != NUL)
!                       nextcmd = line_arg;
!                   else if (*p != NUL && *p != '"' && p_verbose > 0)
!                       give_warning2(eap->cmdidx == CMD_def
!                           ? (char_u *)_("W1001: Text found after :enddef: %s")
!                           : (char_u *)_("W22: Text found after :endfunction: 
%s"),
!                            p, TRUE);
!                   if (nextcmd != NULL)
!                   {
!                       // Another command follows. If the line came from "eap"
!                       // we can simply point into it, otherwise we need to
!                       // change "eap->cmdlinep".
!                       eap->nextcmd = nextcmd;
!                       if (line_to_free != NULL)
!                       {
!                           vim_free(*eap->cmdlinep);
!                           *eap->cmdlinep = line_to_free;
!                           line_to_free = NULL;
!                       }
!                   }
!                   break;
!               }
!           }
! 
!           // Check for mismatched "endfunc" or "enddef".
!           // We don't check for "def" inside "func" thus we also can't check
!           // for "enddef".
!           // We continue to find the end of the function, although we might
!           // not find it.
!           else if (nesting_def[nesting])
!           {
!               if (checkforcmd(&p, "endfunction", 4) && *p != ':')
!                   emsg(_(e_mismatched_endfunction));
!           }
!           else if (eap->cmdidx == CMD_def && checkforcmd(&p, "enddef", 4))
!               emsg(_(e_mismatched_enddef));
! 
!           // Increase indent inside "if", "while", "for" and "try", decrease
!           // at "end".
!           if (indent > 2 && (*p == '}' || STRNCMP(p, "end", 3) == 0))
!               indent -= 2;
!           else if (STRNCMP(p, "if", 2) == 0
!                   || STRNCMP(p, "wh", 2) == 0
!                   || STRNCMP(p, "for", 3) == 0
!                   || STRNCMP(p, "try", 3) == 0)
!               indent += 2;
! 
!           // Check for defining a function inside this function.
!           // Only recognize "def" inside "def", not inside "function",
!           // For backwards compatibility, see Test_function_python().
!           c = *p;
!           if (is_function_cmd(&p)
!                   || (eap->cmdidx == CMD_def && checkforcmd(&p, "def", 3)))
!           {
!               if (*p == '!')
!                   p = skipwhite(p + 1);
!               p += eval_fname_script(p);
!               vim_free(trans_function_name(&p, NULL, TRUE, 0, NULL,
!                                                                 NULL, NULL));
!               if (*skipwhite(p) == '(')
!               {
!                   if (nesting == MAX_FUNC_NESTING - 1)
!                       emsg(_(e_function_nesting_too_deep));
!                   else
!                   {
!                       ++nesting;
!                       nesting_def[nesting] = (c == 'd');
!                       indent += 2;
!                   }
!               }
!           }
! 
!           // Check for ":append", ":change", ":insert".  Not for :def.
!           p = skip_range(p, FALSE, NULL);
!           if (eap->cmdidx != CMD_def
!               && ((p[0] == 'a' && (!ASCII_ISALPHA(p[1]) || p[1] == 'p'))
!                   || (p[0] == 'c'
!                       && (!ASCII_ISALPHA(p[1]) || (p[1] == 'h'
!                               && (!ASCII_ISALPHA(p[2]) || (p[2] == 'a'
!                                       && (STRNCMP(&p[3], "nge", 3) != 0
!                                           || !ASCII_ISALPHA(p[6])))))))
!                   || (p[0] == 'i'
!                       && (!ASCII_ISALPHA(p[1]) || (p[1] == 'n'
!                               && (!ASCII_ISALPHA(p[2])
!                                   || (p[2] == 's'
!                                       && (!ASCII_ISALPHA(p[3])
!                                               || p[3] == 'e'))))))))
!               skip_until = vim_strsave((char_u *)".");
! 
!           // Check for ":python <<EOF", ":tcl <<EOF", etc.
!           arg = skipwhite(skiptowhite(p));
!           if (arg[0] == '<' && arg[1] =='<'
!                   && ((p[0] == 'p' && p[1] == 'y'
!                                   && (!ASCII_ISALNUM(p[2]) || p[2] == 't'
!                                       || ((p[2] == '3' || p[2] == 'x')
!                                                  && !ASCII_ISALPHA(p[3]))))
!                       || (p[0] == 'p' && p[1] == 'e'
!                                   && (!ASCII_ISALPHA(p[2]) || p[2] == 'r'))
!                       || (p[0] == 't' && p[1] == 'c'
!                                   && (!ASCII_ISALPHA(p[2]) || p[2] == 'l'))
!                       || (p[0] == 'l' && p[1] == 'u' && p[2] == 'a'
!                                   && !ASCII_ISALPHA(p[3]))
!                       || (p[0] == 'r' && p[1] == 'u' && p[2] == 'b'
!                                   && (!ASCII_ISALPHA(p[3]) || p[3] == 'y'))
!                       || (p[0] == 'm' && p[1] == 'z'
!                                   && (!ASCII_ISALPHA(p[2]) || p[2] == 's'))
!                       ))
!           {
!               // ":python <<" continues until a dot, like ":append"
!               p = skipwhite(arg + 2);
!               if (STRNCMP(p, "trim", 4) == 0)
!               {
!                   // Ignore leading white space.
!                   p = skipwhite(p + 4);
!                   heredoc_trimmed = vim_strnsave(theline,
!                                                skipwhite(theline) - theline);
!               }
!               if (*p == NUL)
!                   skip_until = vim_strsave((char_u *)".");
!               else
!                   skip_until = vim_strnsave(p, skiptowhite(p) - p);
!               getline_options = GETLINE_NONE;
!               is_heredoc = TRUE;
!           }
! 
!           // Check for ":cmd v =<< [trim] EOF"
!           //       and ":cmd [a, b] =<< [trim] EOF"
!           //       and "lines =<< [trim] EOF" for Vim9
!           // Where "cmd" can be "let", "var", "final" or "const".
!           arg = skipwhite(skiptowhite(p));
!           if (*arg == '[')
!               arg = vim_strchr(arg, ']');
!           if (arg != NULL)
!           {
!               int found = (eap->cmdidx == CMD_def && arg[0] == '='
!                                            && arg[1] == '<' && arg[2] =='<');
! 
!               if (!found)
!                   // skip over the argument after "cmd"
!                   arg = skipwhite(skiptowhite(arg));
!               if (found || (arg[0] == '=' && arg[1] == '<' && arg[2] =='<'
!                       && (checkforcmd(&p, "let", 2)
!                           || checkforcmd(&p, "var", 3)
!                           || checkforcmd(&p, "final", 5)
!                           || checkforcmd(&p, "const", 5))))
!               {
!                   p = skipwhite(arg + 3);
!                   if (STRNCMP(p, "trim", 4) == 0)
!                   {
!                       // Ignore leading white space.
!                       p = skipwhite(p + 4);
!                       heredoc_trimmed = vim_strnsave(theline,
!                                                skipwhite(theline) - theline);
!                   }
!                   skip_until = vim_strnsave(p, skiptowhite(p) - p);
!                   getline_options = GETLINE_NONE;
!                   is_heredoc = TRUE;
!               }
!           }
!       }
! 
!       // Add the line to the function.
!       if (ga_grow(&newlines, 1 + sourcing_lnum_off) == FAIL)
!           goto erret;
! 
!       // Copy the line to newly allocated memory.  get_one_sourceline()
!       // allocates 250 bytes per line, this saves 80% on average.  The cost
!       // is an extra alloc/free.
!       p = vim_strsave(theline);
!       if (p == NULL)
!           goto erret;
!       ((char_u **)(newlines.ga_data))[newlines.ga_len++] = p;
! 
!       // Add NULL lines for continuation lines, so that the line count is
!       // equal to the index in the growarray.
!       while (sourcing_lnum_off-- > 0)
!           ((char_u **)(newlines.ga_data))[newlines.ga_len++] = NULL;
! 
!       // Check for end of eap->arg.
!       if (line_arg != NULL && *line_arg == NUL)
!           line_arg = NULL;
!     }
! 
!     // Don't define the function when skipping commands or when an error was
!     // detected.
!     if (eap->skip || did_emsg)
        goto erret;
  
      /*
--- 3890,3896 ----
      // Save the starting line number.
      sourcing_lnum_top = SOURCING_LNUM;
  
!     if (get_function_body(eap, &newlines, line_arg, &line_to_free) == FAIL)
        goto erret;
  
      /*
***************
*** 3933,3950 ****
        varargs = FALSE;
  
        // parse the return type, if any
!       if (ret_type == NULL)
!           fp->uf_ret_type = &t_void;
!       else
        {
!           p = ret_type;
!           fp->uf_ret_type = parse_type(&p, &fp->uf_type_list, TRUE);
!           if (fp->uf_ret_type == NULL)
!           {
!               fp->uf_ret_type = &t_void;
!               SOURCING_LNUM = lnum_save;
!               goto erret;
!           }
        }
        SOURCING_LNUM = lnum_save;
      }
--- 4122,4131 ----
        varargs = FALSE;
  
        // parse the return type, if any
!       if (parse_return_type(fp, ret_type) == FAIL)
        {
!           SOURCING_LNUM = lnum_save;
!           goto erret;
        }
        SOURCING_LNUM = lnum_save;
      }
***************
*** 4004,4018 ****
        VIM_CLEAR(fp->uf_arg_types);
  ret_free:
      ga_clear_strings(&argtypes);
-     vim_free(skip_until);
-     vim_free(heredoc_trimmed);
      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;
  }
--- 4185,4196 ----
*** ../vim-8.2.2634/src/errors.h        2021-03-20 14:59:58.508414399 +0100
--- src/errors.h        2021-03-21 19:26:46.337374165 +0100
***************
*** 377,379 ****
--- 377,383 ----
        INIT(= N_("E1169: 'import * as {name}' not supported here"));
  EXTERN char e_cannot_use_hash_curly_to_start_comment[]
        INIT(= N_("E1170: Cannot use #{ to start a comment"));
+ EXTERN char e_missing_end_block[]
+       INIT(= N_("E1171: Missing } after inline function"));
+ EXTERN char e_cannot_use_default_values_in_lambda[]
+       INIT(= N_("E1172: Cannot use default values in a lambda"));
*** ../vim-8.2.2634/src/vim9compile.c   2021-03-18 21:37:52.192105213 +0100
--- src/vim9compile.c   2021-03-21 18:47:45.667261722 +0100
***************
*** 3171,3177 ****
  
  /*
   * Parse a lambda: "(arg, arg) => expr"
!  * "*arg" points to the '{'.
   * Returns OK/FAIL when a lambda is recognized, NOTDONE if it's not a lambda.
   */
      static int
--- 3171,3177 ----
  
  /*
   * Parse a lambda: "(arg, arg) => expr"
!  * "*arg" points to the '('.
   * Returns OK/FAIL when a lambda is recognized, NOTDONE if it's not a lambda.
   */
      static int
***************
*** 5126,5131 ****
--- 5126,5138 ----
      }
  }
  
+     void
+ fill_exarg_from_cctx(exarg_T *eap, cctx_T *cctx)
+ {
+     eap->getline = exarg_getline;
+     eap->cookie = cctx;
+ }
+ 
  /*
   * Compile a nested :def command.
   */
***************
*** 5176,5184 ****
        return NULL;
  
      eap->arg = name_end;
!     eap->getline = exarg_getline;
!     eap->cookie = cctx;
!     eap->skip = cctx->ctx_skip == SKIP_YES;
      eap->forceit = FALSE;
      lambda_name = vim_strsave(get_lambda_name());
      if (lambda_name == NULL)
--- 5183,5190 ----
        return NULL;
  
      eap->arg = name_end;
!     fill_exarg_from_cctx(eap, cctx);
! 
      eap->forceit = FALSE;
      lambda_name = vim_strsave(get_lambda_name());
      if (lambda_name == NULL)
*** ../vim-8.2.2634/src/misc2.c 2021-02-23 19:19:53.826835832 +0100
--- src/misc2.c 2021-03-21 19:58:37.441165287 +0100
***************
*** 2026,2033 ****
  {
      int               i;
  
!     for (i = 0; i < gap->ga_len; ++i)
!       vim_free(((char_u **)(gap->ga_data))[i]);
      ga_clear(gap);
  }
  
--- 2026,2034 ----
  {
      int               i;
  
!     if (gap->ga_data != NULL)
!       for (i = 0; i < gap->ga_len; ++i)
!           vim_free(((char_u **)(gap->ga_data))[i]);
      ga_clear(gap);
  }
  
*** ../vim-8.2.2634/src/proto/vim9compile.pro   2021-02-28 16:55:07.509026860 
+0100
--- src/proto/vim9compile.pro   2021-03-21 18:46:41.855405325 +0100
***************
*** 14,19 ****
--- 14,20 ----
  char_u *to_name_const_end(char_u *arg);
  exprtype_T get_compare_type(char_u *p, int *len, int *type_is);
  void error_white_both(char_u *op, int len);
+ void fill_exarg_from_cctx(exarg_T *eap, cctx_T *cctx);
  int assignment_len(char_u *p, int *heredoc);
  void vim9_declare_error(char_u *name);
  int check_vim9_unlet(char_u *name);
*** ../vim-8.2.2634/src/testdir/test_vim9_expr.vim      2021-03-20 
14:59:58.508414399 +0100
--- src/testdir/test_vim9_expr.vim      2021-03-21 20:36:32.500213520 +0100
***************
*** 1946,1951 ****
--- 1946,1970 ----
    CheckScriptSuccess(lines)
  enddef
  
+ def Test_expr7_lambda_block()
+   var lines =<< trim END
+       var Func = (s: string): string => {
+                       return 'hello ' .. s
+                     }
+       assert_equal('hello there', Func('there'))
+ 
+       var ll = range(3)
+       var dll = mapnew(ll, (k, v): string => {
+           if v % 2
+             return 'yes'
+           endif
+           return 'no'
+         })
+       assert_equal(['no', 'yes', 'no'], dll)
+   END
+   CheckDefAndScriptSuccess(lines)
+ enddef
+ 
  def NewLambdaWithComments(): func
    return (x) =>
              # some comment
*** ../vim-8.2.2634/src/version.c       2021-03-21 14:49:53.453675479 +0100
--- src/version.c       2021-03-21 20:51:42.674526422 +0100
***************
*** 752,753 ****
--- 752,755 ----
  {   /* Add new patch number below this line */
+ /**/
+     2635,
  /**/

-- 
hundred-and-one symptoms of being an internet addict:
9. All your daydreaming is preoccupied with getting a faster connection to the
   net: cable modem...100 Mbit...Fiber...1Gbit

 /// 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/202103211953.12LJrxaK2140213%40masaka.moolenaar.net.

Raspunde prin e-mail lui