]> granicus.if.org Git - vim/commitdiff
patch 8.2.4870: Vim9: expression in :substitute is not compiled v8.2.4870
authorLemonBoy <thatlemon@gmail.com>
Thu, 5 May 2022 12:53:03 +0000 (13:53 +0100)
committerBram Moolenaar <Bram@vim.org>
Thu, 5 May 2022 12:53:03 +0000 (13:53 +0100)
Problem:    Vim9: expression in :substitute is not compiled.
Solution:   Use an INSTR instruction if possible. (closes #10334)

src/evalfunc.c
src/regexp.c
src/testdir/test_vim9_builtin.vim
src/testdir/test_vim9_disassemble.vim
src/version.c
src/vim9execute.c
src/vim9expr.c

index f0650260d343d1ab2be00ab3c17f1bac27655aa9..55041b945f468a1122313e0c40a4cc3531cea0d8 100644 (file)
@@ -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);
index a9bdd95aaaea10ab4c168f974ccc28e64df4b6b1..98231dac6dab15f4bfbff96a8ca9e2adb31b9353 100644 (file)
@@ -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);
index 7b64fc444a91d2c0c913d539c088aaacaffd0e1e..41093f0014750a86ac778559f67783b56ff9fa3f 100644 (file)
@@ -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()
index 363d731a41d3fa4af17c81b0519d379adaab3376..e42ee78470b4bceb68aaaaebc191ad8059a7edbe 100644 (file)
@@ -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
index 1237ebf71ae676885450d91ac6029dc72bdeb548..3f7825eee2ec5e7c95ca4f852f3695b28574c892 100644 (file)
@@ -746,6 +746,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    4870,
 /**/
     4869,
 /**/
index 4646968f5f8d3f9f29e63abf4c90999ca162d222..eb6dc2829b590003d587c48485f6410bac93834a 100644 (file)
@@ -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)
index efa2ef59781798106b899069f7e4c39a743a1789..d002dc9b2707e4e3e4a7c8b3dc1f8077502b8046 100644 (file)
@@ -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;
@@ -615,12 +616,25 @@ compile_string(isn_T *isn, cctx_T *cctx)
     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.
  * "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