Patch 8.2.4870
Problem: Vim9: expression in :substitute is not compiled.
Solution: Use an INSTR instruction if possible. (closes #10334)
Files: src/evalfunc.c, src/regexp.c, src/vim9execute.c, src/vim9expr.c,
src/testdir/test_vim9_builtin.vim,
src/testdir/test_vim9_disassemble.vim
*** ../vim-8.2.4869/src/evalfunc.c 2022-05-04 15:40:16.032317666 +0100
--- src/evalfunc.c 2022-05-05 13:45:18.912728496 +0100
***************
*** 9966,9972 ****
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)
expr = &argvars[2];
else
sub = tv_get_string_buf_chk(&argvars[2], subbuf);
--- 9966,9974 ----
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
! || argvars[2].v_type == VAR_INSTR)
expr = &argvars[2];
else
sub = tv_get_string_buf_chk(&argvars[2], subbuf);
*** ../vim-8.2.4869/src/regexp.c 2022-04-23 11:03:55.093928150 +0100
--- src/regexp.c 2022-05-05 13:41:25.313028876 +0100
***************
*** 2004,2009 ****
--- 2004,2013 ----
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);
*** ../vim-8.2.4869/src/vim9execute.c 2022-04-28 12:00:45.109439279 +0100
--- src/vim9execute.c 2022-05-05 13:41:25.317028871 +0100
***************
*** 5010,5015 ****
--- 5010,5019 ----
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)
*** ../vim-8.2.4869/src/vim9expr.c 2022-04-25 12:43:15.179819208 +0100
--- src/vim9expr.c 2022-05-05 13:52:05.048252933 +0100
***************
*** 567,578 ****
/*
* 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;
--- 567,579 ----
/*
* 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, int str_offset)
{
! char_u *s = isn->isn_arg.string + str_offset;
garray_T save_ga = cctx->ctx_instr;
int expr_res;
int trailing_error;
***************
*** 616,626 ****
}
/*
* 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)
{
char_u *p = *arg;
char_u *whitep = *arg;
--- 617,640 ----
}
/*
+ * 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,
! ca_special_T special_fn)
{
char_u *p = *arg;
char_u *whitep = *arg;
***************
*** 647,660 ****
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) == ',')
--- 661,685 ----
return FAIL;
++*argcount;
! 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, 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,712 ****
int res = FAIL;
int is_autoload;
int has_g_namespace;
! int is_searchpair;
imported_T *import;
if (varlen >= sizeof(namebuf))
--- 731,737 ----
int res = FAIL;
int is_autoload;
int has_g_namespace;
! ca_special_T special_fn;
imported_T *import;
if (varlen >= sizeof(namebuf))
***************
*** 776,788 ****
// 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);
*arg = skipwhite(*arg + varlen + 1);
! if (compile_arguments(arg, cctx, &argcount, is_searchpair) == FAIL)
goto theend;
is_autoload = vim_strchr(name, AUTOLOAD_CHAR) != NULL;
--- 801,818 ----
// We handle the "skip" argument of searchpair() and searchpairpos()
// differently.
! 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, special_fn) == FAIL)
goto theend;
is_autoload = vim_strchr(name, AUTOLOAD_CHAR) != NULL;
***************
*** 1717,1723 ****
type = get_type_on_stack(cctx, 0);
*arg = skipwhite(p + 1);
! if (compile_arguments(arg, cctx, &argcount, FALSE) == FAIL)
return FAIL;
if (generate_PCALL(cctx, argcount, name_start, type, TRUE) == FAIL)
return FAIL;
--- 1747,1753 ----
type = get_type_on_stack(cctx, 0);
*arg = skipwhite(p + 1);
! 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,1854 ****
expr_isn_end = cctx->ctx_instr.ga_len;
*arg = skipwhite(*arg + 1);
! if (compile_arguments(arg, cctx, &argcount, FALSE) == FAIL)
return FAIL;
// Move the instructions for the arguments to before the
--- 1878,1885 ----
expr_isn_end = cctx->ctx_instr.ga_len;
*arg = skipwhite(*arg + 1);
! if (compile_arguments(arg, cctx, &argcount, CA_NOT_SPECIAL)
! == FAIL)
return FAIL;
// Move the instructions for the arguments to before the
*** ../vim-8.2.4869/src/testdir/test_vim9_builtin.vim 2022-05-04
15:40:16.032317666 +0100
--- src/testdir/test_vim9_builtin.vim 2022-05-05 13:41:25.317028871 +0100
***************
*** 4078,4083 ****
--- 4078,4088 ----
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()
*** ../vim-8.2.4869/src/testdir/test_vim9_disassemble.vim 2022-04-28
12:00:45.109439279 +0100
--- src/testdir/test_vim9_disassemble.vim 2022-05-05 13:47:17.920584343
+0100
***************
*** 187,192 ****
--- 187,212 ----
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
*** ../vim-8.2.4869/src/version.c 2022-05-05 12:20:24.359225499 +0100
--- src/version.c 2022-05-05 13:43:29.392866010 +0100
***************
*** 748,749 ****
--- 748,751 ----
{ /* Add new patch number below this line */
+ /**/
+ 4870,
/**/
--
hundred-and-one symptoms of being an internet addict:
98. The Alta Vista administrators ask you what sites are missing
in their index files.
/// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net \\\
/// \\\
\\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
\\\ help me help AIDS victims -- http://ICCF-Holland.org ///
--
--
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php
---
You received this message because you are subscribed to the Google Groups
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/vim_dev/20220505125347.8DC5C1C0100%40moolenaar.net.