To: vim_dev@googlegroups.com Subject: Patch 8.2.2842 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.2842 Problem: Vim9: skip argument to searchpair() is not compiled. Solution: Add VAR_INSTR. Files: src/structs.h, src/vim9.h, src/vim9compile.c, src/vim9execute.c, src/proto/vim9execute.pro, src/eval.c, src/evalfunc.c, src/vim.h, src/evalvars.c, src/typval.c, src/vim9type.c, src/testing.c, src/viminfo.c, src/if_py_both.h, src/json.c, src/testdir/test_vim9_disassemble.vim, src/testdir/test_vim9_builtin.vim *** ../vim-8.2.2841/src/structs.h 2021-04-24 14:15:03.680264471 +0200 --- src/structs.h 2021-05-07 15:33:39.708802153 +0200 *************** *** 1371,1376 **** --- 1371,1377 ---- typedef struct channel_S channel_T; typedef struct cctx_S cctx_T; typedef struct ectx_S ectx_T; + typedef struct instr_S instr_T; typedef enum { *************** *** 1389,1394 **** --- 1390,1396 ---- VAR_DICT, // "v_dict" is used VAR_JOB, // "v_job" is used VAR_CHANNEL, // "v_channel" is used + VAR_INSTR, // "v_instr" is used } vartype_T; // A type specification. *************** *** 1429,1434 **** --- 1431,1437 ---- channel_T *v_channel; // channel value (can be NULL!) #endif blob_T *v_blob; // blob value (can be NULL!) + instr_T *v_instr; // instructions to execute } vval; } typval_T; *** ../vim-8.2.2841/src/vim9.h 2021-05-05 21:31:36.068018579 +0200 --- src/vim9.h 2021-05-07 16:52:30.209276710 +0200 *************** *** 20,25 **** --- 20,26 ---- ISN_ECHOERR, // echo Ex commands isn_arg.number items on top of stack ISN_RANGE, // compute range from isn_arg.string, push to stack ISN_SUBSTITUTE, // :s command with expression + ISN_INSTR, // instructions compiled from expression // get and set variables ISN_LOAD, // push local variable isn_arg.number *************** *** 411,416 **** --- 412,418 ---- isn_outer_T outer; subs_T subs; cexpr_T cexpr; + isn_T *instr; } isn_arg; }; *** ../vim-8.2.2841/src/vim9compile.c 2021-05-06 21:04:51.514534033 +0200 --- src/vim9compile.c 2021-05-07 16:54:08.825005973 +0200 *************** *** 615,620 **** --- 615,621 ---- case VAR_DICT: case VAR_JOB: case VAR_CHANNEL: + case VAR_INSTR: to_string_error((*type)->tt_type); return FAIL; } *************** *** 3097,3112 **** return res; } /* * Compile the argument expressions. * "arg" points to just after the "(" and is advanced to after the ")" */ static int ! compile_arguments(char_u **arg, cctx_T *cctx, int *argcount) { char_u *p = *arg; char_u *whitep = *arg; int must_end = FALSE; for (;;) { --- 3098,3169 ---- return res; } + 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); + } + + /* + * Compile a string in a ISN_PUSHS instruction into an ISN_INSTR. + * Returns FAIL if compilation fails. + */ + static int + compile_string(isn_T *isn, cctx_T *cctx) + { + char_u *s = isn->isn_arg.string; + garray_T save_ga = cctx->ctx_instr; + int expr_res; + int trailing_error; + int instr_count; + isn_T *instr = NULL; + + // Temporarily reset the list of instructions so that the jump labels are + // correct. + cctx->ctx_instr.ga_len = 0; + cctx->ctx_instr.ga_maxlen = 0; + cctx->ctx_instr.ga_data = NULL; + expr_res = compile_expr0(&s, cctx); + s = skipwhite(s); + trailing_error = *s != NUL; + + if (expr_res == FAIL || trailing_error) + { + if (trailing_error) + semsg(_(e_trailing_arg), s); + clear_instr_ga(&cctx->ctx_instr); + cctx->ctx_instr = save_ga; + return FAIL; + } + + // Move the generated instructions into the ISN_INSTR instruction, then + // restore the list of instructions. + instr_count = cctx->ctx_instr.ga_len; + instr = cctx->ctx_instr.ga_data; + instr[instr_count].isn_type = ISN_FINISH; + + cctx->ctx_instr = save_ga; + vim_free(isn->isn_arg.string); + isn->isn_type = ISN_INSTR; + isn->isn_arg.instr = instr; + return OK; + } + /* * Compile the argument expressions. * "arg" points to just after the "(" and is advanced to after the ")" */ static int ! compile_arguments(char_u **arg, cctx_T *cctx, int *argcount, int is_searchpair) { char_u *p = *arg; char_u *whitep = *arg; int must_end = FALSE; + int instr_count; for (;;) { *************** *** 3123,3132 **** --- 3180,3200 ---- return FAIL; } + instr_count = cctx->ctx_instr.ga_len; if (compile_expr0(&p, cctx) == FAIL) return FAIL; ++*argcount; + if (is_searchpair && *argcount == 5 + && cctx->ctx_instr.ga_len == instr_count + 1) + { + isn_T *isn = ((isn_T *)cctx->ctx_instr.ga_data) + instr_count; + + // {skip} argument of searchpair() can be compiled if not empty + if (isn->isn_type == ISN_PUSHS && *isn->isn_arg.string != NUL) + compile_string(isn, cctx); + } + if (*p != ',' && *skipwhite(p) == ',') { semsg(_(e_no_white_space_allowed_before_str_str), ",", p); *************** *** 3175,3180 **** --- 3243,3249 ---- ufunc_T *ufunc = NULL; int res = FAIL; int is_autoload; + int is_searchpair; // we can evaluate "has('name')" at compile time if (varlen == 3 && STRNCMP(*arg, "has", 3) == 0) *************** *** 3216,3223 **** vim_strncpy(namebuf, *arg, varlen); name = fname_trans_sid(namebuf, fname_buf, &tofree, &error); *arg = skipwhite(*arg + varlen + 1); ! if (compile_arguments(arg, cctx, &argcount) == FAIL) goto theend; is_autoload = vim_strchr(name, AUTOLOAD_CHAR) != NULL; --- 3285,3295 ---- vim_strncpy(namebuf, *arg, varlen); name = fname_trans_sid(namebuf, fname_buf, &tofree, &error); + // we handle the "skip" argument of searchpair() differently + is_searchpair = (varlen == 10 && STRNCMP(*arg, "searchpair", 10) == 0); + *arg = skipwhite(*arg + varlen + 1); ! if (compile_arguments(arg, cctx, &argcount, is_searchpair) == FAIL) goto theend; is_autoload = vim_strchr(name, AUTOLOAD_CHAR) != NULL; *************** *** 4027,4033 **** type = ((type_T **)stack->ga_data)[stack->ga_len - 1]; *arg = skipwhite(p + 1); ! if (compile_arguments(arg, cctx, &argcount) == FAIL) return FAIL; if (generate_PCALL(cctx, argcount, name_start, type, TRUE) == FAIL) return FAIL; --- 4099,4105 ---- type = ((type_T **)stack->ga_data)[stack->ga_len - 1]; *arg = skipwhite(p + 1); ! if (compile_arguments(arg, cctx, &argcount, FALSE) == FAIL) return FAIL; if (generate_PCALL(cctx, argcount, name_start, type, TRUE) == FAIL) return FAIL; *************** *** 4080,4086 **** return FAIL; } *arg = skipwhite(*arg + 1); ! if (compile_arguments(arg, cctx, &argcount) == FAIL) return FAIL; // Move the instructions for the arguments to before the --- 4152,4158 ---- return FAIL; } *arg = skipwhite(*arg + 1); ! if (compile_arguments(arg, cctx, &argcount, FALSE) == FAIL) return FAIL; // Move the instructions for the arguments to before the *************** *** 6728,6733 **** --- 6800,6806 ---- case VAR_ANY: case VAR_PARTIAL: case VAR_VOID: + case VAR_INSTR: case VAR_SPECIAL: // cannot happen generate_PUSHNR(cctx, 0); break; *************** *** 8536,8551 **** } - 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/ */ --- 8609,8614 ---- *************** *** 8568,8580 **** int expr_res; 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; --- 8631,8643 ---- int expr_res; int trailing_error; int instr_count; ! isn_T *instr; isn_T *isn; cmd += 3; end = skip_substitute(cmd, delimiter); ! // Temporarily reset the list of instructions so that the jump // labels are correct. cctx->ctx_instr.ga_len = 0; cctx->ctx_instr.ga_maxlen = 0; *************** *** 8592,8598 **** semsg(_(e_trailing_arg), cmd); clear_instr_ga(&cctx->ctx_instr); cctx->ctx_instr = save_ga; - vim_free(instr); return NULL; } --- 8655,8660 ---- *************** *** 9552,9557 **** --- 9614,9630 ---- for (idx = 0; list[idx].isn_type != ISN_FINISH; ++idx) delete_instr(list + idx); vim_free(list); + } + break; + + case ISN_INSTR: + { + int idx; + isn_T *list = isn->isn_arg.instr; + + for (idx = 0; list[idx].isn_type != ISN_FINISH; ++idx) + delete_instr(list + idx); + vim_free(list); } break; *** ../vim-8.2.2841/src/vim9execute.c 2021-05-05 22:51:35.631336525 +0200 --- src/vim9execute.c 2021-05-07 16:57:49.448411700 +0200 *************** *** 1259,1264 **** --- 1259,1270 ---- return OK; } + // used for v_instr of typval of VAR_INSTR + struct instr_S { + ectx_T *instr_ectx; + isn_T *instr_instr; + }; + // used for substitute_instr typedef struct subs_expr_S { ectx_T *subs_ectx; *************** *** 1379,1384 **** --- 1385,1407 ---- } break; + // push typeval VAR_INSTR with instructions to be executed + case ISN_INSTR: + { + if (GA_GROW(&ectx->ec_stack, 1) == FAIL) + return FAIL; + tv = STACK_TV_BOT(0); + tv->vval.v_instr = ALLOC_ONE(instr_T); + if (tv->vval.v_instr == NULL) + goto on_error; + ++ectx->ec_stack.ga_len; + + tv->v_type = VAR_INSTR; + tv->vval.v_instr->instr_ectx = ectx; + tv->vval.v_instr->instr_instr = iptr->isn_arg.instr; + } + break; + // execute :substitute with an expression case ISN_SUBSTITUTE: { *************** *** 3997,4002 **** --- 4020,4052 ---- } /* + * Execute the instructions from a VAR_INSTR typeval and put the result in + * "rettv". + * Return OK or FAIL. + */ + int + exe_typval_instr(typval_T *tv, typval_T *rettv) + { + ectx_T *ectx = tv->vval.v_instr->instr_ectx; + isn_T *save_instr = ectx->ec_instr; + int save_iidx = ectx->ec_iidx; + int res; + + ectx->ec_instr = tv->vval.v_instr->instr_instr; + res = exec_instructions(ectx); + if (res == OK) + { + *rettv = *STACK_TV_BOT(-1); + --ectx->ec_stack.ga_len; + } + + ectx->ec_instr = save_instr; + ectx->ec_iidx = save_iidx; + + return res; + } + + /* * Execute the instructions from an ISN_SUBSTITUTE command, which are in * "substitute_instr". */ *************** *** 4436,4441 **** --- 4486,4499 ---- } #endif break; + case ISN_INSTR: + { + smsg("%s%4d INSTR", pfx, current); + list_instructions(" ", iptr->isn_arg.instr, + INT_MAX, NULL); + msg(" -------------"); + } + break; case ISN_SUBSTITUTE: { subs_T *subs = &iptr->isn_arg.subs; *************** *** 5225,5230 **** --- 5283,5289 ---- case VAR_UNKNOWN: case VAR_ANY: case VAR_VOID: + case VAR_INSTR: break; } return FALSE; *** ../vim-8.2.2841/src/proto/vim9execute.pro 2021-04-19 16:48:44.431055514 +0200 --- src/proto/vim9execute.pro 2021-05-07 16:02:15.324738264 +0200 *************** *** 4,9 **** --- 4,10 ---- char_u *char_from_string(char_u *str, varnumber_T index); char_u *string_slice(char_u *str, varnumber_T first, varnumber_T last, int exclusive); int fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, ectx_T *ectx); + int exe_typval_instr(typval_T *tv, typval_T *rettv); char_u *exe_substitute_instr(void); int call_def_function(ufunc_T *ufunc, int argc_arg, typval_T *argv, partial_T *partial, typval_T *rettv); void ex_disassemble(exarg_T *eap); *** ../vim-8.2.2841/src/eval.c 2021-04-28 20:00:35.014355562 +0200 --- src/eval.c 2021-05-07 16:49:04.853854664 +0200 *************** *** 309,314 **** --- 309,318 ---- return FAIL; } } + else if (expr->v_type == VAR_INSTR) + { + return exe_typval_instr(expr, rettv); + } else { s = tv_get_string_buf_chk(expr, buf); *************** *** 1510,1515 **** --- 1514,1520 ---- case VAR_SPECIAL: case VAR_JOB: case VAR_CHANNEL: + case VAR_INSTR: break; case VAR_BLOB: *************** *** 4084,4089 **** --- 4089,4095 ---- case VAR_SPECIAL: case VAR_JOB: case VAR_CHANNEL: + case VAR_INSTR: if (verbose) emsg(_(e_cannot_index_special_variable)); return FAIL; *************** *** 4177,4182 **** --- 4183,4189 ---- case VAR_SPECIAL: case VAR_JOB: case VAR_CHANNEL: + case VAR_INSTR: break; // not evaluating, skipping over subscript case VAR_NUMBER: *************** *** 5067,5072 **** --- 5074,5084 ---- } break; + case VAR_INSTR: + *tofree = NULL; + r = (char_u *)"instructions"; + break; + case VAR_FLOAT: #ifdef FEAT_FLOAT *tofree = NULL; *************** *** 5987,5992 **** --- 5999,6005 ---- case VAR_SPECIAL: case VAR_JOB: case VAR_CHANNEL: + case VAR_INSTR: copy_tv(from, to); break; case VAR_LIST: *** ../vim-8.2.2841/src/evalfunc.c 2021-04-02 18:55:52.058322772 +0200 --- src/evalfunc.c 2021-05-07 16:50:29.513613937 +0200 *************** *** 2989,2994 **** --- 2989,2995 ---- case VAR_UNKNOWN: case VAR_ANY: case VAR_VOID: + case VAR_INSTR: internal_error_no_abort("f_empty(UNKNOWN)"); n = TRUE; break; *************** *** 6303,6308 **** --- 6304,6310 ---- case VAR_PARTIAL: case VAR_JOB: case VAR_CHANNEL: + case VAR_INSTR: emsg(_("E701: Invalid type for len()")); break; } *************** *** 10215,10220 **** --- 10217,10223 ---- case VAR_JOB: n = VAR_TYPE_JOB; break; case VAR_CHANNEL: n = VAR_TYPE_CHANNEL; break; case VAR_BLOB: n = VAR_TYPE_BLOB; break; + case VAR_INSTR: n = VAR_TYPE_INSTR; break; case VAR_UNKNOWN: case VAR_ANY: case VAR_VOID: *** ../vim-8.2.2841/src/vim.h 2021-04-26 21:14:12.713924760 +0200 --- src/vim.h 2021-05-07 16:50:40.873581896 +0200 *************** *** 2030,2035 **** --- 2030,2036 ---- #define VAR_TYPE_JOB 8 #define VAR_TYPE_CHANNEL 9 #define VAR_TYPE_BLOB 10 + #define VAR_TYPE_INSTR 11 #define DICT_MAXNEST 100 // maximum nesting of lists and dicts *** ../vim-8.2.2841/src/evalvars.c 2021-04-19 20:49:58.156857538 +0200 --- src/evalvars.c 2021-05-07 16:37:44.703664554 +0200 *************** *** 1912,1917 **** --- 1912,1918 ---- case VAR_SPECIAL: case VAR_JOB: case VAR_CHANNEL: + case VAR_INSTR: break; case VAR_BLOB: *** ../vim-8.2.2841/src/typval.c 2021-04-10 21:45:42.938892687 +0200 --- src/typval.c 2021-05-07 16:43:08.430865423 +0200 *************** *** 91,96 **** --- 91,97 ---- case VAR_VOID: case VAR_BOOL: case VAR_SPECIAL: + case VAR_INSTR: break; } vim_free(varp); *************** *** 153,158 **** --- 154,160 ---- case VAR_UNKNOWN: case VAR_ANY: case VAR_VOID: + case VAR_INSTR: break; } varp->v_lock = 0; *************** *** 236,241 **** --- 238,244 ---- case VAR_UNKNOWN: case VAR_ANY: case VAR_VOID: + case VAR_INSTR: internal_error_no_abort("tv_get_number(UNKNOWN)"); break; } *************** *** 333,338 **** --- 336,342 ---- case VAR_UNKNOWN: case VAR_ANY: case VAR_VOID: + case VAR_INSTR: internal_error_no_abort("tv_get_float(UNKNOWN)"); break; } *************** *** 514,519 **** --- 518,524 ---- case VAR_UNKNOWN: case VAR_ANY: case VAR_VOID: + case VAR_INSTR: emsg(_(e_inval_string)); break; } *************** *** 614,619 **** --- 619,628 ---- ++to->vval.v_channel->ch_refcount; break; #endif + case VAR_INSTR: + to->vval.v_instr = from->vval.v_instr; + break; + case VAR_STRING: case VAR_FUNC: if (from->vval.v_string == NULL) *************** *** 1116,1121 **** --- 1125,1132 ---- #ifdef FEAT_JOB_CHANNEL return tv1->vval.v_channel == tv2->vval.v_channel; #endif + case VAR_INSTR: + return tv1->vval.v_instr == tv2->vval.v_instr; case VAR_PARTIAL: return tv1->vval.v_partial == tv2->vval.v_partial; *** ../vim-8.2.2841/src/vim9type.c 2021-04-13 20:53:09.846201149 +0200 --- src/vim9type.c 2021-05-07 16:58:51.892245751 +0200 *************** *** 950,955 **** --- 950,956 ---- case VAR_BLOB: case VAR_JOB: case VAR_CHANNEL: + case VAR_INSTR: break; // not composite is always OK case VAR_LIST: case VAR_DICT: *************** *** 1097,1102 **** --- 1098,1104 ---- case VAR_CHANNEL: return "channel"; case VAR_LIST: return "list"; case VAR_DICT: return "dict"; + case VAR_INSTR: return "instr"; case VAR_FUNC: case VAR_PARTIAL: return "func"; *** ../vim-8.2.2841/src/testing.c 2021-04-02 18:55:52.058322772 +0200 --- src/testing.c 2021-05-07 16:51:01.009525276 +0200 *************** *** 1023,1028 **** --- 1023,1029 ---- case VAR_FLOAT: case VAR_SPECIAL: case VAR_STRING: + case VAR_INSTR: break; case VAR_JOB: #ifdef FEAT_JOB_CHANNEL *** ../vim-8.2.2841/src/viminfo.c 2021-02-10 19:22:12.132400451 +0100 --- src/viminfo.c 2021-05-07 16:59:07.928203253 +0200 *************** *** 1376,1381 **** --- 1376,1382 ---- case VAR_PARTIAL: case VAR_JOB: case VAR_CHANNEL: + case VAR_INSTR: continue; } fprintf(fp, "!%s\t%s\t", this_var->di_key, s); *** ../vim-8.2.2841/src/if_py_both.h 2021-02-21 19:12:43.018019657 +0100 --- src/if_py_both.h 2021-05-07 16:59:37.992123722 +0200 *************** *** 6425,6430 **** --- 6425,6431 ---- case VAR_VOID: case VAR_CHANNEL: case VAR_JOB: + case VAR_INSTR: Py_INCREF(Py_None); return Py_None; case VAR_BOOL: *** ../vim-8.2.2841/src/json.c 2021-02-08 21:53:05.592963320 +0100 --- src/json.c 2021-05-07 17:00:01.392061945 +0200 *************** *** 230,235 **** --- 230,236 ---- case VAR_PARTIAL: case VAR_JOB: case VAR_CHANNEL: + case VAR_INSTR: semsg(_(e_cannot_json_encode_str), vartype_name(val->v_type)); return FAIL; *** ../vim-8.2.2841/src/testdir/test_vim9_disassemble.vim 2021-05-05 21:31:36.068018579 +0200 --- src/testdir/test_vim9_disassemble.vim 2021-05-07 17:44:36.216903993 +0200 *************** *** 140,145 **** --- 140,174 ---- res) enddef + + def s:SearchPair() + var col = 8 + searchpair("{", "", "}", "", "col('.') > col") + enddef + + def Test_disassemble_seachpair() + var res = execute('disass s:SearchPair') + assert_match('\d*_SearchPair.*' .. + ' var col = 8\_s*' .. + '\d STORE 8 in $0\_s*' .. + ' searchpair("{", "", "}", "", "col(''.'') > col")\_s*' .. + '\d PUSHS "{"\_s*' .. + '\d PUSHS ""\_s*' .. + '\d PUSHS "}"\_s*' .. + '\d PUSHS ""\_s*' .. + '\d INSTR\_s*' .. + ' 0 PUSHS "."\_s*' .. + ' 1 BCALL col(argc 1)\_s*' .. + ' 2 LOAD $0\_s*' .. + ' 3 COMPARENR >\_s*' .. + ' -------------\_s*' .. + '\d BCALL searchpair(argc 5)\_s*' .. + '\d DROP\_s*' .. + '\d RETURN 0', + res) + enddef + + def s:RedirVar() var result: string redir =>> result *** ../vim-8.2.2841/src/testdir/test_vim9_builtin.vim 2021-05-03 21:40:05.011799095 +0200 --- src/testdir/test_vim9_builtin.vim 2021-05-07 17:52:35.119650127 +0200 *************** *** 974,979 **** --- 974,993 ---- bwipe! enddef + def Test_searchpair() + new + setline(1, "here { and } there") + normal f{ + var col = 15 + assert_equal(1, searchpair('{', '', '}', '', 'col(".") > col')) + assert_equal(12, col('.')) + col = 8 + normal 0f{ + assert_equal(0, searchpair('{', '', '}', '', 'col(".") > col')) + assert_equal(6, col('.')) + bwipe! + enddef + def Test_set_get_bufline() # similar to Test_setbufline_getbufline() var lines =<< trim END *** ../vim-8.2.2841/src/version.c 2021-05-07 15:00:14.197520296 +0200 --- src/version.c 2021-05-07 17:55:06.431598365 +0200 *************** *** 752,753 **** --- 752,755 ---- { /* Add new patch number below this line */ + /**/ + 2842, /**/ -- A man is incomplete until he's married ... and then he's finished! /// 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 ///