# HG changeset patch # User Bram Moolenaar # Date 1588883403 -7200 # Node ID 3cbcee39fc5c4d1ce0252d4dff7199be1ef783be # Parent 2334bf788e8a01f3d91e1cdc8703d8743073731d patch 8.2.0714: Vim9: handling constant expression does not scale Commit: https://github.com/vim/vim/commit/f0eefce93bb06037a2d45e7ea8c0ad6f1f714151 Author: Bram Moolenaar Date: Thu May 7 22:19:01 2020 +0200 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. diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim --- a/src/testdir/test_vim9_expr.vim +++ b/src/testdir/test_vim9_expr.vim @@ -485,9 +485,8 @@ def Test_expr5() assert_equal(-6, g:alsoint - g:anint) assert_equal('hello', 'hel' .. 'lo') - " TODO: a line break here doesn't work -" assert_equal('hello 123', 'hello ' .. -" 123) + assert_equal('hello 123', 'hello ' .. + 123) assert_equal('hello 123', 'hello ' .. 123) assert_equal('123 hello', 123 .. ' hello') assert_equal('123456', 123 .. 456) diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -747,6 +747,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 714, +/**/ 713, /**/ 712, diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -1042,37 +1042,45 @@ generate_PUSHFUNC(cctx_T *cctx, char_u * /* * Generate a PUSH instruction for "tv". + * "tv" will be consumed or cleared. "tv" may be NULL; */ static int generate_tv_PUSH(cctx_T *cctx, typval_T *tv) { - switch (tv->v_type) - { - case VAR_BOOL: - generate_PUSHBOOL(cctx, tv->vval.v_number); - break; - case VAR_SPECIAL: - generate_PUSHSPEC(cctx, tv->vval.v_number); - break; - case VAR_NUMBER: - generate_PUSHNR(cctx, tv->vval.v_number); - break; + if (tv != NULL) + { + switch (tv->v_type) + { + case VAR_UNKNOWN: + break; + case VAR_BOOL: + generate_PUSHBOOL(cctx, tv->vval.v_number); + break; + case VAR_SPECIAL: + generate_PUSHSPEC(cctx, tv->vval.v_number); + break; + case VAR_NUMBER: + generate_PUSHNR(cctx, tv->vval.v_number); + break; #ifdef FEAT_FLOAT - case VAR_FLOAT: - generate_PUSHF(cctx, tv->vval.v_float); - break; + case VAR_FLOAT: + generate_PUSHF(cctx, tv->vval.v_float); + break; #endif - case VAR_BLOB: - generate_PUSHBLOB(cctx, tv->vval.v_blob); - tv->vval.v_blob = NULL; - break; - case VAR_STRING: - generate_PUSHS(cctx, tv->vval.v_string); - tv->vval.v_string = NULL; - break; - default: - iemsg("constant type not supported"); - return FAIL; + case VAR_BLOB: + generate_PUSHBLOB(cctx, tv->vval.v_blob); + tv->vval.v_blob = NULL; + break; + case VAR_STRING: + generate_PUSHS(cctx, tv->vval.v_string); + tv->vval.v_string = NULL; + break; + default: + iemsg("constant type not supported"); + clear_tv(tv); + return FAIL; + } + tv->v_type = VAR_UNKNOWN; } return OK; } @@ -3719,7 +3727,10 @@ compile_subscript( char_u **arg, cctx_T *cctx, 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 (;;) { @@ -3729,6 +3740,11 @@ compile_subscript( type_T *type; 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) type = ((type_T **)stack->ga_data)[stack->ga_len - 1]; @@ -3742,6 +3758,11 @@ compile_subscript( { 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() // Apply the '!', '-' and '+' first: // -1.0->func() works like (-1.0)->func() @@ -3779,6 +3800,11 @@ compile_subscript( garray_T *stack; 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] // TODO: more arguments // TODO: dict member dict['name'] @@ -3809,6 +3835,11 @@ compile_subscript( { 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; p = *arg; // dictionary member: dict.name @@ -3837,10 +3868,13 @@ compile_subscript( } /* - * Compile an expression at "*p" and add instructions to "instr". - * "p" is advanced until after the expression, skipping white space. + * Compile an expression at "*arg" and add instructions to "cctx->ctx_instr". + * "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 */ 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; 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 - // push constant - if (generate_tv_PUSH(cctx, &rettv) == FAIL) - return FAIL; + // A constant expression can possibly be handled compile time. + *new_tv = rettv; } else if (ret == NOTDONE) { char_u *p; int r; + if (generate_tv_PUSH(cctx, bef1_tv) == FAIL + || generate_tv_PUSH(cctx, bef2_tv) == FAIL) + return FAIL; + if (!eval_isnamec1(**arg)) { semsg(_("E1015: Name expected: %s"), *arg); @@ -4032,7 +4074,8 @@ compile_expr7(char_u **arg, cctx_T *cctx 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; // Now deal with prefixed '-', '+' and '!', if not done already. @@ -4045,12 +4088,16 @@ compile_expr7(char_u **arg, cctx_T *cctx * % number modulo */ 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; - // get the first variable - if (compile_expr7(arg, cctx) == FAIL) + // get the first expression + if (compile_expr7(arg, cctx, NULL, bef_tv, new_tv) == FAIL) return FAIL; /* @@ -4058,9 +4105,12 @@ compile_expr6(char_u **arg, cctx_T *cctx */ for (;;) { + typval_T tv2; + op = skipwhite(*arg); if (*op != '*' && *op != '/' && *op != '%') break; + if (!IS_WHITE_OR_NUL(**arg) || !IS_WHITE_OR_NUL(op[1])) { char_u buf[3]; @@ -4073,11 +4123,32 @@ compile_expr6(char_u **arg, cctx_T *cctx if (may_get_next_line(op + 1, arg, cctx) == FAIL) return FAIL; - // get the second variable - if (compile_expr7(arg, cctx) == FAIL) + // get the second expression + tv2.v_type = VAR_UNKNOWN; + if (compile_expr7(arg, cctx, bef_tv, new_tv, &tv2) == FAIL) return FAIL; - - generate_two_op(cctx, op); + 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); + } } return OK; @@ -4091,11 +4162,13 @@ compile_expr6(char_u **arg, cctx_T *cctx static int compile_expr5(char_u **arg, cctx_T *cctx) { + typval_T tv1; char_u *op; int oplen; // 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; /* @@ -4103,6 +4176,8 @@ compile_expr5(char_u **arg, cctx_T *cctx */ for (;;) { + typval_T tv2; + op = skipwhite(*arg); if (*op != '+' && *op != '-' && !(*op == '.' && (*(*arg + 1) == '.'))) break; @@ -4121,20 +4196,58 @@ compile_expr5(char_u **arg, cctx_T *cctx if (may_get_next_line(op + oplen, arg, cctx) == FAIL) return FAIL; - // get the second variable - if (compile_expr6(arg, cctx) == FAIL) + // get the second expression + tv2.v_type = VAR_UNKNOWN; + if (compile_expr6(arg, cctx, &tv1, &tv2) == FAIL) return FAIL; - if (*op == '.') + 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) { - if (may_generate_2STRING(-2, cctx) == FAIL - || may_generate_2STRING(-1, cctx) == FAIL) + // 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; - generate_instr_drop(cctx, ISN_CONCAT, 1); + } + mch_memmove(tv1.vval.v_string, s1, len1); + STRCPY(tv1.vval.v_string + len1, s2); } else - generate_two_op(cctx, op); - } + { + generate_tv_PUSH(cctx, &tv1); + generate_tv_PUSH(cctx, &tv2); + if (*op == '.') + { + if (may_generate_2STRING(-2, cctx) == FAIL + || may_generate_2STRING(-1, cctx) == FAIL) + return FAIL; + generate_instr_drop(cctx, ISN_CONCAT, 1); + } + else + generate_two_op(cctx, op); + } + } + + // TODO: move to caller + generate_tv_PUSH(cctx, &tv1); return OK; } @@ -4342,19 +4455,9 @@ compile_expr2(char_u **arg, cctx_T *cctx compile_expr1(char_u **arg, cctx_T *cctx) { char_u *p; - typval_T tv; // Evaluate the first expression. - // First try parsing as a constant. If that works just one PUSH - // 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) + if (compile_expr2(arg, cctx) == FAIL) return FAIL; p = skipwhite(*arg);