From f18332fb9e2e4208a97d800f096b02c6681780e7 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Fri, 7 May 2021 17:55:55 +0200 Subject: patch 8.2.2842: Vim9: skip argument to searchpair() is not compiled Problem: Vim9: skip argument to searchpair() is not compiled. Solution: Add VAR_INSTR. --- src/vim9compile.c | 107 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 90 insertions(+), 17 deletions(-) (limited to 'src/vim9compile.c') diff --git a/src/vim9compile.c b/src/vim9compile.c index f89b0d7d35..03aca55b8c 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -615,6 +615,7 @@ may_generate_2STRING(int offset, cctx_T *cctx) case VAR_DICT: case VAR_JOB: case VAR_CHANNEL: + case VAR_INSTR: to_string_error((*type)->tt_type); return FAIL; } @@ -3097,16 +3098,72 @@ theend: return res; } + static void +clear_instr_ga(garray_T *gap) +{ + int idx; + + for (idx = 0; idx < gap->ga_len; ++idx) + delete_instr(((isn_T *)gap->ga_data) + idx); + ga_clear(gap); +} + +/* + * Compile a string in a ISN_PUSHS instruction into an ISN_INSTR. + * Returns FAIL if compilation fails. + */ + static int +compile_string(isn_T *isn, cctx_T *cctx) +{ + char_u *s = isn->isn_arg.string; + garray_T save_ga = cctx->ctx_instr; + int expr_res; + int trailing_error; + int instr_count; + isn_T *instr = NULL; + + // Temporarily reset the list of instructions so that the jump labels are + // correct. + cctx->ctx_instr.ga_len = 0; + cctx->ctx_instr.ga_maxlen = 0; + cctx->ctx_instr.ga_data = NULL; + expr_res = compile_expr0(&s, cctx); + s = skipwhite(s); + trailing_error = *s != NUL; + + if (expr_res == FAIL || trailing_error) + { + if (trailing_error) + semsg(_(e_trailing_arg), s); + clear_instr_ga(&cctx->ctx_instr); + cctx->ctx_instr = save_ga; + return FAIL; + } + + // Move the generated instructions into the ISN_INSTR instruction, then + // restore the list of instructions. + instr_count = cctx->ctx_instr.ga_len; + instr = cctx->ctx_instr.ga_data; + instr[instr_count].isn_type = ISN_FINISH; + + cctx->ctx_instr = save_ga; + vim_free(isn->isn_arg.string); + isn->isn_type = ISN_INSTR; + isn->isn_arg.instr = instr; + return OK; +} + /* * Compile the argument expressions. * "arg" points to just after the "(" and is advanced to after the ")" */ static int -compile_arguments(char_u **arg, cctx_T *cctx, int *argcount) +compile_arguments(char_u **arg, cctx_T *cctx, int *argcount, int is_searchpair) { char_u *p = *arg; char_u *whitep = *arg; int must_end = FALSE; + int instr_count; for (;;) { @@ -3123,10 +3180,21 @@ compile_arguments(char_u **arg, cctx_T *cctx, int *argcount) return FAIL; } + instr_count = cctx->ctx_instr.ga_len; if (compile_expr0(&p, cctx) == FAIL) return FAIL; ++*argcount; + if (is_searchpair && *argcount == 5 + && cctx->ctx_instr.ga_len == instr_count + 1) + { + isn_T *isn = ((isn_T *)cctx->ctx_instr.ga_data) + instr_count; + + // {skip} argument of searchpair() can be compiled if not empty + if (isn->isn_type == ISN_PUSHS && *isn->isn_arg.string != NUL) + compile_string(isn, cctx); + } + if (*p != ',' && *skipwhite(p) == ',') { semsg(_(e_no_white_space_allowed_before_str_str), ",", p); @@ -3175,6 +3243,7 @@ compile_call( ufunc_T *ufunc = NULL; int res = FAIL; int is_autoload; + int is_searchpair; // we can evaluate "has('name')" at compile time if (varlen == 3 && STRNCMP(*arg, "has", 3) == 0) @@ -3216,8 +3285,11 @@ compile_call( vim_strncpy(namebuf, *arg, varlen); name = fname_trans_sid(namebuf, fname_buf, &tofree, &error); + // we handle the "skip" argument of searchpair() differently + is_searchpair = (varlen == 10 && STRNCMP(*arg, "searchpair", 10) == 0); + *arg = skipwhite(*arg + varlen + 1); - if (compile_arguments(arg, cctx, &argcount) == FAIL) + if (compile_arguments(arg, cctx, &argcount, is_searchpair) == FAIL) goto theend; is_autoload = vim_strchr(name, AUTOLOAD_CHAR) != NULL; @@ -4027,7 +4099,7 @@ compile_subscript( type = ((type_T **)stack->ga_data)[stack->ga_len - 1]; *arg = skipwhite(p + 1); - if (compile_arguments(arg, cctx, &argcount) == FAIL) + if (compile_arguments(arg, cctx, &argcount, FALSE) == FAIL) return FAIL; if (generate_PCALL(cctx, argcount, name_start, type, TRUE) == FAIL) return FAIL; @@ -4080,7 +4152,7 @@ compile_subscript( return FAIL; } *arg = skipwhite(*arg + 1); - if (compile_arguments(arg, cctx, &argcount) == FAIL) + if (compile_arguments(arg, cctx, &argcount, FALSE) == FAIL) return FAIL; // Move the instructions for the arguments to before the @@ -6728,6 +6800,7 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx) case VAR_ANY: case VAR_PARTIAL: case VAR_VOID: + case VAR_INSTR: case VAR_SPECIAL: // cannot happen generate_PUSHNR(cctx, 0); break; @@ -8536,16 +8609,6 @@ theend: } - static void -clear_instr_ga(garray_T *gap) -{ - int idx; - - for (idx = 0; idx < gap->ga_len; ++idx) - delete_instr(((isn_T *)gap->ga_data) + idx); - ga_clear(gap); -} - /* * :s/pat/repl/ */ @@ -8568,13 +8631,13 @@ compile_substitute(char_u *arg, exarg_T *eap, cctx_T *cctx) int expr_res; int trailing_error; int instr_count; - isn_T *instr = NULL; + isn_T *instr; isn_T *isn; cmd += 3; end = skip_substitute(cmd, delimiter); - // Temporarily reset the list of instructions so that the jumps + // Temporarily reset the list of instructions so that the jump // labels are correct. cctx->ctx_instr.ga_len = 0; cctx->ctx_instr.ga_maxlen = 0; @@ -8592,7 +8655,6 @@ compile_substitute(char_u *arg, exarg_T *eap, cctx_T *cctx) semsg(_(e_trailing_arg), cmd); clear_instr_ga(&cctx->ctx_instr); cctx->ctx_instr = save_ga; - vim_free(instr); return NULL; } @@ -9555,6 +9617,17 @@ delete_instr(isn_T *isn) } break; + case ISN_INSTR: + { + int idx; + isn_T *list = isn->isn_arg.instr; + + for (idx = 0; list[idx].isn_type != ISN_FINISH; ++idx) + delete_instr(list + idx); + vim_free(list); + } + break; + case ISN_LOADS: case ISN_STORES: vim_free(isn->isn_arg.loadstore.ls_name); -- cgit v1.2.3