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.