To: vim_dev@googlegroups.com Subject: Patch 8.2.2789 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.2789 Problem: Vim9: using \=expr in :substitute does not handle jumps. Solution: Start with instruction count zero. (closes #8128) Files: src/vim9compile.c, src/testdir/test_vim9_cmd.vim *** ../vim-8.2.2788/src/vim9compile.c 2021-04-19 21:06:27.454353644 +0200 --- src/vim9compile.c 2021-04-20 21:09:32.038843810 +0200 *************** *** 2179,2211 **** return OK; } - static int - generate_substitute(char_u *cmd, int instr_start, cctx_T *cctx) - { - isn_T *isn; - isn_T *instr; - int instr_count = cctx->ctx_instr.ga_len - instr_start; - - instr = ALLOC_MULT(isn_T, instr_count + 1); - if (instr == NULL) - return FAIL; - // Move the generated instructions into the ISN_SUBSTITUTE instructions, - // then truncate the list of instructions, so they are used only once. - mch_memmove(instr, ((isn_T *)cctx->ctx_instr.ga_data) + instr_start, - instr_count * sizeof(isn_T)); - instr[instr_count].isn_type = ISN_FINISH; - cctx->ctx_instr.ga_len = instr_start; - - if ((isn = generate_instr(cctx, ISN_SUBSTITUTE)) == NULL) - { - vim_free(instr); - return FAIL; - } - isn->isn_arg.subs.subs_cmd = vim_strsave(cmd); - isn->isn_arg.subs.subs_instr = instr; - return OK; - } - /* * Generate ISN_RANGE. Consumes "range". Return OK/FAIL. */ --- 2179,2184 ---- *************** *** 8522,8527 **** --- 8495,8511 ---- return nextcmd; } + + static void + clear_instr_ga(garray_T *gap) + { + int idx; + + for (idx = 0; idx < gap->ga_len; ++idx) + delete_instr(((isn_T *)gap->ga_data) + idx); + ga_clear(gap); + } + /* * :s/pat/repl/ */ *************** *** 8536,8563 **** int delimiter = *cmd++; // There is a \=expr, find it in the substitute part. ! cmd = skip_regexp_ex(cmd, delimiter, magic_isset(), ! NULL, NULL, NULL); if (cmd[0] == delimiter && cmd[1] == '\\' && cmd[2] == '=') { ! int instr_count = cctx->ctx_instr.ga_len; ! char_u *end; cmd += 3; end = skip_substitute(cmd, delimiter); compile_expr0(&cmd, cctx); if (end[-1] == NUL) end[-1] = delimiter; cmd = skipwhite(cmd); ! if (*cmd != delimiter && *cmd != NUL) ! { ! semsg(_(e_trailing_arg), cmd); return NULL; } ! if (generate_substitute(arg, instr_count, cctx) == FAIL) return NULL; // skip over flags if (*end == '&') --- 8520,8580 ---- int delimiter = *cmd++; // There is a \=expr, find it in the substitute part. ! cmd = skip_regexp_ex(cmd, delimiter, magic_isset(), NULL, NULL, NULL); if (cmd[0] == delimiter && cmd[1] == '\\' && cmd[2] == '=') { ! garray_T save_ga = cctx->ctx_instr; ! char_u *end; ! int trailing_error; ! int instr_count; ! isn_T *instr = NULL; ! isn_T *isn; cmd += 3; end = skip_substitute(cmd, delimiter); + // Temporarily reset the list of instructions so that the jumps + // labels are correct. + cctx->ctx_instr.ga_len = 0; + cctx->ctx_instr.ga_maxlen = 0; + cctx->ctx_instr.ga_data = NULL; compile_expr0(&cmd, cctx); if (end[-1] == NUL) end[-1] = delimiter; cmd = skipwhite(cmd); ! trailing_error = *cmd != delimiter && *cmd != NUL; ! ! instr_count = cctx->ctx_instr.ga_len; ! instr = ALLOC_MULT(isn_T, instr_count + 1); ! if (trailing_error || instr == NULL) ! { ! if (trailing_error) ! semsg(_(e_trailing_arg), cmd); ! clear_instr_ga(&cctx->ctx_instr); ! cctx->ctx_instr = save_ga; ! vim_free(instr); return NULL; } ! // Move the generated instructions into the ISN_SUBSTITUTE ! // instructions, then restore the list of instructions before ! // adding the ISN_SUBSTITUTE instruction. ! mch_memmove(instr, cctx->ctx_instr.ga_data, ! instr_count * sizeof(isn_T)); ! instr[instr_count].isn_type = ISN_FINISH; ! ! cctx->ctx_instr = save_ga; ! if ((isn = generate_instr(cctx, ISN_SUBSTITUTE)) == NULL) ! { ! int idx; ! ! for (idx = 0; idx < instr_count; ++idx) ! delete_instr(instr + idx); ! vim_free(instr); return NULL; + } + isn->isn_arg.subs.subs_cmd = vim_strsave(arg); + isn->isn_arg.subs.subs_instr = instr; // skip over flags if (*end == '&') *************** *** 9285,9297 **** erret: if (ufunc->uf_def_status == UF_COMPILING) { - int idx; dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) + ufunc->uf_dfunc_idx; ! for (idx = 0; idx < instr->ga_len; ++idx) ! delete_instr(((isn_T *)instr->ga_data) + idx); ! ga_clear(instr); VIM_CLEAR(dfunc->df_name); // If using the last entry in the table and it was added above, we --- 9302,9311 ---- erret: if (ufunc->uf_def_status == UF_COMPILING) { dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) + ufunc->uf_dfunc_idx; ! clear_instr_ga(instr); VIM_CLEAR(dfunc->df_name); // If using the last entry in the table and it was added above, we *** ../vim-8.2.2788/src/testdir/test_vim9_cmd.vim 2021-04-19 20:49:58.156857538 +0200 --- src/testdir/test_vim9_cmd.vim 2021-04-20 21:05:13.535665815 +0200 *************** *** 1188,1193 **** --- 1188,1199 ---- s/from/\=to .. '_' .. also/g#e assert_equal('one repl_also two repl_also three', getline(1)) + setline(1, 'abc abc abc') + for choice in [true, false] + :1s/abc/\=choice ? 'yes' : 'no'/ + endfor + assert_equal('yes no abc', getline(1)) + CheckDefFailure(['s/from/\="x")/'], 'E488:') CheckDefFailure(['s/from/\="x"/9'], 'E488:') *** ../vim-8.2.2788/src/version.c 2021-04-20 20:21:19.763851843 +0200 --- src/version.c 2021-04-20 21:10:16.838702338 +0200 *************** *** 752,753 **** --- 752,755 ---- { /* Add new patch number below this line */ + /**/ + 2789, /**/ -- hundred-and-one symptoms of being an internet addict: 131. You challenge authority and society by portnuking people /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///