summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2020-12-10 19:43:40 +0100
committerBram Moolenaar <Bram@vim.org>2020-12-10 19:43:40 +0100
commit08597875b2a1e7d118b0346c652a96e7527e7d8b (patch)
tree0bb1d04eadfe4110a7962e9fcbfd361452677205
parentd356fc65d273959efa9b05bfa0f07ce1c9ff85a4 (diff)
patch 8.2.2124: Vim9: a range cannot be computed at runtimev8.2.2124
Problem: Vim9: a range cannot be computed at runtime. Solution: Add the ISN_RANGE instruction.
-rw-r--r--src/testdir/test_vim9_cmd.vim11
-rw-r--r--src/testdir/test_vim9_disassemble.vim15
-rw-r--r--src/version.c2
-rw-r--r--src/vim9.h6
-rw-r--r--src/vim9compile.c63
-rw-r--r--src/vim9execute.c43
6 files changed, 128 insertions, 12 deletions
diff --git a/src/testdir/test_vim9_cmd.vim b/src/testdir/test_vim9_cmd.vim
index 37fc559bf0..1986c0f71e 100644
--- a/src/testdir/test_vim9_cmd.vim
+++ b/src/testdir/test_vim9_cmd.vim
@@ -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
diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim
index e7445d3860..fabb8d2618 100644
--- a/src/testdir/test_vim9_disassemble.vim
+++ b/src/testdir/test_vim9_disassemble.vim
@@ -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
diff --git a/src/version.c b/src/version.c
index 3c39f0e180..ee6be8a768 100644
--- a/src/version.c
+++ b/src/version.c
@@ -751,6 +751,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 2124,
+/**/
2123,
/**/
2122,
diff --git a/src/vim9.h b/src/vim9.h
index 1368b38437..88cc4fce24 100644
--- a/src/vim9.h
+++ b/src/vim9.h
@@ -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
diff --git a/src/vim9compile.c b/src/vim9compile.c
index da069f405b..ac8fb846a5 100644
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -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)
{
@@ -7099,6 +7119,22 @@ compile_mult_expr(char_u *arg, int cmdidx, cctx_T *cctx)
}
/*
+ * 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:
diff --git a/src/vim9execute.c b/src/vim9execute.c
index 7e8b336d09..3f61cb4457 100644
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -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;