Skip to content

Commit 0859787

Browse files
committed
patch 8.2.2124: Vim9: a range cannot be computed at runtime
Problem: Vim9: a range cannot be computed at runtime. Solution: Add the ISN_RANGE instruction.
1 parent d356fc6 commit 0859787

6 files changed

Lines changed: 128 additions & 12 deletions

File tree

src/testdir/test_vim9_cmd.vim

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,17 @@ def Test_put_command()
614614
assert_equal('above', getline(3))
615615
assert_equal('below', getline(4))
616616

617+
# compute range at runtime
618+
setline(1, range(1, 8))
619+
@a = 'aaa'
620+
:$-2put a
621+
assert_equal('aaa', getline(7))
622+
623+
setline(1, range(1, 8))
624+
:2
625+
:+2put! a
626+
assert_equal('aaa', getline(4))
627+
617628
bwipe!
618629
enddef
619630

src/testdir/test_vim9_disassemble.vim

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,21 @@ def Test_disassemble_put_expr()
133133
res)
134134
enddef
135135

136+
def s:PutRange()
137+
:$-2put a
138+
enddef
139+
140+
def Test_disassemble_put_range()
141+
var res = execute('disass s:PutRange')
142+
assert_match('<SNR>\d*_PutRange.*' ..
143+
' :$-2put a\_s*' ..
144+
'\d RANGE $-2\_s*' ..
145+
'\d PUT a range\_s*' ..
146+
'\d PUSHNR 0\_s*' ..
147+
'\d RETURN',
148+
res)
149+
enddef
150+
136151
def s:ScriptFuncPush()
137152
var localbool = true
138153
var localspec = v:none

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -750,6 +750,8 @@ static char *(features[]) =
750750

751751
static int included_patches[] =
752752
{ /* Add new patch number below this line */
753+
/**/
754+
2124,
753755
/**/
754756
2123,
755757
/**/

src/vim9.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ typedef enum {
1818
ISN_EXECUTE, // execute Ex commands isn_arg.number items on top of stack
1919
ISN_ECHOMSG, // echo Ex commands isn_arg.number items on top of stack
2020
ISN_ECHOERR, // echo Ex commands isn_arg.number items on top of stack
21+
ISN_RANGE, // compute range from isn_arg.string, push to stack
2122

2223
// get and set variables
2324
ISN_LOAD, // push local variable isn_arg.number
@@ -366,3 +367,8 @@ garray_T def_functions = {0, 0, sizeof(dfunc_T), 50, NULL};
366367
extern garray_T def_functions;
367368
#endif
368369

370+
// Used for "lnum" when a range is to be taken from the stack.
371+
#define LNUM_VARIABLE_RANGE -999
372+
373+
// Used for "lnum" when a range is to be taken from the stack and "!" is used.
374+
#define LNUM_VARIABLE_RANGE_ABOVE -888

src/vim9compile.c

Lines changed: 53 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1888,6 +1888,26 @@ generate_EXECCONCAT(cctx_T *cctx, int count)
18881888
return OK;
18891889
}
18901890

1891+
/*
1892+
* Generate ISN_RANGE. Consumes "range". Return OK/FAIL.
1893+
*/
1894+
static int
1895+
generate_RANGE(cctx_T *cctx, char_u *range)
1896+
{
1897+
isn_T *isn;
1898+
garray_T *stack = &cctx->ctx_type_stack;
1899+
1900+
if ((isn = generate_instr(cctx, ISN_RANGE)) == NULL)
1901+
return FAIL;
1902+
isn->isn_arg.string = range;
1903+
1904+
if (ga_grow(stack, 1) == FAIL)
1905+
return FAIL;
1906+
((type_T **)stack->ga_data)[stack->ga_len] = &t_number;
1907+
++stack->ga_len;
1908+
return OK;
1909+
}
1910+
18911911
static int
18921912
generate_UNPACK(cctx_T *cctx, int var_count, int semicolon)
18931913
{
@@ -7098,6 +7118,22 @@ compile_mult_expr(char_u *arg, int cmdidx, cctx_T *cctx)
70987118
return p;
70997119
}
71007120

7121+
/*
7122+
* If "eap" has a range that is not a contstant generate an ISN_RANGE
7123+
* instruction to compute it and return OK.
7124+
* Otherwise return FAIL, the caller must deal with any range.
7125+
*/
7126+
static int
7127+
compile_variable_range(exarg_T *eap, cctx_T *cctx)
7128+
{
7129+
char_u *range_end = skip_range(eap->cmd, TRUE, NULL);
7130+
char_u *p = skipdigits(eap->cmd);
7131+
7132+
if (p == range_end)
7133+
return FAIL;
7134+
return generate_RANGE(cctx, vim_strnsave(eap->cmd, range_end - eap->cmd));
7135+
}
7136+
71017137
/*
71027138
* :put r
71037139
* :put ={expr}
@@ -7123,17 +7159,23 @@ compile_put(char_u *arg, exarg_T *eap, cctx_T *cctx)
71237159
else if (eap->regname != NUL)
71247160
++line;
71257161

7126-
// "errormsg" will not be set because the range is ADDR_LINES.
7127-
// TODO: if the range contains something like "$" or "." need to evaluate
7128-
// at runtime
7129-
if (parse_cmd_address(eap, &errormsg, FALSE) == FAIL)
7130-
return NULL;
7131-
if (eap->addr_count == 0)
7132-
lnum = -1;
7162+
if (compile_variable_range(eap, cctx) == OK)
7163+
{
7164+
lnum = above ? LNUM_VARIABLE_RANGE_ABOVE : LNUM_VARIABLE_RANGE;
7165+
}
71337166
else
7134-
lnum = eap->line2;
7135-
if (above)
7136-
--lnum;
7167+
{
7168+
// Either no range or a number.
7169+
// "errormsg" will not be set because the range is ADDR_LINES.
7170+
if (parse_cmd_address(eap, &errormsg, FALSE) == FAIL)
7171+
return NULL;
7172+
if (eap->addr_count == 0)
7173+
lnum = -1;
7174+
else
7175+
lnum = eap->line2;
7176+
if (above)
7177+
--lnum;
7178+
}
71377179

71387180
generate_PUT(cctx, eap->regname, lnum);
71397181
return line;
@@ -7960,6 +8002,7 @@ delete_instr(isn_T *isn)
79608002
case ISN_PUSHEXC:
79618003
case ISN_PUSHFUNC:
79628004
case ISN_PUSHS:
8005+
case ISN_RANGE:
79638006
case ISN_STOREB:
79648007
case ISN_STOREENV:
79658008
case ISN_STOREG:

src/vim9execute.c

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2861,6 +2861,26 @@ call_def_function(
28612861
}
28622862
break;
28632863

2864+
case ISN_RANGE:
2865+
{
2866+
exarg_T ea;
2867+
char *errormsg;
2868+
2869+
if (GA_GROW(&ectx.ec_stack, 1) == FAIL)
2870+
goto failed;
2871+
++ectx.ec_stack.ga_len;
2872+
tv = STACK_TV_BOT(-1);
2873+
ea.addr_type = ADDR_LINES;
2874+
ea.cmd = iptr->isn_arg.string;
2875+
if (parse_cmd_address(&ea, &errormsg, FALSE) == FAIL)
2876+
goto failed;
2877+
if (ea.addr_count == 0)
2878+
tv->vval.v_number = curwin->w_cursor.lnum;
2879+
else
2880+
tv->vval.v_number = ea.line2;
2881+
}
2882+
break;
2883+
28642884
case ISN_PUT:
28652885
{
28662886
int regname = iptr->isn_arg.put.put_regname;
@@ -2880,7 +2900,16 @@ call_def_function(
28802900
}
28812901
--ectx.ec_stack.ga_len;
28822902
}
2883-
if (lnum == -2)
2903+
if (lnum < -2)
2904+
{
2905+
// line number was put on the stack by ISN_RANGE
2906+
tv = STACK_TV_BOT(-1);
2907+
curwin->w_cursor.lnum = tv->vval.v_number;
2908+
if (lnum == LNUM_VARIABLE_RANGE_ABOVE)
2909+
dir = BACKWARD;
2910+
--ectx.ec_stack.ga_len;
2911+
}
2912+
else if (lnum == -2)
28842913
// :put! above cursor
28852914
dir = BACKWARD;
28862915
else if (lnum >= 0)
@@ -3690,8 +3719,18 @@ ex_disassemble(exarg_T *eap)
36903719
case ISN_2STRING_ANY: smsg("%4d 2STRING_ANY stack[%lld]", current,
36913720
(long long)(iptr->isn_arg.number));
36923721
break;
3722+
case ISN_RANGE: smsg("%4d RANGE %s", current, iptr->isn_arg.string);
3723+
break;
36933724
case ISN_PUT:
3694-
smsg("%4d PUT %c %ld", current, iptr->isn_arg.put.put_regname,
3725+
if (iptr->isn_arg.put.put_lnum == LNUM_VARIABLE_RANGE_ABOVE)
3726+
smsg("%4d PUT %c above range",
3727+
current, iptr->isn_arg.put.put_regname);
3728+
else if (iptr->isn_arg.put.put_lnum == LNUM_VARIABLE_RANGE)
3729+
smsg("%4d PUT %c range",
3730+
current, iptr->isn_arg.put.put_regname);
3731+
else
3732+
smsg("%4d PUT %c %ld", current,
3733+
iptr->isn_arg.put.put_regname,
36953734
(long)iptr->isn_arg.put.put_lnum);
36963735
break;
36973736

0 commit comments

Comments
 (0)