Patch 8.2.0222
Problem: Vim9: optional function arguments don't work yet.
Solution: Implement optional function arguments.
Files: src/userfunc.c, src/vim9compile.c, src/vim9execute.c,
src/structs.h, src/testdir/test_vim9_script.vim
*** ../vim-8.2.0221/src/userfunc.c 2020-02-04 21:54:03.277158742 +0100
--- src/userfunc.c 2020-02-06 14:03:26.305655386 +0100
***************
*** 200,205 ****
--- 200,206 ----
{
typval_T rettv;
+ // find the end of the expression (doesn't evaluate it)
any_default = TRUE;
p = skipwhite(p) + 1;
p = skipwhite(p);
*** ../vim-8.2.0221/src/vim9compile.c 2020-02-05 22:10:01.644130754 +0100
--- src/vim9compile.c 2020-02-06 17:48:32.907020245 +0100
***************
*** 956,966 ****
* Return FAIL if the number of arguments is wrong.
*/
static int
! generate_CALL(cctx_T *cctx, ufunc_T *ufunc, int argcount)
{
isn_T *isn;
garray_T *stack = &cctx->ctx_type_stack;
int regular_args = ufunc->uf_args.ga_len;
if (argcount > regular_args && !has_varargs(ufunc))
{
--- 956,967 ----
* Return FAIL if the number of arguments is wrong.
*/
static int
! 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,986 ****
{
int count = argcount - regular_args;
! // TODO: add default values for optional arguments?
! generate_NEWLIST(cctx, count < 0 ? 0 : count);
! argcount = regular_args + 1;
}
if ((isn = generate_instr(cctx,
--- 979,991 ----
{
int count = argcount - regular_args;
! // 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,4605 ****
--- 4605,4648 ----
// 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 == '|')
*** ../vim-8.2.0221/src/vim9execute.c 2020-02-05 22:10:01.640130754 +0100
--- src/vim9execute.c 2020-02-06 16:58:59.750275754 +0100
***************
*** 70,76 ****
#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.
*/
static int
ufunc_argcount(ufunc_T *ufunc)
--- 70,76 ----
#define STACK_TV_BOT(idx) (((typval_T *)ectx->ec_stack.ga_data) + ectx->ec_stack.ga_len + idx)
/*
! * Return the number of arguments, including optional arguments and any vararg.
*/
static int
ufunc_argcount(ufunc_T *ufunc)
***************
*** 79,84 ****
--- 79,113 ----
}
/*
+ * 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,129 ****
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;
! // }
// Store current execution state in stack frame for ISN_RETURN.
// TODO: If the actual number of arguments doesn't match what the called
--- 136,150 ----
if (ga_grow(&ectx->ec_stack, optcount + 3 + dfunc->df_varcount) == FAIL)
return FAIL;
if (optcount < 0)
{
emsg("argument count wrong?");
return FAIL;
}
!
! // 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,148 ****
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;
return OK;
}
--- 163,171 ----
ectx->ec_dfunc_idx = cdf_idx;
ectx->ec_instr = dfunc->df_instr;
estack_push_ufunc(ETYPE_UFUNC, dfunc->df_ufunc, 1);
!
! // Decide where to start execution, handles optional arguments.
! init_instr_idx(ufunc, argcount, ectx);
return OK;
}
***************
*** 156,164 ****
static void
func_return(ectx_T *ectx)
{
- int ret_idx = ectx->ec_stack.ga_len - 1;
int idx;
dfunc_T *dfunc;
// execution context goes one level up
estack_pop();
--- 179,187 ----
static void
func_return(ectx_T *ectx)
{
int idx;
dfunc_T *dfunc;
+ int top;
// execution context goes one level up
estack_pop();
***************
*** 166,182 ****
// 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)
clear_tv(STACK_TV(idx));
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;
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;
}
#undef STACK_TV
--- 189,215 ----
// 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)
clear_tv(STACK_TV(idx));
+
+ // Clear the arguments.
dfunc = ((dfunc_T *)def_functions.ga_data) + ectx->ec_dfunc_idx;
! 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;
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,368 ****
int idx;
int ret = FAIL;
dfunc_T *dfunc;
! int optcount = ufunc_argcount(ufunc) - argc;
// Get pointer to item in the stack.
#define STACK_TV(idx) (((typval_T *)ectx.ec_stack.ga_data) + idx)
--- 395,401 ----
int idx;
int ret = FAIL;
dfunc_T *dfunc;
! 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,404 ****
copy_tv(&argv[idx], STACK_TV_BOT(0));
++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)
{
--- 421,438 ----
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;
// dummy frame entries
for (idx = 0; idx < STACK_FRAME_SIZE; ++idx)
{
***************
*** 413,419 ****
ectx.ec_stack.ga_len += dfunc->df_varcount;
ectx.ec_instr = dfunc->df_instr;
! ectx.ec_iidx = 0;
for (;;)
{
isn_T *iptr;
--- 447,456 ----
ectx.ec_stack.ga_len += dfunc->df_varcount;
ectx.ec_instr = dfunc->df_instr;
!
! // Decide where to start execution, handles optional arguments.
! init_instr_idx(ufunc, argc, &ectx);
!
for (;;)
{
isn_T *iptr;
***************
*** 1657,1663 ****
break;
case ISN_STORE:
! smsg("%4d STORE $%lld", current, iptr->isn_arg.number);
break;
case ISN_STOREV:
smsg("%4d STOREV v:%s", current,
--- 1694,1704 ----
break;
case ISN_STORE:
! 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,
*** ../vim-8.2.0221/src/structs.h 2020-01-30 16:27:02.068562909 +0100
--- src/structs.h 2020-02-06 14:13:18.447208957 +0100
***************
*** 1515,1520 ****
--- 1515,1522 ----
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
*** ../vim-8.2.0221/src/testdir/test_vim9_script.vim 2020-02-06 13:15:48.891468179 +0100
--- src/testdir/test_vim9_script.vim 2020-02-06 17:50:21.894602641 +0100
***************
*** 139,172 ****
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
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('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:')
enddef
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')
--- 139,186 ----
assert_equal('one,two,three', MyVarargs('one', 'two', 'three'))
enddef
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()
! call assert_equal('string', MyDefaultArgs())
call assert_equal('one', MyDefaultArgs('one'))
call assert_fails('call MyDefaultArgs("one", "two")', 'E118:')
endfunc
! " 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')
*** ../vim-8.2.0221/src/version.c 2020-02-06 13:15:48.891468179 +0100
--- src/version.c 2020-02-06 14:02:16.609974366 +0100
***************
*** 744,745 ****
--- 744,747 ----
{ /* Add new patch number below this line */
+ /**/
+ 222,
/**/
--
hundred-and-one symptoms of being an internet addict:
38. You wake up at 3 a.m. to go to the bathroom and stop and check your e-mail
on the way back to bed.
/// Bram Moolenaar -- Br...@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 ///