forked from aniani/vim
patch 8.2.4870: Vim9: expression in :substitute is not compiled
Problem: Vim9: expression in :substitute is not compiled. Solution: Use an INSTR instruction if possible. (closes #10334)
This commit is contained in:
@@ -9966,7 +9966,9 @@ f_substitute(typval_T *argvars, typval_T *rettv)
|
|||||||
pat = tv_get_string_buf_chk(&argvars[1], patbuf);
|
pat = tv_get_string_buf_chk(&argvars[1], patbuf);
|
||||||
flg = tv_get_string_buf_chk(&argvars[3], flagsbuf);
|
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];
|
expr = &argvars[2];
|
||||||
else
|
else
|
||||||
sub = tv_get_string_buf_chk(&argvars[2], subbuf);
|
sub = tv_get_string_buf_chk(&argvars[2], subbuf);
|
||||||
|
@@ -2004,6 +2004,10 @@ vim_regsub_both(
|
|||||||
funcexe.fe_partial = partial;
|
funcexe.fe_partial = partial;
|
||||||
call_func(s, -1, &rettv, 1, argv, &funcexe);
|
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)
|
if (matchList.sl_list.lv_len > 0)
|
||||||
// fill_submatch_list() was called
|
// fill_submatch_list() was called
|
||||||
clear_submatch_list(&matchList);
|
clear_submatch_list(&matchList);
|
||||||
|
@@ -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", 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'])
|
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('')
|
substitute('', '', '', '')->assert_equal('')
|
||||||
|
|
||||||
|
var lines =<< trim END
|
||||||
|
assert_equal("4", substitute("3", '\d', '\=str2nr(submatch(0)) + 1', 'g'))
|
||||||
|
END
|
||||||
|
v9.CheckDefAndScriptSuccess(lines)
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
def Test_swapinfo()
|
def Test_swapinfo()
|
||||||
|
@@ -187,6 +187,26 @@ def Test_disassemble_seachpair()
|
|||||||
enddef
|
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()
|
def s:RedirVar()
|
||||||
var result: string
|
var result: string
|
||||||
redir =>> result
|
redir =>> result
|
||||||
|
@@ -746,6 +746,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
4870,
|
||||||
/**/
|
/**/
|
||||||
4869,
|
4869,
|
||||||
/**/
|
/**/
|
||||||
|
@@ -5010,6 +5010,10 @@ exe_typval_instr(typval_T *tv, typval_T *rettv)
|
|||||||
int save_iidx = ectx->ec_iidx;
|
int save_iidx = ectx->ec_iidx;
|
||||||
int res;
|
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;
|
ectx->ec_instr = tv->vval.v_instr->instr_instr;
|
||||||
res = exec_instructions(ectx);
|
res = exec_instructions(ectx);
|
||||||
if (res == OK)
|
if (res == OK)
|
||||||
|
@@ -567,12 +567,13 @@ theend:
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Compile a string in a ISN_PUSHS instruction into an ISN_INSTR.
|
* 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.
|
* Returns FAIL if compilation fails.
|
||||||
*/
|
*/
|
||||||
static int
|
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;
|
garray_T save_ga = cctx->ctx_instr;
|
||||||
int expr_res;
|
int expr_res;
|
||||||
int trailing_error;
|
int trailing_error;
|
||||||
@@ -615,12 +616,25 @@ compile_string(isn_T *isn, cctx_T *cctx)
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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.
|
* Compile the argument expressions.
|
||||||
* "arg" points to just after the "(" and is advanced to after the ")"
|
* "arg" points to just after the "(" and is advanced to after the ")"
|
||||||
*/
|
*/
|
||||||
static int
|
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 *p = *arg;
|
||||||
char_u *whitep = *arg;
|
char_u *whitep = *arg;
|
||||||
@@ -647,14 +661,25 @@ compile_arguments(char_u **arg, cctx_T *cctx, int *argcount, int is_searchpair)
|
|||||||
return FAIL;
|
return FAIL;
|
||||||
++*argcount;
|
++*argcount;
|
||||||
|
|
||||||
if (is_searchpair && *argcount == 5
|
if (special_fn == CA_SEARCHPAIR && *argcount == 5
|
||||||
&& cctx->ctx_instr.ga_len == instr_count + 1)
|
&& cctx->ctx_instr.ga_len == instr_count + 1)
|
||||||
{
|
{
|
||||||
isn_T *isn = ((isn_T *)cctx->ctx_instr.ga_data) + instr_count;
|
isn_T *isn = ((isn_T *)cctx->ctx_instr.ga_data) + instr_count;
|
||||||
|
|
||||||
// {skip} argument of searchpair() can be compiled if not empty
|
// {skip} argument of searchpair() can be compiled if not empty
|
||||||
if (isn->isn_type == ISN_PUSHS && *isn->isn_arg.string != NUL)
|
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) == ',')
|
if (*p != ',' && *skipwhite(p) == ',')
|
||||||
@@ -706,7 +731,7 @@ compile_call(
|
|||||||
int res = FAIL;
|
int res = FAIL;
|
||||||
int is_autoload;
|
int is_autoload;
|
||||||
int has_g_namespace;
|
int has_g_namespace;
|
||||||
int is_searchpair;
|
ca_special_T special_fn;
|
||||||
imported_T *import;
|
imported_T *import;
|
||||||
|
|
||||||
if (varlen >= sizeof(namebuf))
|
if (varlen >= sizeof(namebuf))
|
||||||
@@ -776,13 +801,18 @@ compile_call(
|
|||||||
|
|
||||||
// We handle the "skip" argument of searchpair() and searchpairpos()
|
// We handle the "skip" argument of searchpair() and searchpairpos()
|
||||||
// differently.
|
// differently.
|
||||||
is_searchpair = (varlen == 6 && STRNCMP(*arg, "search", 6) == 0)
|
if ((varlen == 6 && STRNCMP(*arg, "search", 6) == 0)
|
||||||
|| (varlen == 9 && STRNCMP(*arg, "searchpos", 9) == 0)
|
|| (varlen == 9 && STRNCMP(*arg, "searchpos", 9) == 0)
|
||||||
|| (varlen == 10 && STRNCMP(*arg, "searchpair", 10) == 0)
|
|| (varlen == 10 && STRNCMP(*arg, "searchpair", 10) == 0)
|
||||||
|| (varlen == 13 && STRNCMP(*arg, "searchpairpos", 13) == 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);
|
*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;
|
goto theend;
|
||||||
|
|
||||||
is_autoload = vim_strchr(name, AUTOLOAD_CHAR) != NULL;
|
is_autoload = vim_strchr(name, AUTOLOAD_CHAR) != NULL;
|
||||||
@@ -1717,7 +1747,7 @@ compile_subscript(
|
|||||||
type = get_type_on_stack(cctx, 0);
|
type = get_type_on_stack(cctx, 0);
|
||||||
|
|
||||||
*arg = skipwhite(p + 1);
|
*arg = skipwhite(p + 1);
|
||||||
if (compile_arguments(arg, cctx, &argcount, FALSE) == FAIL)
|
if (compile_arguments(arg, cctx, &argcount, CA_NOT_SPECIAL) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
if (generate_PCALL(cctx, argcount, name_start, type, TRUE) == FAIL)
|
if (generate_PCALL(cctx, argcount, name_start, type, TRUE) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
@@ -1848,7 +1878,8 @@ compile_subscript(
|
|||||||
expr_isn_end = cctx->ctx_instr.ga_len;
|
expr_isn_end = cctx->ctx_instr.ga_len;
|
||||||
|
|
||||||
*arg = skipwhite(*arg + 1);
|
*arg = skipwhite(*arg + 1);
|
||||||
if (compile_arguments(arg, cctx, &argcount, FALSE) == FAIL)
|
if (compile_arguments(arg, cctx, &argcount, CA_NOT_SPECIAL)
|
||||||
|
== FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
|
||||||
// Move the instructions for the arguments to before the
|
// Move the instructions for the arguments to before the
|
||||||
|
Reference in New Issue
Block a user