To: vim_dev@googlegroups.com Subject: Patch 8.2.0222 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ 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 ! 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 -- Bram@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 ///