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.

Raspunde prin e-mail lui