mirror of
https://github.com/vim/vim.git
synced 2025-09-23 03:43:49 -04:00
patch 8.2.0222: Vim9: optional function arguments don't work yet
Problem: Vim9: optional function arguments don't work yet. Solution: Implement optional function arguments.
This commit is contained in:
@@ -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
|
||||
|
||||
|
@@ -139,6 +139,39 @@ def Test_call_varargs()
|
||||
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:')
|
||||
@@ -148,25 +181,6 @@ 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')
|
||||
|
@@ -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);
|
||||
|
@@ -742,6 +742,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
222,
|
||||
/**/
|
||||
221,
|
||||
/**/
|
||||
|
@@ -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,10 +979,14 @@ 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);
|
||||
// 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,
|
||||
ufunc->uf_dfunc_idx >= 0 ? ISN_DCALL : ISN_UCALL)) == NULL)
|
||||
@@ -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 == '|')
|
||||
|
@@ -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)
|
||||
@@ -78,6 +78,35 @@ ufunc_argcount(ufunc_T *ufunc)
|
||||
return ufunc->uf_args.ga_len + (ufunc->uf_va_name != NULL ? 1 : 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
@@ -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();
|
||||
@@ -168,15 +191,25 @@ func_return(ectx_T *ectx)
|
||||
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;
|
||||
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,6 +1694,10 @@ ex_disassemble(exarg_T *eap)
|
||||
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:
|
||||
|
Reference in New Issue
Block a user