To: vim_dev@googlegroups.com Subject: Patch 8.2.0714 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.0714 Problem: Vim9: handling constant expression does not scale. Solution: Use another solution, passint typval_T. Files: src/vim9compile.c, src/testdir/test_vim9_expr.vim *** ../vim-8.2.0713/src/vim9compile.c 2020-05-07 16:58:10.868572663 +0200 --- src/vim9compile.c 2020-05-07 21:18:51.200742094 +0200 *************** *** 1042,1078 **** /* * Generate a PUSH instruction for "tv". */ 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; #ifdef FEAT_FLOAT ! 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; } return OK; } --- 1042,1086 ---- /* * 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) { ! 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; #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"); ! clear_tv(tv); ! return FAIL; ! } ! tv->v_type = VAR_UNKNOWN; } return OK; } *************** *** 3719,3725 **** char_u **arg, cctx_T *cctx, char_u **start_leader, ! char_u *end_leader) { for (;;) { --- 3727,3736 ---- char_u **arg, cctx_T *cctx, char_u **start_leader, ! char_u *end_leader, ! typval_T *bef1_tv, ! typval_T *bef2_tv, ! typval_T *new_tv) { for (;;) { *************** *** 3729,3734 **** --- 3740,3750 ---- 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,3747 **** --- 3758,3768 ---- { 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,3784 **** --- 3800,3810 ---- 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,3814 **** --- 3835,3845 ---- { 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,3846 **** } /* ! * Compile an expression at "*p" and add instructions to "instr". ! * "p" is advanced until after the expression, skipping white space. * ! * This is the equivalent of eval1(), eval2(), etc. */ /* --- 3868,3880 ---- } /* ! * Compile an expression at "*arg" and add instructions to "cctx->ctx_instr". ! * "arg" is advanced until after the expression, skipping white space. * ! * 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,3874 **** * trailing ->name() method call */ static int ! compile_expr7(char_u **arg, cctx_T *cctx) { typval_T rettv; char_u *start_leader, *end_leader; --- 3902,3913 ---- * trailing ->name() method call */ static int ! 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,4021 **** } start_leader = end_leader; // don't apply again below ! // push constant ! if (generate_tv_PUSH(cctx, &rettv) == FAIL) ! return FAIL; } else if (ret == NOTDONE) { char_u *p; int r; if (!eval_isnamec1(**arg)) { semsg(_("E1015: Name expected: %s"), *arg); --- 4046,4063 ---- } start_leader = end_leader; // don't apply again below ! // 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,4038 **** return FAIL; } ! if (compile_subscript(arg, cctx, &start_leader, end_leader) == FAIL) return FAIL; // Now deal with prefixed '-', '+' and '!', if not done already. --- 4074,4081 ---- return 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,4056 **** * % number modulo */ static int ! compile_expr6(char_u **arg, cctx_T *cctx) { char_u *op; ! // get the first variable ! if (compile_expr7(arg, cctx) == FAIL) return FAIL; /* --- 4088,4103 ---- * % number modulo */ static int ! compile_expr6( ! char_u **arg, ! cctx_T *cctx, ! typval_T *bef_tv, ! typval_T *new_tv) { char_u *op; ! // get the first expression ! if (compile_expr7(arg, cctx, NULL, bef_tv, new_tv) == FAIL) return FAIL; /* *************** *** 4058,4066 **** --- 4105,4116 ---- */ 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,4083 **** if (may_get_next_line(op + 1, arg, cctx) == FAIL) return FAIL; ! // get the second variable ! if (compile_expr7(arg, cctx) == FAIL) return FAIL; ! generate_two_op(cctx, op); } return OK; --- 4123,4154 ---- if (may_get_next_line(op + 1, arg, cctx) == FAIL) return FAIL; ! // get the second expression ! tv2.v_type = VAR_UNKNOWN; ! if (compile_expr7(arg, cctx, bef_tv, new_tv, &tv2) == 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); ! } } return OK; *************** *** 4091,4101 **** static int compile_expr5(char_u **arg, cctx_T *cctx) { char_u *op; int oplen; // get the first variable ! if (compile_expr6(arg, cctx) == FAIL) return FAIL; /* --- 4162,4174 ---- static int compile_expr5(char_u **arg, cctx_T *cctx) { + typval_T tv1; char_u *op; int oplen; // get the first variable ! tv1.v_type = VAR_UNKNOWN; ! if (compile_expr6(arg, cctx, NULL, &tv1) == FAIL) return FAIL; /* *************** *** 4103,4108 **** --- 4176,4183 ---- */ for (;;) { + typval_T tv2; + op = skipwhite(*arg); if (*op != '+' && *op != '-' && !(*op == '.' && (*(*arg + 1) == '.'))) break; *************** *** 4121,4141 **** if (may_get_next_line(op + oplen, arg, cctx) == FAIL) return FAIL; ! // get the second variable ! if (compile_expr6(arg, cctx) == FAIL) return FAIL; ! 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); } return OK; } --- 4196,4254 ---- if (may_get_next_line(op + oplen, arg, cctx) == FAIL) return FAIL; ! // get the second expression ! tv2.v_type = VAR_UNKNOWN; ! if (compile_expr6(arg, cctx, &tv1, &tv2) == 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 (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,4360 **** 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) return FAIL; p = skipwhite(*arg); --- 4455,4463 ---- compile_expr1(char_u **arg, cctx_T *cctx) { char_u *p; // Evaluate the first expression. ! if (compile_expr2(arg, cctx) == FAIL) return FAIL; p = skipwhite(*arg); *** ../vim-8.2.0713/src/testdir/test_vim9_expr.vim 2020-05-07 16:58:10.872572650 +0200 --- src/testdir/test_vim9_expr.vim 2020-05-07 21:03:19.840077657 +0200 *************** *** 485,493 **** 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('123 hello', 123 .. ' hello') assert_equal('123456', 123 .. 456) --- 485,492 ---- assert_equal(-6, g:alsoint - g:anint) assert_equal('hello', 'hel' .. 'lo') ! assert_equal('hello 123', 'hello ' .. ! 123) assert_equal('hello 123', 'hello ' .. 123) assert_equal('123 hello', 123 .. ' hello') assert_equal('123456', 123 .. 456) *** ../vim-8.2.0713/src/version.c 2020-05-07 18:51:24.161933050 +0200 --- src/version.c 2020-05-07 21:04:17.547881750 +0200 *************** *** 748,749 **** --- 748,751 ---- { /* Add new patch number below this line */ + /**/ + 714, /**/ -- hundred-and-one symptoms of being an internet addict: 65. The last time you looked at the clock it was 11:30pm, and in what seems like only a few seconds later, your sister runs past you to catch her 7am school bus. /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///