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 -- [email protected] -- 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    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/202002061652.016Gq1eU001605%40masaka.moolenaar.net.

Raspunde prin e-mail lui