]> granicus.if.org Git - vim/commitdiff
patch 8.2.2124: Vim9: a range cannot be computed at runtime v8.2.2124
authorBram Moolenaar <Bram@vim.org>
Thu, 10 Dec 2020 18:43:40 +0000 (19:43 +0100)
committerBram Moolenaar <Bram@vim.org>
Thu, 10 Dec 2020 18:43:40 +0000 (19:43 +0100)
Problem:    Vim9: a range cannot be computed at runtime.
Solution:   Add the ISN_RANGE instruction.

src/testdir/test_vim9_cmd.vim
src/testdir/test_vim9_disassemble.vim
src/version.c
src/vim9.h
src/vim9compile.c
src/vim9execute.c

index 37fc559bf07dbd4861565c746d920b7113411b9e..1986c0f71e18a81717ac6d12f0c157cebfb51be3 100644 (file)
@@ -614,6 +614,17 @@ def Test_put_command()
   assert_equal('above', getline(3))
   assert_equal('below', getline(4))
 
+  # compute range at runtime
+  setline(1, range(1, 8))
+  @a = 'aaa'
+  :$-2put a
+  assert_equal('aaa', getline(7))
+
+  setline(1, range(1, 8))
+  :2
+  :+2put! a
+  assert_equal('aaa', getline(4))
+
   bwipe!
 enddef
 
index e7445d3860131508d9eccafb5dbb0786196c5f00..fabb8d2618f85550503b217a6f55b0ca485e8074 100644 (file)
@@ -133,6 +133,21 @@ def Test_disassemble_put_expr()
         res)
 enddef
 
+def s:PutRange()
+  :$-2put a
+enddef
+
+def Test_disassemble_put_range()
+  var res = execute('disass s:PutRange')
+  assert_match('<SNR>\d*_PutRange.*' ..
+        ' :$-2put a\_s*' ..
+        '\d RANGE $-2\_s*' ..
+        '\d PUT a range\_s*' ..
+        '\d PUSHNR 0\_s*' ..
+        '\d RETURN',
+        res)
+enddef
+
 def s:ScriptFuncPush()
   var localbool = true
   var localspec = v:none
index 3c39f0e1801152dab3a9cb7195f50bf90c512ea5..ee6be8a7681b516d9d8581c3cecff11989ebb4dd 100644 (file)
@@ -750,6 +750,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    2124,
 /**/
     2123,
 /**/
index 1368b38437cd9508c8a9c445e6655e3f2ba64bac..88cc4fce24ad2f573f17a6511a4d72e30afd8672 100644 (file)
@@ -18,6 +18,7 @@ typedef enum {
     ISN_EXECUTE,    // execute Ex commands isn_arg.number items on top of stack
     ISN_ECHOMSG,    // echo Ex commands isn_arg.number items on top of stack
     ISN_ECHOERR,    // echo Ex commands isn_arg.number items on top of stack
+    ISN_RANGE,     // compute range from isn_arg.string, push to stack
 
     // get and set variables
     ISN_LOAD,      // push local variable isn_arg.number
@@ -366,3 +367,8 @@ garray_T def_functions = {0, 0, sizeof(dfunc_T), 50, NULL};
 extern garray_T def_functions;
 #endif
 
+// Used for "lnum" when a range is to be taken from the stack.
+#define LNUM_VARIABLE_RANGE -999
+
+// Used for "lnum" when a range is to be taken from the stack and "!" is used.
+#define LNUM_VARIABLE_RANGE_ABOVE -888
index da069f405b6c7cca6e4bff6438ef3a11fd9ce83b..ac8fb846a5250cc6298f113933a4219b55ff0d81 100644 (file)
@@ -1888,6 +1888,26 @@ generate_EXECCONCAT(cctx_T *cctx, int count)
     return OK;
 }
 
+/*
+ * Generate ISN_RANGE.  Consumes "range".  Return OK/FAIL.
+ */
+    static int
+generate_RANGE(cctx_T *cctx, char_u *range)
+{
+    isn_T      *isn;
+    garray_T   *stack = &cctx->ctx_type_stack;
+
+    if ((isn = generate_instr(cctx, ISN_RANGE)) == NULL)
+       return FAIL;
+    isn->isn_arg.string = range;
+
+    if (ga_grow(stack, 1) == FAIL)
+       return FAIL;
+    ((type_T **)stack->ga_data)[stack->ga_len] = &t_number;
+    ++stack->ga_len;
+    return OK;
+}
+
     static int
 generate_UNPACK(cctx_T *cctx, int var_count, int semicolon)
 {
@@ -7098,6 +7118,22 @@ compile_mult_expr(char_u *arg, int cmdidx, cctx_T *cctx)
     return p;
 }
 
+/*
+ * If "eap" has a range that is not a contstant generate an ISN_RANGE
+ * instruction to compute it and return OK.
+ * Otherwise return FAIL, the caller must deal with any range.
+ */
+    static int
+compile_variable_range(exarg_T *eap, cctx_T *cctx)
+{
+    char_u *range_end = skip_range(eap->cmd, TRUE, NULL);
+    char_u *p = skipdigits(eap->cmd);
+
+    if (p == range_end)
+       return FAIL;
+    return generate_RANGE(cctx, vim_strnsave(eap->cmd, range_end - eap->cmd));
+}
+
 /*
  * :put r
  * :put ={expr}
@@ -7123,17 +7159,23 @@ compile_put(char_u *arg, exarg_T *eap, cctx_T *cctx)
     else if (eap->regname != NUL)
        ++line;
 
-    // "errormsg" will not be set because the range is ADDR_LINES.
-    // TODO: if the range contains something like "$" or "." need to evaluate
-    // at runtime
-    if (parse_cmd_address(eap, &errormsg, FALSE) == FAIL)
-       return NULL;
-    if (eap->addr_count == 0)
-       lnum = -1;
+    if (compile_variable_range(eap, cctx) == OK)
+    {
+       lnum = above ? LNUM_VARIABLE_RANGE_ABOVE : LNUM_VARIABLE_RANGE;
+    }
     else
-       lnum = eap->line2;
-    if (above)
-       --lnum;
+    {
+       // Either no range or a number.
+       // "errormsg" will not be set because the range is ADDR_LINES.
+       if (parse_cmd_address(eap, &errormsg, FALSE) == FAIL)
+           return NULL;
+       if (eap->addr_count == 0)
+           lnum = -1;
+       else
+           lnum = eap->line2;
+       if (above)
+           --lnum;
+    }
 
     generate_PUT(cctx, eap->regname, lnum);
     return line;
@@ -7960,6 +8002,7 @@ delete_instr(isn_T *isn)
        case ISN_PUSHEXC:
        case ISN_PUSHFUNC:
        case ISN_PUSHS:
+       case ISN_RANGE:
        case ISN_STOREB:
        case ISN_STOREENV:
        case ISN_STOREG:
index 7e8b336d090ddda409ae3bc5abc48589f708b32c..3f61cb4457fe8852b9532c82a503058c1b332eaf 100644 (file)
@@ -2861,6 +2861,26 @@ call_def_function(
                }
                break;
 
+           case ISN_RANGE:
+               {
+                   exarg_T     ea;
+                   char        *errormsg;
+
+                   if (GA_GROW(&ectx.ec_stack, 1) == FAIL)
+                       goto failed;
+                   ++ectx.ec_stack.ga_len;
+                   tv = STACK_TV_BOT(-1);
+                   ea.addr_type = ADDR_LINES;
+                   ea.cmd = iptr->isn_arg.string;
+                   if (parse_cmd_address(&ea, &errormsg, FALSE) == FAIL)
+                       goto failed;
+                   if (ea.addr_count == 0)
+                       tv->vval.v_number = curwin->w_cursor.lnum;
+                   else
+                       tv->vval.v_number = ea.line2;
+               }
+               break;
+
            case ISN_PUT:
                {
                    int         regname = iptr->isn_arg.put.put_regname;
@@ -2880,7 +2900,16 @@ call_def_function(
                        }
                        --ectx.ec_stack.ga_len;
                    }
-                   if (lnum == -2)
+                   if (lnum < -2)
+                   {
+                       // line number was put on the stack by ISN_RANGE
+                       tv = STACK_TV_BOT(-1);
+                       curwin->w_cursor.lnum = tv->vval.v_number;
+                       if (lnum == LNUM_VARIABLE_RANGE_ABOVE)
+                           dir = BACKWARD;
+                       --ectx.ec_stack.ga_len;
+                   }
+                   else if (lnum == -2)
                        // :put! above cursor
                        dir = BACKWARD;
                    else if (lnum >= 0)
@@ -3690,8 +3719,18 @@ ex_disassemble(exarg_T *eap)
            case ISN_2STRING_ANY: smsg("%4d 2STRING_ANY stack[%lld]", current,
                                         (long long)(iptr->isn_arg.number));
                              break;
+           case ISN_RANGE: smsg("%4d RANGE %s", current, iptr->isn_arg.string);
+                           break;
            case ISN_PUT:
-               smsg("%4d PUT %c %ld", current, iptr->isn_arg.put.put_regname,
+               if (iptr->isn_arg.put.put_lnum == LNUM_VARIABLE_RANGE_ABOVE)
+                   smsg("%4d PUT %c above range",
+                                      current, iptr->isn_arg.put.put_regname);
+               else if (iptr->isn_arg.put.put_lnum == LNUM_VARIABLE_RANGE)
+                   smsg("%4d PUT %c range",
+                                      current, iptr->isn_arg.put.put_regname);
+               else
+                   smsg("%4d PUT %c %ld", current,
+                                                iptr->isn_arg.put.put_regname,
                                             (long)iptr->isn_arg.put.put_lnum);
                break;