summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2020-02-06 17:51:35 +0100
committerBram Moolenaar <Bram@vim.org>2020-02-06 17:51:35 +0100
commit170fcfcf250954d76fca86e3fed088ddfdb49383 (patch)
tree8bb4fddeecab3a9e4e5f8c0e6917977f72ffaf32
parent6e587dcbf319ea898ef50f7e367c46586a6f408a (diff)
downloadvim-git-170fcfcf250954d76fca86e3fed088ddfdb49383.tar.gz
patch 8.2.0222: Vim9: optional function arguments don't work yetv8.2.0222
Problem: Vim9: optional function arguments don't work yet. Solution: Implement optional function arguments.
-rw-r--r--src/structs.h2
-rw-r--r--src/testdir/test_vim9_script.vim48
-rw-r--r--src/userfunc.c1
-rw-r--r--src/version.c2
-rw-r--r--src/vim9compile.c51
-rw-r--r--src/vim9execute.c97
6 files changed, 152 insertions, 49 deletions
diff --git a/src/structs.h b/src/structs.h
index bb3e72f63..7aff8af20 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -1515,6 +1515,8 @@ typedef struct
type_T **uf_arg_types; // argument types (count == uf_args.ga_len)
type_T *uf_ret_type; // return type
garray_T uf_type_list; // types used in arg and return types
+ int *uf_def_arg_idx; // instruction indexes for evaluating
+ // uf_def_args; length: uf_def_args.ga_len + 1
char_u *uf_va_name; // name from "...name" or NULL
type_T *uf_va_type; // type from "...name: type" or NULL
diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim
index 10bbf9168..d7b3b4c99 100644
--- a/src/testdir/test_vim9_script.vim
+++ b/src/testdir/test_vim9_script.vim
@@ -139,34 +139,48 @@ def Test_call_varargs()
assert_equal('one,two,three', MyVarargs('one', 'two', 'three'))
enddef
-"def Test_call_func_defined_later()
-" call assert_equal('one', DefineLater('one'))
-" call assert_fails('call NotDefined("one")', 'E99:')
-"enddef
-
-func DefineLater(arg)
- return a:arg
-endfunc
-
def MyDefaultArgs(name = 'string'): string
return name
enddef
+def Test_call_default_args()
+ assert_equal('string', MyDefaultArgs())
+ assert_equal('one', MyDefaultArgs('one'))
+ assert_fails('call MyDefaultArgs("one", "two")', 'E118:')
+enddef
+
func Test_call_default_args_from_func()
- " TODO: implement using default value for optional argument
- "call assert_equal('string', MyDefaultArgs())
- call assert_fails('call MyDefaultArgs()', 'optional arguments not implemented yet')
+ call assert_equal('string', MyDefaultArgs())
call assert_equal('one', MyDefaultArgs('one'))
call assert_fails('call MyDefaultArgs("one", "two")', 'E118:')
endfunc
-def Test_call_default_args()
- " TODO: implement using default value for optional argument
- "assert_equal('string', MyDefaultArgs())
- assert_equal('one', MyDefaultArgs('one'))
- assert_fails('call MyDefaultArgs("one", "two")', 'E118:')
+" Default arg and varargs
+def MyDefVarargs(one: string, two = 'foo', ...rest: list<string>): string
+ let res = one .. ',' .. two
+ for s in rest
+ res ..= ',' .. s
+ endfor
+ return res
enddef
+def Test_call_def_varargs()
+ call assert_fails('call MyDefVarargs()', 'E119:')
+ assert_equal('one,foo', MyDefVarargs('one'))
+ assert_equal('one,two', MyDefVarargs('one', 'two'))
+ assert_equal('one,two,three', MyDefVarargs('one', 'two', 'three'))
+enddef
+
+
+"def Test_call_func_defined_later()
+" call assert_equal('one', DefineLater('one'))
+" call assert_fails('call NotDefined("one")', 'E99:')
+"enddef
+
+func DefineLater(arg)
+ return a:arg
+endfunc
+
def Test_return_type_wrong()
CheckScriptFailure(['def Func(): number', 'return "a"', 'enddef'], 'expected number but got string')
CheckScriptFailure(['def Func(): string', 'return 1', 'enddef'], 'expected string but got number')
diff --git a/src/userfunc.c b/src/userfunc.c
index ef1d42d12..3c2673dde 100644
--- a/src/userfunc.c
+++ b/src/userfunc.c
@@ -200,6 +200,7 @@ get_function_args(
{
typval_T rettv;
+ // find the end of the expression (doesn't evaluate it)
any_default = TRUE;
p = skipwhite(p) + 1;
p = skipwhite(p);
diff --git a/src/version.c b/src/version.c
index f302040e8..09f9e9c3c 100644
--- a/src/version.c
+++ b/src/version.c
@@ -743,6 +743,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 222,
+/**/
221,
/**/
220,
diff --git a/src/vim9compile.c b/src/vim9compile.c
index 12400a35d..a362fe8b4 100644
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -956,11 +956,12 @@ generate_BCALL(cctx_T *cctx, int func_idx, int argcount)
* Return FAIL if the number of arguments is wrong.
*/
static int
-generate_CALL(cctx_T *cctx, ufunc_T *ufunc, int argcount)
+generate_CALL(cctx_T *cctx, ufunc_T *ufunc, int pushed_argcount)
{
isn_T *isn;
garray_T *stack = &cctx->ctx_type_stack;
int regular_args = ufunc->uf_args.ga_len;
+ int argcount = pushed_argcount;
if (argcount > regular_args && !has_varargs(ufunc))
{
@@ -978,9 +979,13 @@ generate_CALL(cctx_T *cctx, ufunc_T *ufunc, int argcount)
{
int count = argcount - regular_args;
- // TODO: add default values for optional arguments?
- generate_NEWLIST(cctx, count < 0 ? 0 : count);
- argcount = regular_args + 1;
+ // If count is negative an empty list will be added after evaluating
+ // default values for missing optional arguments.
+ if (count >= 0)
+ {
+ generate_NEWLIST(cctx, count);
+ argcount = regular_args + 1;
+ }
}
if ((isn = generate_instr(cctx,
@@ -4600,6 +4605,44 @@ compile_def_function(ufunc_T *ufunc, int set_return_type)
// Most modern script version.
current_sctx.sc_version = SCRIPT_VERSION_VIM9;
+ if (ufunc->uf_def_args.ga_len > 0)
+ {
+ int count = ufunc->uf_def_args.ga_len;
+ int i;
+ char_u *arg;
+ int off = STACK_FRAME_SIZE + (ufunc->uf_va_name != NULL ? 1 : 0);
+
+ // Produce instructions for the default values of optional arguments.
+ // Store the instruction index in uf_def_arg_idx[] so that we know
+ // where to start when the function is called, depending on the number
+ // of arguments.
+ ufunc->uf_def_arg_idx = ALLOC_CLEAR_MULT(int, count + 1);
+ if (ufunc->uf_def_arg_idx == NULL)
+ goto erret;
+ for (i = 0; i < count; ++i)
+ {
+ ufunc->uf_def_arg_idx[i] = instr->ga_len;
+ arg = ((char_u **)(ufunc->uf_def_args.ga_data))[i];
+ if (compile_expr1(&arg, &cctx) == FAIL
+ || generate_STORE(&cctx, ISN_STORE,
+ i - count - off, NULL) == FAIL)
+ goto erret;
+ }
+
+ // If a varargs is following, push an empty list.
+ if (ufunc->uf_va_name != NULL)
+ {
+ if (generate_NEWLIST(&cctx, 0) == FAIL
+ || generate_STORE(&cctx, ISN_STORE, -off, NULL) == FAIL)
+ goto erret;
+ }
+
+ ufunc->uf_def_arg_idx[count] = instr->ga_len;
+ }
+
+ /*
+ * Loop over all the lines of the function and generate instructions.
+ */
for (;;)
{
if (line != NULL && *line == '|')
diff --git a/src/vim9execute.c b/src/vim9execute.c
index 093fbad8e..d40320122 100644
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -70,7 +70,7 @@ typedef struct {
#define STACK_TV_BOT(idx) (((typval_T *)ectx->ec_stack.ga_data) + ectx->ec_stack.ga_len + idx)
/*
- * Return the number of arguments, including any vararg.
+ * Return the number of arguments, including optional arguments and any vararg.
*/
static int
ufunc_argcount(ufunc_T *ufunc)
@@ -79,6 +79,35 @@ ufunc_argcount(ufunc_T *ufunc)
}
/*
+ * Set the instruction index, depending on omitted arguments, where the default
+ * values are to be computed. If all optional arguments are present, start
+ * with the function body.
+ * The expression evaluation is at the start of the instructions:
+ * 0 -> EVAL default1
+ * STORE arg[-2]
+ * 1 -> EVAL default2
+ * STORE arg[-1]
+ * 2 -> function body
+ */
+ static void
+init_instr_idx(ufunc_T *ufunc, int argcount, ectx_T *ectx)
+{
+ if (ufunc->uf_def_args.ga_len == 0)
+ ectx->ec_iidx = 0;
+ else
+ {
+ int defcount = ufunc->uf_args.ga_len - argcount;
+
+ // If there is a varargs argument defcount can be negative, no defaults
+ // to evaluate then.
+ if (defcount < 0)
+ defcount = 0;
+ ectx->ec_iidx = ufunc->uf_def_arg_idx[
+ ufunc->uf_def_args.ga_len - defcount];
+ }
+}
+
+/*
* Call compiled function "cdf_idx" from compiled code.
*
* Stack has:
@@ -107,23 +136,15 @@ call_dfunc(int cdf_idx, int argcount, ectx_T *ectx)
if (ga_grow(&ectx->ec_stack, optcount + 3 + dfunc->df_varcount) == FAIL)
return FAIL;
-// TODO: Put omitted argument default values on the stack.
- if (optcount > 0)
- {
- emsg("optional arguments not implemented yet");
- return FAIL;
- }
if (optcount < 0)
{
emsg("argument count wrong?");
return FAIL;
}
-// for (idx = argcount - dfunc->df_minarg;
-// idx < dfunc->df_maxarg; ++idx)
-// {
-// copy_tv(&dfunc->df_defarg[idx], STACK_TV_BOT(0));
-// ++ectx->ec_stack.ga_len;
-// }
+
+ // Reserve space for omitted optional arguments, filled in soon.
+ // Also any empty varargs argument.
+ ectx->ec_stack.ga_len += optcount;
// Store current execution state in stack frame for ISN_RETURN.
// TODO: If the actual number of arguments doesn't match what the called
@@ -142,7 +163,9 @@ call_dfunc(int cdf_idx, int argcount, ectx_T *ectx)
ectx->ec_dfunc_idx = cdf_idx;
ectx->ec_instr = dfunc->df_instr;
estack_push_ufunc(ETYPE_UFUNC, dfunc->df_ufunc, 1);
- ectx->ec_iidx = 0;
+
+ // Decide where to start execution, handles optional arguments.
+ init_instr_idx(ufunc, argcount, ectx);
return OK;
}
@@ -156,9 +179,9 @@ call_dfunc(int cdf_idx, int argcount, ectx_T *ectx)
static void
func_return(ectx_T *ectx)
{
- int ret_idx = ectx->ec_stack.ga_len - 1;
int idx;
dfunc_T *dfunc;
+ int top;
// execution context goes one level up
estack_pop();
@@ -166,17 +189,27 @@ func_return(ectx_T *ectx)
// Clear the local variables and temporary values, but not
// the return value.
for (idx = ectx->ec_frame + STACK_FRAME_SIZE;
- idx < ectx->ec_stack.ga_len - 1; ++idx)
+ idx < ectx->ec_stack.ga_len - 1; ++idx)
clear_tv(STACK_TV(idx));
+
+ // Clear the arguments.
dfunc = ((dfunc_T *)def_functions.ga_data) + ectx->ec_dfunc_idx;
- ectx->ec_stack.ga_len = ectx->ec_frame
- - ufunc_argcount(dfunc->df_ufunc) + 1;
+ top = ectx->ec_frame - ufunc_argcount(dfunc->df_ufunc);
+ for (idx = top; idx < ectx->ec_frame; ++idx)
+ clear_tv(STACK_TV(idx));
+
+ // Restore the previous frame.
ectx->ec_dfunc_idx = STACK_TV(ectx->ec_frame)->vval.v_number;
ectx->ec_iidx = STACK_TV(ectx->ec_frame + 1)->vval.v_number;
ectx->ec_frame = STACK_TV(ectx->ec_frame + 2)->vval.v_number;
- *STACK_TV_BOT(-1) = *STACK_TV(ret_idx);
dfunc = ((dfunc_T *)def_functions.ga_data) + ectx->ec_dfunc_idx;
ectx->ec_instr = dfunc->df_instr;
+
+ // Reset the stack to the position before the call, move the return value
+ // to the top of the stack.
+ idx = ectx->ec_stack.ga_len - 1;
+ ectx->ec_stack.ga_len = top + 1;
+ *STACK_TV_BOT(-1) = *STACK_TV(idx);
}
#undef STACK_TV
@@ -362,7 +395,7 @@ call_def_function(
int idx;
int ret = FAIL;
dfunc_T *dfunc;
- int optcount = ufunc_argcount(ufunc) - argc;
+ int defcount = ufunc->uf_args.ga_len - argc;
// Get pointer to item in the stack.
#define STACK_TV(idx) (((typval_T *)ectx.ec_stack.ga_data) + idx)
@@ -388,17 +421,18 @@ call_def_function(
copy_tv(&argv[idx], STACK_TV_BOT(0));
++ectx.ec_stack.ga_len;
}
+ // Make space for omitted arguments, will store default value below.
+ if (defcount > 0)
+ for (idx = 0; idx < defcount; ++idx)
+ {
+ STACK_TV_BOT(0)->v_type = VAR_UNKNOWN;
+ ++ectx.ec_stack.ga_len;
+ }
// Frame pointer points to just after arguments.
ectx.ec_frame = ectx.ec_stack.ga_len;
initial_frame_ptr = ectx.ec_frame;
-// TODO: Put omitted argument default values on the stack.
- if (optcount > 0)
- {
- emsg("optional arguments not implemented yet");
- return FAIL;
- }
// dummy frame entries
for (idx = 0; idx < STACK_FRAME_SIZE; ++idx)
{
@@ -413,7 +447,10 @@ call_def_function(
ectx.ec_stack.ga_len += dfunc->df_varcount;
ectx.ec_instr = dfunc->df_instr;
- ectx.ec_iidx = 0;
+
+ // Decide where to start execution, handles optional arguments.
+ init_instr_idx(ufunc, argc, &ectx);
+
for (;;)
{
isn_T *iptr;
@@ -1657,7 +1694,11 @@ ex_disassemble(exarg_T *eap)
break;
case ISN_STORE:
- smsg("%4d STORE $%lld", current, iptr->isn_arg.number);
+ if (iptr->isn_arg.number < 0)
+ smsg("%4d STORE arg[%lld]", current,
+ iptr->isn_arg.number + STACK_FRAME_SIZE);
+ else
+ smsg("%4d STORE $%lld", current, iptr->isn_arg.number);
break;
case ISN_STOREV:
smsg("%4d STOREV v:%s", current,