diff options
Diffstat (limited to 'src/userfunc.c')
-rw-r--r-- | src/userfunc.c | 890 |
1 files changed, 534 insertions, 356 deletions
diff --git a/src/userfunc.c b/src/userfunc.c index 4793ee555..51894dc10 100644 --- a/src/userfunc.c +++ b/src/userfunc.c @@ -397,6 +397,25 @@ parse_argument_types(ufunc_T *fp, garray_T *argtypes, int varargs) 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,7 +555,501 @@ skip_arrow( } /* - * Parse a lambda expression and get a Funcref from "*arg". + * 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,6 +1067,7 @@ get_lambda_tv( garray_T newlines; garray_T *pnewargs; garray_T argtypes; + garray_T default_args; ufunc_T *fp = NULL; partial_T *pt = NULL; int varargs; @@ -596,7 +1110,8 @@ get_lambda_tv( *arg += 1; ret = get_function_args(arg, equal_arrow ? ')' : '-', pnewargs, types_optional ? &argtypes : NULL, types_optional, evalarg, - &varargs, NULL, FALSE, NULL, NULL); + &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,9 +1139,15 @@ get_lambda_tv( // 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"); + 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,6 +1253,7 @@ get_lambda_tv( 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,6 +1267,7 @@ get_lambda_tv( 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,7 +2982,7 @@ call_func( { // Check that the argument types are OK for the types of the funcref. if (check_argument_types(funcexe->check_type, argvars, argcount, - name) == FAIL) + (name != NULL) ? name : funcname) == FAIL) error = FCERR_OTHER; } @@ -3006,27 +3529,6 @@ list_functions(regmatch_T *regmatch) } /* - * 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. @@ -3035,12 +3537,10 @@ is_function_cmd(char_u **cmd) 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; @@ -3056,21 +3556,12 @@ define_function(exarg_T *eap, char_u *name_arg) 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; @@ -3263,7 +3754,7 @@ define_function(exarg_T *eap, char_u *name_arg) goto ret_free; } - ga_init2(&newlines, (int)sizeof(char_u *), 3); + ga_init2(&newlines, (int)sizeof(char_u *), 10); if (!eap->skip && name_arg == NULL) { @@ -3399,309 +3890,7 @@ define_function(exarg_T *eap, char_u *name_arg) // 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) + if (get_function_body(eap, &newlines, line_arg, &line_to_free) == FAIL) goto erret; /* @@ -3933,18 +4122,10 @@ define_function(exarg_T *eap, char_u *name_arg) varargs = FALSE; // parse the return type, if any - if (ret_type == NULL) - fp->uf_ret_type = &t_void; - else + if (parse_return_type(fp, ret_type) == FAIL) { - 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; + goto erret; } SOURCING_LNUM = lnum_save; } @@ -4004,15 +4185,12 @@ errret_2: 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; } |