summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLemonBoy <thatlemon@gmail.com>2022-05-05 13:53:03 +0100
committerBram Moolenaar <Bram@vim.org>2022-05-05 13:53:03 +0100
commitf3b4895f2727e3849ca10030b251cccd9d1383f3 (patch)
tree97af9696abeba219135a0baf1947058cd4d5d995
parente442d59f6d6732c226d636067c07b7342838b36b (diff)
patch 8.2.4870: Vim9: expression in :substitute is not compiledv8.2.4870
Problem: Vim9: expression in :substitute is not compiled. Solution: Use an INSTR instruction if possible. (closes #10334)
-rw-r--r--src/evalfunc.c4
-rw-r--r--src/regexp.c4
-rw-r--r--src/testdir/test_vim9_builtin.vim5
-rw-r--r--src/testdir/test_vim9_disassemble.vim20
-rw-r--r--src/version.c2
-rw-r--r--src/vim9execute.c4
-rw-r--r--src/vim9expr.c57
7 files changed, 82 insertions, 14 deletions
diff --git a/src/evalfunc.c b/src/evalfunc.c
index f0650260d3..55041b945f 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -9966,7 +9966,9 @@ f_substitute(typval_T *argvars, typval_T *rettv)
pat = tv_get_string_buf_chk(&argvars[1], patbuf);
flg = tv_get_string_buf_chk(&argvars[3], flagsbuf);
- if (argvars[2].v_type == VAR_FUNC || argvars[2].v_type == VAR_PARTIAL)
+ if (argvars[2].v_type == VAR_FUNC
+ || argvars[2].v_type == VAR_PARTIAL
+ || argvars[2].v_type == VAR_INSTR)
expr = &argvars[2];
else
sub = tv_get_string_buf_chk(&argvars[2], subbuf);
diff --git a/src/regexp.c b/src/regexp.c
index a9bdd95aaa..98231dac6d 100644
--- a/src/regexp.c
+++ b/src/regexp.c
@@ -2004,6 +2004,10 @@ vim_regsub_both(
funcexe.fe_partial = partial;
call_func(s, -1, &rettv, 1, argv, &funcexe);
}
+ else if (expr->v_type == VAR_INSTR)
+ {
+ exe_typval_instr(expr, &rettv);
+ }
if (matchList.sl_list.lv_len > 0)
// fill_submatch_list() was called
clear_submatch_list(&matchList);
diff --git a/src/testdir/test_vim9_builtin.vim b/src/testdir/test_vim9_builtin.vim
index 7b64fc444a..41093f0014 100644
--- a/src/testdir/test_vim9_builtin.vim
+++ b/src/testdir/test_vim9_builtin.vim
@@ -4078,6 +4078,11 @@ def Test_substitute()
v9.CheckDefAndScriptFailure(['substitute("a", 2, "1", "d")'], ['E1013: Argument 2: type mismatch, expected string but got number', 'E1174: String required for argument 2'])
v9.CheckDefAndScriptFailure(['substitute("a", "b", "1", 4)'], ['E1013: Argument 4: type mismatch, expected string but got number', 'E1174: String required for argument 4'])
substitute('', '', '', '')->assert_equal('')
+
+ var lines =<< trim END
+ assert_equal("4", substitute("3", '\d', '\=str2nr(submatch(0)) + 1', 'g'))
+ END
+ v9.CheckDefAndScriptSuccess(lines)
enddef
def Test_swapinfo()
diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim
index 363d731a41..e42ee78470 100644
--- a/src/testdir/test_vim9_disassemble.vim
+++ b/src/testdir/test_vim9_disassemble.vim
@@ -187,6 +187,26 @@ def Test_disassemble_seachpair()
enddef
+def s:SubstituteExpr()
+ substitute('a', 'b', '\=123', 'g')
+enddef
+
+def Test_disassemble_substitute_expr()
+ var res = execute('disass s:SubstituteExpr')
+ assert_match('<SNR>\d*_SubstituteExpr.*' ..
+ 'substitute(''a'', ''b'', ''\\=123'', ''g'')\_s*' ..
+ '\d PUSHS "a"\_s*' ..
+ '\d PUSHS "b"\_s*' ..
+ '\d INSTR\_s*' ..
+ ' 0 PUSHNR 123\_s*' ..
+ ' -------------\_s*' ..
+ '\d PUSHS "g"\_s*' ..
+ '\d BCALL substitute(argc 4)\_s*' ..
+ '\d DROP\_s*' ..
+ '\d RETURN void',
+ res)
+enddef
+
def s:RedirVar()
var result: string
redir =>> result
diff --git a/src/version.c b/src/version.c
index 1237ebf71a..3f7825eee2 100644
--- a/src/version.c
+++ b/src/version.c
@@ -747,6 +747,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 4870,
+/**/
4869,
/**/
4868,
diff --git a/src/vim9execute.c b/src/vim9execute.c
index 4646968f5f..eb6dc2829b 100644
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -5010,6 +5010,10 @@ exe_typval_instr(typval_T *tv, typval_T *rettv)
int save_iidx = ectx->ec_iidx;
int res;
+ // Initialize rettv so that it is safe for caller to invoke clear_tv(rettv)
+ // even when the compilation fails.
+ rettv->v_type = VAR_UNKNOWN;
+
ectx->ec_instr = tv->vval.v_instr->instr_instr;
res = exec_instructions(ectx);
if (res == OK)
diff --git a/src/vim9expr.c b/src/vim9expr.c
index efa2ef5978..d002dc9b27 100644
--- a/src/vim9expr.c
+++ b/src/vim9expr.c
@@ -567,12 +567,13 @@ theend:
/*
* Compile a string in a ISN_PUSHS instruction into an ISN_INSTR.
+ * "str_offset" is the number of leading bytes to skip from the string.
* Returns FAIL if compilation fails.
*/
static int
-compile_string(isn_T *isn, cctx_T *cctx)
+compile_string(isn_T *isn, cctx_T *cctx, int str_offset)
{
- char_u *s = isn->isn_arg.string;
+ char_u *s = isn->isn_arg.string + str_offset;
garray_T save_ga = cctx->ctx_instr;
int expr_res;
int trailing_error;
@@ -616,11 +617,24 @@ compile_string(isn_T *isn, cctx_T *cctx)
}
/*
+ * List of special functions for "compile_arguments".
+ */
+typedef enum {
+ CA_NOT_SPECIAL,
+ CA_SEARCHPAIR, // {skip} in searchpair() and searchpairpos()
+ CA_SUBSTITUTE, // {sub} in substitute(), when prefixed with \=
+} ca_special_T;
+
+/*
* 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, int is_searchpair)
+compile_arguments(
+ char_u **arg,
+ cctx_T *cctx,
+ int *argcount,
+ ca_special_T special_fn)
{
char_u *p = *arg;
char_u *whitep = *arg;
@@ -647,14 +661,25 @@ compile_arguments(char_u **arg, cctx_T *cctx, int *argcount, int is_searchpair)
return FAIL;
++*argcount;
- if (is_searchpair && *argcount == 5
+ if (special_fn == CA_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);
+ compile_string(isn, cctx, 0);
+ }
+ else if (special_fn == CA_SUBSTITUTE && *argcount == 3
+ && cctx->ctx_instr.ga_len == instr_count + 1)
+ {
+ isn_T *isn = ((isn_T *)cctx->ctx_instr.ga_data) + instr_count;
+
+ // {sub} argument of substitute() can be compiled if it starts
+ // with \=
+ if (isn->isn_type == ISN_PUSHS && isn->isn_arg.string[0] == '\\'
+ && isn->isn_arg.string[1] == '=')
+ compile_string(isn, cctx, 2);
}
if (*p != ',' && *skipwhite(p) == ',')
@@ -706,7 +731,7 @@ compile_call(
int res = FAIL;
int is_autoload;
int has_g_namespace;
- int is_searchpair;
+ ca_special_T special_fn;
imported_T *import;
if (varlen >= sizeof(namebuf))
@@ -776,13 +801,18 @@ compile_call(
// We handle the "skip" argument of searchpair() and searchpairpos()
// differently.
- is_searchpair = (varlen == 6 && STRNCMP(*arg, "search", 6) == 0)
- || (varlen == 9 && STRNCMP(*arg, "searchpos", 9) == 0)
- || (varlen == 10 && STRNCMP(*arg, "searchpair", 10) == 0)
- || (varlen == 13 && STRNCMP(*arg, "searchpairpos", 13) == 0);
+ if ((varlen == 6 && STRNCMP(*arg, "search", 6) == 0)
+ || (varlen == 9 && STRNCMP(*arg, "searchpos", 9) == 0)
+ || (varlen == 10 && STRNCMP(*arg, "searchpair", 10) == 0)
+ || (varlen == 13 && STRNCMP(*arg, "searchpairpos", 13) == 0))
+ special_fn = CA_SEARCHPAIR;
+ else if (varlen == 10 && STRNCMP(*arg, "substitute", 10) == 0)
+ special_fn = CA_SUBSTITUTE;
+ else
+ special_fn = CA_NOT_SPECIAL;
*arg = skipwhite(*arg + varlen + 1);
- if (compile_arguments(arg, cctx, &argcount, is_searchpair) == FAIL)
+ if (compile_arguments(arg, cctx, &argcount, special_fn) == FAIL)
goto theend;
is_autoload = vim_strchr(name, AUTOLOAD_CHAR) != NULL;
@@ -1717,7 +1747,7 @@ compile_subscript(
type = get_type_on_stack(cctx, 0);
*arg = skipwhite(p + 1);
- if (compile_arguments(arg, cctx, &argcount, FALSE) == FAIL)
+ if (compile_arguments(arg, cctx, &argcount, CA_NOT_SPECIAL) == FAIL)
return FAIL;
if (generate_PCALL(cctx, argcount, name_start, type, TRUE) == FAIL)
return FAIL;
@@ -1848,7 +1878,8 @@ compile_subscript(
expr_isn_end = cctx->ctx_instr.ga_len;
*arg = skipwhite(*arg + 1);
- if (compile_arguments(arg, cctx, &argcount, FALSE) == FAIL)
+ if (compile_arguments(arg, cctx, &argcount, CA_NOT_SPECIAL)
+ == FAIL)
return FAIL;
// Move the instructions for the arguments to before the