To: vim_dev@googlegroups.com Subject: Patch 8.2.2635 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ 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 <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 <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 <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 <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 -- Bram@Moolenaar.net -- 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 ///