summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2021-04-19 16:48:48 +0200
committerBram Moolenaar <Bram@vim.org>2021-04-19 16:48:48 +0200
commit4c13721482d7786f92f5a56e43b0f5c499264b7e (patch)
tree2aae28c81b61721cb9ecc5d9aee66bb51d67e371
parente8209b91b9974da95899b51dba4058b411d04d5b (diff)
patch 8.2.2784: Vim9: cannot use \=expr in :substitutev8.2.2784
Problem: Vim9: cannot use \=expr in :substitute. Solution: Compile the expression into instructions and execute them when invoked.
-rw-r--r--src/ex_cmds.c36
-rw-r--r--src/globals.h3
-rw-r--r--src/proto/ex_cmds.pro1
-rw-r--r--src/proto/vim9execute.pro1
-rw-r--r--src/regexp.c3
-rw-r--r--src/testdir/test_vim9_cmd.vim22
-rw-r--r--src/testdir/test_vim9_disassemble.vim19
-rw-r--r--src/version.c2
-rw-r--r--src/vim9.h15
-rw-r--r--src/vim9compile.c92
-rw-r--r--src/vim9execute.c1507
11 files changed, 996 insertions, 705 deletions
diff --git a/src/ex_cmds.c b/src/ex_cmds.c
index 6cd54d20ce..96ff6ecba6 100644
--- a/src/ex_cmds.c
+++ b/src/ex_cmds.c
@@ -3604,6 +3604,29 @@ typedef struct {
} subflags_T;
/*
+ * Skip over the "sub" part in :s/pat/sub/ where "delimiter" is the separating
+ * character.
+ */
+ char_u *
+skip_substitute(char_u *start, int delimiter)
+{
+ char_u *p = start;
+
+ while (p[0])
+ {
+ if (p[0] == delimiter) // end delimiter found
+ {
+ *p++ = NUL; // replace it with a NUL
+ break;
+ }
+ if (p[0] == '\\' && p[1] != 0) // skip escaped characters
+ ++p;
+ MB_PTR_ADV(p);
+ }
+ return p;
+}
+
+/*
* Perform a substitution from line eap->line1 to line eap->line2 using the
* command pointed to by eap->arg which should be of the form:
*
@@ -3704,18 +3727,7 @@ ex_substitute(exarg_T *eap)
* Vim we want to use '\n' to find/substitute a NUL.
*/
sub = cmd; // remember the start of the substitution
-
- while (cmd[0])
- {
- if (cmd[0] == delimiter) // end delimiter found
- {
- *cmd++ = NUL; // replace it with a NUL
- break;
- }
- if (cmd[0] == '\\' && cmd[1] != 0) // skip escaped characters
- ++cmd;
- MB_PTR_ADV(cmd);
- }
+ cmd = skip_substitute(cmd, delimiter);
if (!eap->skip)
{
diff --git a/src/globals.h b/src/globals.h
index 51a69465c7..017c059a48 100644
--- a/src/globals.h
+++ b/src/globals.h
@@ -1379,6 +1379,9 @@ EXTERN char_u no_lines_msg[] INIT(= N_("--No lines in buffer--"));
EXTERN long sub_nsubs; // total number of substitutions
EXTERN linenr_T sub_nlines; // total number of lines changed
+// Used when a compiled :substitute has an expression.
+EXTERN struct subs_expr_S *substitute_instr INIT(= NULL);
+
// table to store parsed 'wildmode'
EXTERN char_u wim_flags[4];
diff --git a/src/proto/ex_cmds.pro b/src/proto/ex_cmds.pro
index 9036dd205b..1711af4537 100644
--- a/src/proto/ex_cmds.pro
+++ b/src/proto/ex_cmds.pro
@@ -27,6 +27,7 @@ void ex_change(exarg_T *eap);
void ex_z(exarg_T *eap);
int check_restricted(void);
int check_secure(void);
+char_u *skip_substitute(char_u *start, int delimiter);
void ex_substitute(exarg_T *eap);
int do_sub_msg(int count_only);
void ex_global(exarg_T *eap);
diff --git a/src/proto/vim9execute.pro b/src/proto/vim9execute.pro
index 212428219d..46b314c8b1 100644
--- a/src/proto/vim9execute.pro
+++ b/src/proto/vim9execute.pro
@@ -4,6 +4,7 @@ void funcstack_check_refcount(funcstack_T *funcstack);
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);
+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);
int tv2bool(typval_T *tv);
diff --git a/src/regexp.c b/src/regexp.c
index 9d2d441fc4..e372dd44dc 100644
--- a/src/regexp.c
+++ b/src/regexp.c
@@ -2069,6 +2069,9 @@ vim_regsub_both(
}
clear_tv(&rettv);
}
+ else if (substitute_instr != NULL)
+ // Execute instructions from ISN_SUBSTITUTE.
+ eval_result = exe_substitute_instr();
else
eval_result = eval_to_string(source + 2, TRUE);
diff --git a/src/testdir/test_vim9_cmd.vim b/src/testdir/test_vim9_cmd.vim
index 3b479f31d5..15c9a59870 100644
--- a/src/testdir/test_vim9_cmd.vim
+++ b/src/testdir/test_vim9_cmd.vim
@@ -1172,5 +1172,27 @@ def Test_lockvar()
CheckDefFailure(lines, 'E1178', 2)
enddef
+def Test_substitute_expr()
+ var to = 'repl'
+ new
+ setline(1, 'one from two')
+ s/from/\=to
+ assert_equal('one repl two', getline(1))
+
+ setline(1, 'one from two')
+ s/from/\=to .. '_x'
+ assert_equal('one repl_x two', getline(1))
+
+ setline(1, 'one from two from three')
+ var also = 'also'
+ s/from/\=to .. '_' .. also/g#e
+ assert_equal('one repl_also two repl_also three', getline(1))
+
+ CheckDefFailure(['s/from/\="x")/'], 'E488:')
+ CheckDefFailure(['s/from/\="x"/9'], 'E488:')
+
+ bwipe!
+enddef
+
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim
index e6b3751452..8a96bf2732 100644
--- a/src/testdir/test_vim9_disassemble.vim
+++ b/src/testdir/test_vim9_disassemble.vim
@@ -121,6 +121,25 @@ def Test_disassemble_exec_expr()
res)
enddef
+def s:Substitute()
+ var expr = "abc"
+ :%s/a/\=expr/&g#c
+enddef
+
+def Test_disassemble_substitute()
+ var res = execute('disass s:Substitute')
+ assert_match('<SNR>\d*_Substitute.*' ..
+ ' var expr = "abc"\_s*' ..
+ '\d PUSHS "abc"\_s*' ..
+ '\d STORE $0\_s*' ..
+ ' :%s/a/\\=expr/&g#c\_s*' ..
+ '\d SUBSTITUTE :%s/a/\\=expr/&g#c\_s*' ..
+ ' 0 LOAD $0\_s*' ..
+ ' -------------\_s*' ..
+ '\d RETURN 0',
+ res)
+enddef
+
def s:YankRange()
norm! m[jjm]
:'[,']yank
diff --git a/src/version.c b/src/version.c
index 9a2da2fbc2..a43a2f432d 100644
--- a/src/version.c
+++ b/src/version.c
@@ -751,6 +751,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 2784,
+/**/
2783,
/**/
2782,
diff --git a/src/vim9.h b/src/vim9.h
index 4a10e12b1c..52b7c6dbff 100644
--- a/src/vim9.h
+++ b/src/vim9.h
@@ -19,6 +19,7 @@ typedef enum {
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
+ ISN_SUBSTITUTE, // :s command with expression
// get and set variables
ISN_LOAD, // push local variable isn_arg.number
@@ -94,7 +95,8 @@ typedef enum {
// expression operations
ISN_JUMP, // jump if condition is matched isn_arg.jump
- ISN_JUMP_IF_ARG_SET, // jump if argument is already set, uses isn_arg.jumparg
+ ISN_JUMP_IF_ARG_SET, // jump if argument is already set, uses
+ // isn_arg.jumparg
// loop
ISN_FOR, // get next item from a list, uses isn_arg.forloop
@@ -165,7 +167,9 @@ typedef enum {
ISN_UNPACK, // unpack list into items, uses isn_arg.unpack
ISN_SHUFFLE, // move item on stack up or down
- ISN_DROP // pop stack and discard value
+ ISN_DROP, // pop stack and discard value
+
+ ISN_FINISH // end marker in list of instructions
} isntype_T;
@@ -339,6 +343,12 @@ typedef struct {
int outer_depth; // nesting level, stack frames to go up
} isn_outer_T;
+// arguments to ISN_SUBSTITUTE
+typedef struct {
+ char_u *subs_cmd; // :s command
+ isn_T *subs_instr; // sequence of instructions
+} subs_T;
+
/*
* Instruction
*/
@@ -381,6 +391,7 @@ struct isn_S {
cmod_T cmdmod;
unpack_T unpack;
isn_outer_T outer;
+ subs_T subs;
} isn_arg;
};
diff --git a/src/vim9compile.c b/src/vim9compile.c
index 961050f985..71a8831623 100644
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -2130,6 +2130,33 @@ generate_EXECCONCAT(cctx_T *cctx, int count)
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.
*/
@@ -8466,6 +8493,55 @@ theend:
}
/*
+ * :s/pat/repl/
+ */
+ static char_u *
+compile_substitute(char_u *arg, exarg_T *eap, cctx_T *cctx)
+{
+ char_u *cmd = eap->arg;
+ char_u *expr = (char_u *)strstr((char *)cmd, "\\=");
+
+ if (expr != NULL)
+ {
+ 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 == '&')
+ ++end;
+ while (ASCII_ISALPHA(*end) || *end == '#')
+ ++end;
+ return end;
+ }
+ }
+
+ return compile_exec(arg, eap, cctx);
+}
+
+/*
* Add a function to the list of :def functions.
* This sets "ufunc->uf_dfunc_idx" but the function isn't compiled yet.
*/
@@ -8996,6 +9072,16 @@ compile_def_function(
line = compile_put(p, &ea, &cctx);
break;
+ case CMD_substitute:
+ if (cctx.ctx_skip == SKIP_YES)
+ line = (char_u *)"";
+ else
+ {
+ ea.arg = p;
+ line = compile_substitute(line, &ea, &cctx);
+ }
+ break;
+
// TODO: any other commands with an expression argument?
case CMD_append:
@@ -9223,6 +9309,11 @@ delete_instr(isn_T *isn)
vim_free(isn->isn_arg.string);
break;
+ case ISN_SUBSTITUTE:
+ vim_free(isn->isn_arg.subs.subs_cmd);
+ vim_free(isn->isn_arg.subs.subs_instr);
+ break;
+
case ISN_LOADS:
case ISN_STORES:
vim_free(isn->isn_arg.loadstore.ls_name);
@@ -9400,6 +9491,7 @@ delete_instr(isn_T *isn)
case ISN_UNLETINDEX:
case ISN_UNLETRANGE:
case ISN_UNPACK:
+ case ISN_FINISH:
// nothing allocated
break;
}
diff --git a/src/vim9execute.c b/src/vim9execute.c
index 60b58f98a3..6a11e8a6bc 100644
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -34,6 +34,14 @@ typedef struct {
int tcd_return; // when TRUE return from end of :finally
} trycmd_T;
+// Data local to a function.
+// On a function call, if not empty, is saved on the stack and restored when
+// returning.
+typedef struct {
+ int floc_restore_cmdmod;
+ cmdmod_T floc_save_cmdmod;
+ int floc_restore_cmdmod_stacklen;
+} funclocal_T;
// A stack is used to store:
// - arguments passed to a :def function
@@ -60,8 +68,10 @@ typedef struct {
struct ectx_S {
garray_T ec_stack; // stack of typval_T values
int ec_frame_idx; // index in ec_stack: context of ec_dfunc_idx
+ int ec_initial_frame_idx; // frame index when called
outer_T *ec_outer; // outer scope used for closures, allocated
+ funclocal_T ec_funclocal;
garray_T ec_trystack; // stack of trycmd_T values
int ec_in_catch; // when TRUE in catch or finally block
@@ -71,6 +81,10 @@ struct ectx_S {
int ec_iidx; // index in ec_instr: instruction to execute
garray_T ec_funcrefs; // partials that might be a closure
+
+ int ec_did_emsg_before;
+ int ec_trylevel_at_start;
+ where_T ec_where;
};
#ifdef FEAT_PROFILE
@@ -125,15 +139,6 @@ exe_newlist(int count, ectx_T *ectx)
return OK;
}
-// Data local to a function.
-// On a function call, if not empty, is saved on the stack and restored when
-// returning.
-typedef struct {
- int floc_restore_cmdmod;
- cmdmod_T floc_save_cmdmod;
- int floc_restore_cmdmod_stacklen;
-} funclocal_T;
-
/*
* Call compiled function "cdf_idx" from compiled code.
* This adds a stack frame and sets the instruction pointer to the start of the
@@ -154,7 +159,6 @@ call_dfunc(
int cdf_idx,
partial_T *pt,
int argcount_arg,
- funclocal_T *funclocal,
ectx_T *ectx)
{
int argcount = argcount_arg;
@@ -254,13 +258,13 @@ call_dfunc(
return FAIL;
// Only make a copy of funclocal if it contains something to restore.
- if (funclocal->floc_restore_cmdmod)
+ if (ectx->ec_funclocal.floc_restore_cmdmod)
{
floc = ALLOC_ONE(funclocal_T);
if (floc == NULL)
return FAIL;
- *floc = *funclocal;
- funclocal->floc_restore_cmdmod = FALSE;
+ *floc = ectx->ec_funclocal;
+ ectx->ec_funclocal.floc_restore_cmdmod = FALSE;
}
// Move the vararg-list to below the missing optional arguments.
@@ -527,7 +531,7 @@ funcstack_check_refcount(funcstack_T *funcstack)
* Return from the current function.
*/
static int
-func_return(funclocal_T *funclocal, ectx_T *ectx)
+func_return(ectx_T *ectx)
{
int idx;
int ret_idx;
@@ -598,10 +602,10 @@ func_return(funclocal_T *funclocal, ectx_T *ectx)
ectx->ec_instr = INSTRUCTIONS(prev_dfunc);
if (floc == NULL)
- funclocal->floc_restore_cmdmod = FALSE;
+ ectx->ec_funclocal.floc_restore_cmdmod = FALSE;
else
{
- *funclocal = *floc;
+ ectx->ec_funclocal = *floc;
vim_free(floc);
}
@@ -698,7 +702,6 @@ call_ufunc(
ufunc_T *ufunc,
partial_T *pt,
int argcount,
- funclocal_T *funclocal,
ectx_T *ectx,
isn_T *iptr)
{
@@ -738,7 +741,7 @@ call_ufunc(
iptr->isn_arg.dfunc.cdf_idx = ufunc->uf_dfunc_idx;
iptr->isn_arg.dfunc.cdf_argcount = argcount;
}
- return call_dfunc(ufunc->uf_dfunc_idx, pt, argcount, funclocal, ectx);
+ return call_dfunc(ufunc->uf_dfunc_idx, pt, argcount, ectx);
}
if (call_prepare(argcount, argvars, ectx) == FAIL)
@@ -800,7 +803,6 @@ vim9_aborting(int prev_called_emsg)
call_by_name(
char_u *name,
int argcount,
- funclocal_T *funclocal,
ectx_T *ectx,
isn_T *iptr)
{
@@ -853,7 +855,7 @@ call_by_name(
}
}
- return call_ufunc(ufunc, NULL, argcount, funclocal, ectx, iptr);
+ return call_ufunc(ufunc, NULL, argcount, ectx, iptr);
}
return FAIL;
@@ -863,7 +865,6 @@ call_by_name(
call_partial(
typval_T *tv,
int argcount_arg,
- funclocal_T *funclocal,
ectx_T *ectx)
{
int argcount = argcount_arg;
@@ -893,7 +894,7 @@ call_partial(
}
if (pt->pt_func != NULL)
- return call_ufunc(pt->pt_func, pt, argcount, funclocal, ectx, NULL);
+ return call_ufunc(pt->pt_func, pt, argcount, ectx, NULL);
name = pt->pt_name;
}
@@ -911,7 +912,7 @@ call_partial(
if (error != FCERR_NONE)
res = FAIL;
else
- res = call_by_name(fname, argcount, funclocal, ectx, NULL);
+ res = call_by_name(fname, argcount, ectx, NULL);
vim_free(tofree);
}
@@ -1184,14 +1185,13 @@ get_script_svar(scriptref_T *sref, ectx_T *ectx)
call_eval_func(
char_u *name,
int argcount,
- funclocal_T *funclocal,
ectx_T *ectx,
isn_T *iptr)
{
int called_emsg_before = called_emsg;
int res;
- res = call_by_name(name, argcount, funclocal, ectx, iptr);
+ res = call_by_name(name, argcount, ectx, iptr);
if (res == FAIL && called_emsg == called_emsg_before)
{
dictitem_T *v;
@@ -1207,7 +1207,7 @@ call_eval_func(
semsg(_(e_unknownfunc), name);
return FAIL;
}
- return call_partial(&v->di_tv, argcount, funclocal, ectx);
+ return call_partial(&v->di_tv, argcount, ectx);
}
return res;
}
@@ -1257,255 +1257,35 @@ fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, ectx_T *ectx)
return OK;
}
-
-/*
- * Call a "def" function from old Vim script.
- * Return OK or FAIL.
- */
- int
-call_def_function(
- ufunc_T *ufunc,
- int argc_arg, // nr of arguments
- typval_T *argv, // arguments
- partial_T *partial, // optional partial for context
- typval_T *rettv) // return value
-{
- ectx_T ectx; // execution context
- int argc = argc_arg;
- int initial_frame_idx;
- typval_T *tv;
- int idx;
- int ret = FAIL;
- int defcount = ufunc->uf_args.ga_len - argc;
- sctx_T save_current_sctx = current_sctx;
- int breakcheck_count = 0;
- int did_emsg_before = did_emsg_cumul + did_emsg;
- int save_suppress_errthrow = suppress_errthrow;
- msglist_T **saved_msg_list = NULL;
- msglist_T *private_msg_list = NULL;
- funclocal_T funclocal;
- int save_emsg_silent_def = emsg_silent_def;
- int save_did_emsg_def = did_emsg_def;
- int trylevel_at_start = trylevel;
- int orig_funcdepth;
- where_T where;
+// used for substitute_instr
+typedef struct subs_expr_S {
+ ectx_T *subs_ectx;
+ isn_T *subs_instr;
+ int subs_status;
+} subs_expr_T;
// Get pointer to item in the stack.
-#define STACK_TV(idx) (((typval_T *)ectx.ec_stack.ga_data) + idx)
+#define STACK_TV(idx) (((typval_T *)ectx->ec_stack.ga_data) + idx)
// Get pointer to item at the bottom of the stack, -1 is the bottom.
#undef STACK_TV_BOT
-#define STACK_TV_BOT(idx) (((typval_T *)ectx.ec_stack.ga_data) + ectx.ec_stack.ga_len + idx)
+#define STACK_TV_BOT(idx) (((typval_T *)ectx->ec_stack.ga_data) + ectx->ec_stack.ga_len + idx)
// Get pointer to a local variable on the stack. Negative for arguments.
-#define STACK_TV_VAR(idx) (((typval_T *)ectx.ec_stack.ga_data) + ectx.ec_frame_idx + STACK_FRAME_SIZE + idx)
-
- if (ufunc->uf_def_status == UF_NOT_COMPILED
- || ufunc->uf_def_status == UF_COMPILE_ERROR
- || (func_needs_compiling(ufunc, PROFILING(ufunc))
- && compile_def_function(ufunc, FALSE, PROFILING(ufunc), NULL)
- == FAIL))
- {
- if (did_emsg_cumul + did_emsg == did_emsg_before)
- semsg(_(e_function_is_not_compiled_str),
- printable_func_name(ufunc));
- return FAIL;
- }
-
- {
- // Check the function was really compiled.
- dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
- + ufunc->uf_dfunc_idx;
- if (INSTRUCTIONS(dfunc) == NULL)
- {
- iemsg("using call_def_function() on not compiled function");
- return FAIL;
- }
- }
-
- // If depth of calling is getting too high, don't execute the function.
- orig_funcdepth = funcdepth_get();
- if (funcdepth_increment() == FAIL)
- return FAIL;
+#define STACK_TV_VAR(idx) (((typval_T *)ectx->ec_stack.ga_data) + ectx->ec_frame_idx + STACK_FRAME_SIZE + idx)
- CLEAR_FIELD(funclocal);
- CLEAR_FIELD(ectx);
- ectx.ec_dfunc_idx = ufunc->uf_dfunc_idx;
- ga_init2(&ectx.ec_stack, sizeof(typval_T), 500);
- if (ga_grow(&ectx.ec_stack, 20) == FAIL)
- {
- funcdepth_decrement();
- return FAIL;
- }
- ga_init2(&ectx.ec_trystack, sizeof(trycmd_T), 10);
- ga_init2(&ectx.ec_funcrefs, sizeof(partial_T *), 10);
-
- idx = argc - ufunc->uf_args.ga_len;
- if (idx > 0 && ufunc->uf_va_name == NULL)
- {
- if (idx == 1)
- emsg(_(e_one_argument_too_many));
- else
- semsg(_(e_nr_arguments_too_many), idx);
- goto failed_early;
- }
-
- // Put arguments on the stack, but no more than what the function expects.
- // A lambda can be called with more arguments than it uses.
- for (idx = 0; idx < argc
- && (ufunc->uf_va_name != NULL || idx < ufunc->uf_args.ga_len);
- ++idx)
- {
- if (idx >= ufunc->uf_args.ga_len - ufunc->uf_def_args.ga_len
- && argv[idx].v_type == VAR_SPECIAL
- && argv[idx].vval.v_number == VVAL_NONE)
- {
- // Use the default value.
- STACK_TV_BOT(0)->v_type = VAR_UNKNOWN;
- }
- else
- {
- if (ufunc->uf_arg_types != NULL && idx < ufunc->uf_args.ga_len
- && check_typval_arg_type(
- ufunc->uf_arg_types[idx], &argv[idx], idx + 1) == FAIL)
- goto failed_early;
- copy_tv(&argv[idx], STACK_TV_BOT(0));
- }
- ++ectx.ec_stack.ga_len;
- }
-
- // Turn varargs into a list. Empty list if no args.
- if (ufunc->uf_va_name != NULL)
- {
- int vararg_count = argc - ufunc->uf_args.ga_len;
-
- if (vararg_count < 0)
- vararg_count = 0;
- else
- argc -= vararg_count;
- if (exe_newlist(vararg_count, &ectx) == FAIL)
- goto failed_early;
-
- // Check the type of the list items.
- tv = STACK_TV_BOT(-1);
- if (ufunc->uf_va_type != NULL
- && ufunc->uf_va_type != &t_list_any
- && ufunc->uf_va_type->tt_member != &t_any
- && tv->vval.v_list != NULL)
- {
- type_T *expected = ufunc->uf_va_type->tt_member;
- listitem_T *li = tv->vval.v_list->lv_first;
-
- for (idx = 0; idx < vararg_count; ++idx)
- {
- if (check_typval_arg_type(expected, &li->li_tv,
- argc + idx + 1) == FAIL)
- goto failed_early;
- li = li->li_next;
- }
- }
-
- if (defcount > 0)
- // Move varargs list to below missing default arguments.
- *STACK_TV_BOT(defcount - 1) = *STACK_TV_BOT(-1);
- --ectx.ec_stack.ga_len;
- }
-
- // Make space for omitted arguments, will store default value below.
- // Any varargs list goes after them.
- if (defcount > 0)
- for (idx = 0; idx < defcount; ++idx)
- {
- STACK_TV_BOT(0)->v_type = VAR_UNKNOWN;
- ++ectx.ec_stack.ga_len;
- }
- if (ufunc->uf_va_name != NULL)
- ++ectx.ec_stack.ga_len;
-
- // Frame pointer points to just after arguments.
- ectx.ec_frame_idx = ectx.ec_stack.ga_len;
- initial_frame_idx = ectx.ec_frame_idx;
-
- {
- dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
- + ufunc->uf_dfunc_idx;
- ufunc_T *base_ufunc = dfunc->df_ufunc;
-
- // "uf_partial" is on the ufunc that "df_ufunc" points to, as is done
- // by copy_func().
- if (partial != NULL || base_ufunc->uf_partial != NULL)
- {
- ectx.ec_outer = ALLOC_CLEAR_ONE(outer_T);
- if (ectx.ec_outer == NULL)
- goto failed_early;
- if (partial != NULL)
- {
- if (partial->pt_outer.out_stack == NULL && current_ectx != NULL)
- {
- if (current_ectx->ec_outer != NULL)
- *ectx.ec_outer = *current_ectx->ec_outer;
- }
- else
- *ectx.ec_outer = partial->pt_outer;
- }
- else
- *ectx.ec_outer = base_ufunc->uf_partial->pt_outer;
- ectx.ec_outer->out_up_is_copy = TRUE;
- }
- }
-
- // dummy frame entries
- for (idx = 0; idx < STACK_FRAME_SIZE; ++idx)
- {
- STACK_TV(ectx.ec_stack.ga_len)->v_type = VAR_UNKNOWN;
- ++ectx.ec_stack.ga_len;
- }
-
- {
- // Reserve space for local variables and any closure reference count.
- dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
- + ufunc->uf_dfunc_idx;
-
- for (idx = 0; idx < dfunc->df_varcount; ++idx)
- STACK_TV_VAR(idx)->v_type = VAR_UNKNOWN;
- ectx.ec_stack.ga_len += dfunc->df_varcount;
- if (dfunc->df_has_closure)
- {
- STACK_TV_VAR(idx)->v_type = VAR_NUMBER;
- STACK_TV_VAR(idx)->vval.v_number = 0;
- ++ectx.ec_stack.ga_len;
- }
-
- ectx.ec_instr = INSTRUCTIONS(dfunc);
- }
-
- // Following errors are in the function, not the caller.
- // Commands behave like vim9script.
- estack_push_ufunc(ufunc, 1);
- current_sctx = ufunc->uf_script_ctx;
- current_sctx.sc_version = SCRIPT_VERSION_VIM9;
-
- // Use a specific location for storing error messages to be converted to an
- // exception.
- saved_msg_list = msg_list;
- msg_list = &private_msg_list;
-
- // Do turn errors into exceptions.
- suppress_errthrow = FALSE;
-
- // Do not delete the function while executing it.
- ++ufunc->uf_calls;
-
- // When ":silent!" was used before calling then we still abort the
- // function. If ":silent!" is used in the function then we don't.
- emsg_silent_def = emsg_silent;
- did_emsg_def = 0;
-
- where.wt_index = 0;
- where.wt_variable = FALSE;
+/*
+ * Execute instructions in execution context "ectx".
+ * Return OK or FAIL;
+ */
+ static int
+exec_instructions(ectx_T *ectx)
+{
+ int breakcheck_count = 0;
+ typval_T *tv;
// Start execution at the first instruction.
- ectx.ec_iidx = 0;
+ ectx->ec_iidx = 0;
for (;;)
{
@@ -1521,7 +1301,7 @@ call_def_function(
// Turn CTRL-C into an exception.
got_int = FALSE;
if (throw_exception("Vim:Interrupt", ET_INTERRUPT, NULL) == FAIL)
- goto failed;
+ return FAIL;
did_throw = TRUE;
}
@@ -1530,52 +1310,52 @@ call_def_function(
// Turn an error message into an exception.
did_emsg = FALSE;
if (throw_exception(*msg_list, ET_ERROR, NULL) == FAIL)
- goto failed;
+ return FAIL;
did_throw = TRUE;
*msg_list = NULL;
}
- if (did_throw && !ectx.ec_in_catch)
+ if (did_throw && !ectx->ec_in_catch)
{
- garray_T *trystack = &ectx.ec_trystack;
+ garray_T *trystack = &ectx->ec_trystack;
trycmd_T *trycmd = NULL;
// An exception jumps to the first catch, finally, or returns from
// the current function.
if (trystack->ga_len > 0)
trycmd = ((trycmd_T *)trystack->ga_data) + trystack->ga_len - 1;
- if (trycmd != NULL && trycmd->tcd_frame_idx == ectx.ec_frame_idx)
+ if (trycmd != NULL && trycmd->tcd_frame_idx == ectx->ec_frame_idx)
{
// jump to ":catch" or ":finally"
- ectx.ec_in_catch = TRUE;
- ectx.ec_iidx = trycmd->tcd_catch_idx;
+ ectx->ec_in_catch = TRUE;
+ ectx->ec_iidx = trycmd->tcd_catch_idx;
}
else
{
// Not inside try or need to return from current functions.
// Push a dummy return value.
- if (GA_GROW(&ectx.ec_stack, 1) == FAIL)
- goto failed;
+ if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
+ return FAIL;
tv = STACK_TV_BOT(0);
tv->v_type = VAR_NUMBER;
tv->vval.v_number = 0;
- ++ectx.ec_stack.ga_len;
- if (ectx.ec_frame_idx == initial_frame_idx)
+ ++ectx->ec_stack.ga_len;
+ if (ectx->ec_frame_idx == ectx->ec_initial_frame_idx)
{
// At the toplevel we are done.
need_rethrow = TRUE;
- if (handle_closure_in_use(&ectx, FALSE) == FAIL)
- goto failed;
+ if (handle_closure_in_use(ectx, FALSE) == FAIL)
+ return FAIL;
goto done;
}
- if (func_return(&funclocal, &ectx) == FAIL)
- goto failed;
+ if (func_return(ectx) == FAIL)
+ return FAIL;
}
continue;
}
- iptr = &ectx.ec_instr[ectx.ec_iidx++];
+ iptr = &ectx->ec_instr[ectx->ec_iidx++];
switch (iptr->isn_type)
{
// execute Ex command line
@@ -1597,6 +1377,38 @@ call_def_function(
}
break;
+ // execute :substitute with an expression
+ case ISN_SUBSTITUTE:
+ {
+ subs_T *subs = &iptr->isn_arg.subs;
+ source_cookie_T cookie;
+ struct subs_expr_S *save_instr = substitute_instr;
+ struct subs_expr_S subs_instr;
+ int res;
+
+ subs_instr.subs_ectx = ectx;
+ subs_instr.subs_instr = subs->subs_instr;
+ subs_instr.subs_status = OK;
+ substitute_instr = &subs_instr;
+
+ SOURCING_LNUM = iptr->isn_lnum;
+ // This is very much like ISN_EXEC
+ CLEAR_FIELD(cookie);
+ cookie.sourcing_lnum = iptr->isn_lnum - 1;
+ res = do_cmdline(subs->subs_cmd,
+ getsourceline, &cookie,
+ DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_KEYTYPED);
+ substitute_instr = save_instr;
+
+ if (res == FAIL || did_emsg
+ || subs_instr.subs_status == FAIL)
+ goto on_error;
+ }
+ break;
+
+ case ISN_FINISH:
+ goto done;
+
// execute Ex command from pieces on the stack
case ISN_EXECCONCAT:
{
@@ -1626,7 +1438,7 @@ call_def_function(
{
cmd = alloc(len + 1);
if (cmd == NULL)
- goto failed;
+ return FAIL;
len = 0;
}
}
@@ -1643,6 +1455,7 @@ call_def_function(
int count = iptr->isn_arg.echo.echo_count;
int atstart = TRUE;
int needclr = TRUE;
+ int idx;
for (idx = 0; idx < count; ++idx)
{
@@ -1653,7 +1466,7 @@ call_def_function(
}
if (needclr)
msg_clr_eos();
- ectx.ec_stack.ga_len -= count;
+ ectx->ec_stack.ga_len -= count;
}
break;
@@ -1670,6 +1483,7 @@ call_def_function(
char_u *p;
int len;
int failed = FALSE;
+ int idx;
ga_init2(&ga, 1, 80);
for (idx = 0; idx < count; ++idx)
@@ -1702,7 +1516,7 @@ call_def_function(
}
clear_tv(tv);
}
- ectx.ec_stack.ga_len -= count;
+ ectx->ec_stack.ga_len -= count;
if (failed)
{
ga_clear(&ga);
@@ -1742,18 +1556,18 @@ call_def_function(
// load local variable or argument
case ISN_LOAD:
- if (GA_GROW(&ectx.ec_stack, 1) == FAIL)
- goto failed;
+ if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
+ return FAIL;
copy_tv(STACK_TV_VAR(iptr->isn_arg.number), STACK_TV_BOT(0));
- ++ectx.ec_stack.ga_len;
+ ++ectx->ec_stack.ga_len;
break;
// load v: variable
case ISN_LOADV:
- if (GA_GROW(&ectx.ec_stack, 1) == FAIL)
- goto failed;
+ if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
+ return FAIL;
copy_tv(get_vim_var_tv(iptr->isn_arg.number), STACK_TV_BOT(0));
- ++ectx.ec_stack.ga_len;
+ ++ectx->ec_stack.ga_len;
break;
// load s: variable in Vim9 script
@@ -1762,14 +1576,14 @@ call_def_function(
scriptref_T *sref = iptr->isn_arg.script.scriptref;
svar_T *sv;
- sv = get_script_svar(sref, &ectx);
+ sv = get_script_svar(sref, ectx);
if (sv == NULL)
- goto failed;
+ return FAIL;
allocate_if_null(sv->sv_tv);
- if (GA_GROW(&ectx.ec_stack, 1) == FAIL)
- goto failed;
+ if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
+ return FAIL;
copy_tv(sv->sv_tv, STACK_TV_BOT(0));
- ++ectx.ec_stack.ga_len;
+ ++ectx->ec_stack.ga_len;
}
break;
@@ -1789,10 +1603,10 @@ call_def_function(
}
else
{
- if (GA_GROW(&ectx.ec_stack, 1) == FAIL)
- goto failed;
+ if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
+ return FAIL;
copy_tv(&di->di_tv, STACK_TV_BOT(0));
- ++ectx.ec_stack.ga_len;
+ ++ectx->ec_stack.ga_len;
}
}
break;
@@ -1826,7 +1640,7 @@ call_def_function(
namespace = 't';
break;
default: // Cannot reach here
- goto failed;
+ return FAIL;
}
di = find_var_in_ht(ht, 0, iptr->isn_arg.string, TRUE);
@@ -1839,10 +1653,10 @@ call_def_function(
}
else
{
- if (GA_GROW(&ectx.ec_stack, 1) == FAIL)
- goto fa