To: vim_dev@googlegroups.com Subject: Patch 8.2.2124 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.2124 Problem: Vim9: a range cannot be computed at runtime. Solution: Add the ISN_RANGE instruction. Files: src/vim9.h, src/vim9compile.c, src/vim9execute.c, src/testdir/test_vim9_cmd.vim, src/testdir/test_vim9_disassemble.vim *** ../vim-8.2.2123/src/vim9.h 2020-11-23 08:31:14.097789125 +0100 --- src/vim9.h 2020-12-09 19:36:11.914988439 +0100 *************** *** 18,23 **** --- 18,24 ---- 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,368 **** --- 367,374 ---- 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 *** ../vim-8.2.2123/src/vim9compile.c 2020-12-08 22:08:47.672125693 +0100 --- src/vim9compile.c 2020-12-10 19:42:52.365297164 +0100 *************** *** 1888,1893 **** --- 1888,1913 ---- 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,7104 **** --- 7119,7140 ---- } /* + * 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,7139 **** 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; else ! lnum = eap->line2; ! if (above) ! --lnum; generate_PUT(cctx, eap->regname, lnum); return line; --- 7159,7181 ---- else if (eap->regname != NUL) ++line; ! if (compile_variable_range(eap, cctx) == OK) ! { ! lnum = above ? LNUM_VARIABLE_RANGE_ABOVE : LNUM_VARIABLE_RANGE; ! } else ! { ! // 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,7965 **** --- 8002,8008 ---- case ISN_PUSHEXC: case ISN_PUSHFUNC: case ISN_PUSHS: + case ISN_RANGE: case ISN_STOREB: case ISN_STOREENV: case ISN_STOREG: *** ../vim-8.2.2123/src/vim9execute.c 2020-12-05 21:22:03.626811733 +0100 --- src/vim9execute.c 2020-12-09 21:47:43.623141586 +0100 *************** *** 2861,2866 **** --- 2861,2886 ---- } 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,2886 **** } --ectx.ec_stack.ga_len; } ! if (lnum == -2) // :put! above cursor dir = BACKWARD; else if (lnum >= 0) --- 2900,2915 ---- } --ectx.ec_stack.ga_len; } ! 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,3697 **** case ISN_2STRING_ANY: smsg("%4d 2STRING_ANY stack[%lld]", current, (long long)(iptr->isn_arg.number)); break; case ISN_PUT: ! smsg("%4d PUT %c %ld", current, iptr->isn_arg.put.put_regname, (long)iptr->isn_arg.put.put_lnum); break; --- 3719,3736 ---- 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: ! 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; *** ../vim-8.2.2123/src/testdir/test_vim9_cmd.vim 2020-12-05 17:11:09.241679401 +0100 --- src/testdir/test_vim9_cmd.vim 2020-12-09 22:02:08.095304957 +0100 *************** *** 614,619 **** --- 614,630 ---- 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 *** ../vim-8.2.2123/src/testdir/test_vim9_disassemble.vim 2020-12-02 17:36:49.173409740 +0100 --- src/testdir/test_vim9_disassemble.vim 2020-12-09 21:57:08.792637746 +0100 *************** *** 133,138 **** --- 133,153 ---- res) enddef + def s:PutRange() + :$-2put a + enddef + + def Test_disassemble_put_range() + var res = execute('disass s:PutRange') + assert_match('\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 *** ../vim-8.2.2123/src/version.c 2020-12-09 18:13:40.713680179 +0100 --- src/version.c 2020-12-10 19:40:03.325837051 +0100 *************** *** 752,753 **** --- 752,755 ---- { /* Add new patch number below this line */ + /**/ + 2124, /**/ -- Behold the warranty! The bold print giveth and the fine print taketh. /// 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 ///