0
0
mirror of https://github.com/vim/vim.git synced 2025-09-24 03:44:06 -04:00

patch 8.2.0714: Vim9: handling constant expression does not scale

Problem:    Vim9: handling constant expression does not scale.
Solution:   Use another solution, passint typval_T.
This commit is contained in:
Bram Moolenaar
2020-05-07 22:19:01 +02:00
parent 2cfb4a2a72
commit f0eefce93b
3 changed files with 165 additions and 61 deletions

View File

@@ -485,9 +485,8 @@ def Test_expr5()
assert_equal(-6, g:alsoint - g:anint) assert_equal(-6, g:alsoint - g:anint)
assert_equal('hello', 'hel' .. 'lo') assert_equal('hello', 'hel' .. 'lo')
" TODO: a line break here doesn't work assert_equal('hello 123', 'hello ' ..
" assert_equal('hello 123', 'hello ' .. 123)
" 123)
assert_equal('hello 123', 'hello ' .. 123) assert_equal('hello 123', 'hello ' .. 123)
assert_equal('123 hello', 123 .. ' hello') assert_equal('123 hello', 123 .. ' hello')
assert_equal('123456', 123 .. 456) assert_equal('123456', 123 .. 456)

View File

@@ -746,6 +746,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
714,
/**/ /**/
713, 713,
/**/ /**/

View File

@@ -1042,12 +1042,17 @@ generate_PUSHFUNC(cctx_T *cctx, char_u *name, type_T *type)
/* /*
* Generate a PUSH instruction for "tv". * Generate a PUSH instruction for "tv".
* "tv" will be consumed or cleared. "tv" may be NULL;
*/ */
static int static int
generate_tv_PUSH(cctx_T *cctx, typval_T *tv) generate_tv_PUSH(cctx_T *cctx, typval_T *tv)
{ {
if (tv != NULL)
{
switch (tv->v_type) switch (tv->v_type)
{ {
case VAR_UNKNOWN:
break;
case VAR_BOOL: case VAR_BOOL:
generate_PUSHBOOL(cctx, tv->vval.v_number); generate_PUSHBOOL(cctx, tv->vval.v_number);
break; break;
@@ -1072,8 +1077,11 @@ generate_tv_PUSH(cctx_T *cctx, typval_T *tv)
break; break;
default: default:
iemsg("constant type not supported"); iemsg("constant type not supported");
clear_tv(tv);
return FAIL; return FAIL;
} }
tv->v_type = VAR_UNKNOWN;
}
return OK; return OK;
} }
@@ -3719,7 +3727,10 @@ compile_subscript(
char_u **arg, char_u **arg,
cctx_T *cctx, cctx_T *cctx,
char_u **start_leader, char_u **start_leader,
char_u *end_leader) char_u *end_leader,
typval_T *bef1_tv,
typval_T *bef2_tv,
typval_T *new_tv)
{ {
for (;;) for (;;)
{ {
@@ -3729,6 +3740,11 @@ compile_subscript(
type_T *type; type_T *type;
int argcount = 0; int argcount = 0;
if (generate_tv_PUSH(cctx, bef1_tv) == FAIL
|| generate_tv_PUSH(cctx, bef2_tv) == FAIL
|| generate_tv_PUSH(cctx, new_tv) == FAIL)
return FAIL;
// funcref(arg) // funcref(arg)
type = ((type_T **)stack->ga_data)[stack->ga_len - 1]; type = ((type_T **)stack->ga_data)[stack->ga_len - 1];
@@ -3742,6 +3758,11 @@ compile_subscript(
{ {
char_u *p; char_u *p;
if (generate_tv_PUSH(cctx, bef1_tv) == FAIL
|| generate_tv_PUSH(cctx, bef2_tv) == FAIL
|| generate_tv_PUSH(cctx, new_tv) == FAIL)
return FAIL;
// something->method() // something->method()
// Apply the '!', '-' and '+' first: // Apply the '!', '-' and '+' first:
// -1.0->func() works like (-1.0)->func() // -1.0->func() works like (-1.0)->func()
@@ -3779,6 +3800,11 @@ compile_subscript(
garray_T *stack; garray_T *stack;
type_T **typep; type_T **typep;
if (generate_tv_PUSH(cctx, bef1_tv) == FAIL
|| generate_tv_PUSH(cctx, bef2_tv) == FAIL
|| generate_tv_PUSH(cctx, new_tv) == FAIL)
return FAIL;
// list index: list[123] // list index: list[123]
// TODO: more arguments // TODO: more arguments
// TODO: dict member dict['name'] // TODO: dict member dict['name']
@@ -3809,6 +3835,11 @@ compile_subscript(
{ {
char_u *p; char_u *p;
if (generate_tv_PUSH(cctx, bef1_tv) == FAIL
|| generate_tv_PUSH(cctx, bef2_tv) == FAIL
|| generate_tv_PUSH(cctx, new_tv) == FAIL)
return FAIL;
++*arg; ++*arg;
p = *arg; p = *arg;
// dictionary member: dict.name // dictionary member: dict.name
@@ -3837,10 +3868,13 @@ compile_subscript(
} }
/* /*
* Compile an expression at "*p" and add instructions to "instr". * Compile an expression at "*arg" and add instructions to "cctx->ctx_instr".
* "p" is advanced until after the expression, skipping white space. * "arg" is advanced until after the expression, skipping white space.
* *
* This is the equivalent of eval1(), eval2(), etc. * If the value is a constant "new_tv" will be set.
* Before instructions are generated, any "bef_tv" will generated.
*
* This is the compiling equivalent of eval1(), eval2(), etc.
*/ */
/* /*
@@ -3868,7 +3902,12 @@ compile_subscript(
* trailing ->name() method call * trailing ->name() method call
*/ */
static int static int
compile_expr7(char_u **arg, cctx_T *cctx) compile_expr7(
char_u **arg,
cctx_T *cctx,
typval_T *bef1_tv,
typval_T *bef2_tv,
typval_T *new_tv)
{ {
typval_T rettv; typval_T rettv;
char_u *start_leader, *end_leader; char_u *start_leader, *end_leader;
@@ -4007,15 +4046,18 @@ compile_expr7(char_u **arg, cctx_T *cctx)
} }
start_leader = end_leader; // don't apply again below start_leader = end_leader; // don't apply again below
// push constant // A constant expression can possibly be handled compile time.
if (generate_tv_PUSH(cctx, &rettv) == FAIL) *new_tv = rettv;
return FAIL;
} }
else if (ret == NOTDONE) else if (ret == NOTDONE)
{ {
char_u *p; char_u *p;
int r; int r;
if (generate_tv_PUSH(cctx, bef1_tv) == FAIL
|| generate_tv_PUSH(cctx, bef2_tv) == FAIL)
return FAIL;
if (!eval_isnamec1(**arg)) if (!eval_isnamec1(**arg))
{ {
semsg(_("E1015: Name expected: %s"), *arg); semsg(_("E1015: Name expected: %s"), *arg);
@@ -4032,7 +4074,8 @@ compile_expr7(char_u **arg, cctx_T *cctx)
return FAIL; return FAIL;
} }
if (compile_subscript(arg, cctx, &start_leader, end_leader) == FAIL) if (compile_subscript(arg, cctx, &start_leader, end_leader,
bef1_tv, bef2_tv, new_tv) == FAIL)
return FAIL; return FAIL;
// Now deal with prefixed '-', '+' and '!', if not done already. // Now deal with prefixed '-', '+' and '!', if not done already.
@@ -4045,12 +4088,16 @@ compile_expr7(char_u **arg, cctx_T *cctx)
* % number modulo * % number modulo
*/ */
static int static int
compile_expr6(char_u **arg, cctx_T *cctx) compile_expr6(
char_u **arg,
cctx_T *cctx,
typval_T *bef_tv,
typval_T *new_tv)
{ {
char_u *op; char_u *op;
// get the first variable // get the first expression
if (compile_expr7(arg, cctx) == FAIL) if (compile_expr7(arg, cctx, NULL, bef_tv, new_tv) == FAIL)
return FAIL; return FAIL;
/* /*
@@ -4058,9 +4105,12 @@ compile_expr6(char_u **arg, cctx_T *cctx)
*/ */
for (;;) for (;;)
{ {
typval_T tv2;
op = skipwhite(*arg); op = skipwhite(*arg);
if (*op != '*' && *op != '/' && *op != '%') if (*op != '*' && *op != '/' && *op != '%')
break; break;
if (!IS_WHITE_OR_NUL(**arg) || !IS_WHITE_OR_NUL(op[1])) if (!IS_WHITE_OR_NUL(**arg) || !IS_WHITE_OR_NUL(op[1]))
{ {
char_u buf[3]; char_u buf[3];
@@ -4073,12 +4123,33 @@ compile_expr6(char_u **arg, cctx_T *cctx)
if (may_get_next_line(op + 1, arg, cctx) == FAIL) if (may_get_next_line(op + 1, arg, cctx) == FAIL)
return FAIL; return FAIL;
// get the second variable // get the second expression
if (compile_expr7(arg, cctx) == FAIL) tv2.v_type = VAR_UNKNOWN;
if (compile_expr7(arg, cctx, bef_tv, new_tv, &tv2) == FAIL)
return FAIL; return FAIL;
if (new_tv->v_type == VAR_NUMBER && tv2.v_type == VAR_NUMBER)
{
varnumber_T res = 0;
// both are numbers: compute the result
switch (*op)
{
case '*': res = new_tv->vval.v_number * tv2.vval.v_number;
break;
case '/': res = new_tv->vval.v_number / tv2.vval.v_number;
break;
case '%': res = new_tv->vval.v_number % tv2.vval.v_number;
break;
}
new_tv->vval.v_number = res;
}
else
{
generate_tv_PUSH(cctx, new_tv);
generate_tv_PUSH(cctx, &tv2);
generate_two_op(cctx, op); generate_two_op(cctx, op);
} }
}
return OK; return OK;
} }
@@ -4091,11 +4162,13 @@ compile_expr6(char_u **arg, cctx_T *cctx)
static int static int
compile_expr5(char_u **arg, cctx_T *cctx) compile_expr5(char_u **arg, cctx_T *cctx)
{ {
typval_T tv1;
char_u *op; char_u *op;
int oplen; int oplen;
// get the first variable // get the first variable
if (compile_expr6(arg, cctx) == FAIL) tv1.v_type = VAR_UNKNOWN;
if (compile_expr6(arg, cctx, NULL, &tv1) == FAIL)
return FAIL; return FAIL;
/* /*
@@ -4103,6 +4176,8 @@ compile_expr5(char_u **arg, cctx_T *cctx)
*/ */
for (;;) for (;;)
{ {
typval_T tv2;
op = skipwhite(*arg); op = skipwhite(*arg);
if (*op != '+' && *op != '-' && !(*op == '.' && (*(*arg + 1) == '.'))) if (*op != '+' && *op != '-' && !(*op == '.' && (*(*arg + 1) == '.')))
break; break;
@@ -4121,10 +4196,44 @@ compile_expr5(char_u **arg, cctx_T *cctx)
if (may_get_next_line(op + oplen, arg, cctx) == FAIL) if (may_get_next_line(op + oplen, arg, cctx) == FAIL)
return FAIL; return FAIL;
// get the second variable // get the second expression
if (compile_expr6(arg, cctx) == FAIL) tv2.v_type = VAR_UNKNOWN;
if (compile_expr6(arg, cctx, &tv1, &tv2) == FAIL)
return FAIL; return FAIL;
if (*op == '+' && tv1.v_type == VAR_NUMBER && tv2.v_type == VAR_NUMBER)
{
// add constant numbers
tv1.vval.v_number = tv1.vval.v_number + tv2.vval.v_number;
}
else if (*op == '-' && tv1.v_type == VAR_NUMBER
&& tv2.v_type == VAR_NUMBER)
{
// subtract constant numbers
tv1.vval.v_number = tv1.vval.v_number - tv2.vval.v_number;
}
else if (*op == '.' && tv1.v_type == VAR_STRING
&& tv2.v_type == VAR_STRING)
{
// concatenate constant strings
char_u *s1 = tv1.vval.v_string;
char_u *s2 = tv2.vval.v_string;
size_t len1 = STRLEN(s1);
tv1.vval.v_string = alloc((int)(len1 + STRLEN(s2) + 1));
if (tv1.vval.v_string == NULL)
{
vim_free(s1);
vim_free(s2);
return FAIL;
}
mch_memmove(tv1.vval.v_string, s1, len1);
STRCPY(tv1.vval.v_string + len1, s2);
}
else
{
generate_tv_PUSH(cctx, &tv1);
generate_tv_PUSH(cctx, &tv2);
if (*op == '.') if (*op == '.')
{ {
if (may_generate_2STRING(-2, cctx) == FAIL if (may_generate_2STRING(-2, cctx) == FAIL
@@ -4135,6 +4244,10 @@ compile_expr5(char_u **arg, cctx_T *cctx)
else else
generate_two_op(cctx, op); generate_two_op(cctx, op);
} }
}
// TODO: move to caller
generate_tv_PUSH(cctx, &tv1);
return OK; return OK;
} }
@@ -4342,19 +4455,9 @@ compile_expr2(char_u **arg, cctx_T *cctx)
compile_expr1(char_u **arg, cctx_T *cctx) compile_expr1(char_u **arg, cctx_T *cctx)
{ {
char_u *p; char_u *p;
typval_T tv;
// Evaluate the first expression. // Evaluate the first expression.
// First try parsing as a constant. If that works just one PUSH if (compile_expr2(arg, cctx) == FAIL)
// instruction needs to be generated.
tv.v_type = VAR_UNKNOWN;
p = *arg;
if (evaluate_const_expr2(&p, cctx, &tv) == OK)
{
*arg = p;
generate_tv_PUSH(cctx, &tv);
}
else if (compile_expr2(arg, cctx) == FAIL)
return FAIL; return FAIL;
p = skipwhite(*arg); p = skipwhite(*arg);