Patch 9.0.1123
Problem:    Class function not implemented yet.
Solution:   Implement defining and calling a class function.
Files:      src/vim9class.c, src/proto/vim9class.pro, src/structs.h,
            src/vim9expr.c, src/testdir/test_vim9_class.vim


*** ../vim-9.0.1122/src/vim9class.c     2022-12-23 17:56:21.405511534 +0000
--- src/vim9class.c     2023-01-01 12:29:03.529703540 +0000
***************
*** 223,231 ****
      garray_T classmembers;
      ga_init2(&classmembers, sizeof(ocmember_T), 10);
  
!     // Growarray with object methods declared in the class.
!     garray_T classmethods;
!     ga_init2(&classmethods, sizeof(ufunc_T *), 10);
  
      // Growarray with object members declared in the class.
      garray_T objmembers;
--- 223,231 ----
      garray_T classmembers;
      ga_init2(&classmembers, sizeof(ocmember_T), 10);
  
!     // Growarray with functions declared in the class.
!     garray_T classfunctions;
!     ga_init2(&classfunctions, sizeof(ufunc_T *), 10);
  
      // Growarray with object members declared in the class.
      garray_T objmembers;
***************
*** 288,293 ****
--- 288,306 ----
            }
        }
  
+       int has_static = FALSE;
+       char_u *ps = p;
+       if (checkforcmd(&p, "static", 4))
+       {
+           if (STRNCMP(ps, "static", 6) != 0)
+           {
+               semsg(_(e_command_cannot_be_shortened_str), ps);
+               break;
+           }
+           has_static = TRUE;
+           p = skipwhite(ps + 6);
+       }
+ 
        // object members (public, read access, private):
        //      "this._varname"
        //      "this.varname"
***************
*** 314,360 ****
            }
        }
  
-       // class members and methods
-       else if (checkforcmd(&p, "static", 6))
-       {
-           p = skipwhite(p);
-           if (checkforcmd(&p, "def", 3))
-           {
-               // TODO: class method
-               //        static def someMethod()
-               //        enddef
-               //        static def <Tval> someMethod()
-               //        enddef
-           }
-           else
-           {
-               // class members (public, read access, private):
-               //      "static _varname"
-               //      "static varname"
-               //      "public static varname"
-               char_u *varname = p;
-               char_u *varname_end = NULL;
-               type_T *type = NULL;
-               char_u *init_expr = NULL;
-               if (parse_member(eap, line, varname, has_public,
-                         &varname_end, &type_list, &type, &init_expr) == FAIL)
-                   break;
-               if (add_member(&classmembers, varname, varname_end,
-                                         has_public, type, init_expr) == FAIL)
-               {
-                   vim_free(init_expr);
-                   break;
-               }
-           }
-       }
- 
        // constructors:
        //        def new()
        //        enddef
        //        def newOther()
        //        enddef
!       // methods:
!       //        def someMethod()
        //        enddef
        // TODO:
        //        def <Tval> someMethod()
--- 327,341 ----
            }
        }
  
        // constructors:
        //        def new()
        //        enddef
        //        def newOther()
        //        enddef
!       // object methods and class functions:
!       //        def SomeMethod()
!       //        enddef
!       //        static def ClassFunction()
        //        enddef
        // TODO:
        //        def <Tval> someMethod()
***************
*** 364,369 ****
--- 345,352 ----
            exarg_T     ea;
            garray_T    lines_to_free;
  
+           // TODO: error for "public static def Func()"?
+ 
            CLEAR_FIELD(ea);
            ea.cmd = line;
            ea.arg = p;
***************
*** 376,388 ****
            ga_clear_strings(&lines_to_free);
  
            // TODO: how about errors?
!           if (uf != NULL && ga_grow(&objmethods, 1) == OK)
            {
!               if (STRNCMP(uf->uf_name, "new", 3) == 0)
                    uf->uf_flags |= FC_NEW;
  
!               ((ufunc_T **)objmethods.ga_data)[objmethods.ga_len] = uf;
!               ++objmethods.ga_len;
            }
        }
  
--- 359,396 ----
            ga_clear_strings(&lines_to_free);
  
            // TODO: how about errors?
!           int is_new = STRNCMP(uf->uf_name, "new", 3) == 0;
!           garray_T *fgap = has_static || is_new
!                                              ? &classfunctions : &objmethods;
!           if (uf != NULL && ga_grow(fgap, 1) == OK)
            {
!               if (is_new)
                    uf->uf_flags |= FC_NEW;
  
!               ((ufunc_T **)fgap->ga_data)[fgap->ga_len] = uf;
!               ++fgap->ga_len;
!           }
!       }
! 
!       // class members
!       else if (has_static)
!       {
!           // class members (public, read access, private):
!           //  "static _varname"
!           //  "static varname"
!           //  "public static varname"
!           char_u *varname = p;
!           char_u *varname_end = NULL;
!           type_T *type = NULL;
!           char_u *init_expr = NULL;
!           if (parse_member(eap, line, varname, has_public,
!                     &varname_end, &type_list, &type, &init_expr) == FAIL)
!               break;
!           if (add_member(&classmembers, varname, varname_end,
!                                     has_public, type, init_expr) == FAIL)
!           {
!               vim_free(init_expr);
!               break;
            }
        }
  
***************
*** 445,452 ****
        }
  
        int have_new = FALSE;
!       for (int i = 0; i < objmethods.ga_len; ++i)
!           if (STRCMP(((ufunc_T **)objmethods.ga_data)[i]->uf_name,
                                                                   "new") == 0)
            {
                have_new = TRUE;
--- 453,460 ----
        }
  
        int have_new = FALSE;
!       for (int i = 0; i < classfunctions.ga_len; ++i)
!           if (STRCMP(((ufunc_T **)classfunctions.ga_data)[i]->uf_name,
                                                                   "new") == 0)
            {
                have_new = TRUE;
***************
*** 483,492 ****
            ga_clear_strings(&lines_to_free);
            vim_free(fga.ga_data);
  
!           if (nf != NULL && ga_grow(&objmethods, 1) == OK)
            {
!               ((ufunc_T **)objmethods.ga_data)[objmethods.ga_len] = nf;
!               ++objmethods.ga_len;
  
                nf->uf_flags |= FC_NEW;
                nf->uf_ret_type = get_type_ptr(&type_list);
--- 491,500 ----
            ga_clear_strings(&lines_to_free);
            vim_free(fga.ga_data);
  
!           if (nf != NULL && ga_grow(&classfunctions, 1) == OK)
            {
!               ((ufunc_T **)classfunctions.ga_data)[classfunctions.ga_len] = 
nf;
!               ++classfunctions.ga_len;
  
                nf->uf_flags |= FC_NEW;
                nf->uf_ret_type = get_type_ptr(&type_list);
***************
*** 500,520 ****
            }
        }
  
!       // TODO: class methods
!       cl->class_obj_method_count = objmethods.ga_len;
!       cl->class_obj_methods = ALLOC_MULT(ufunc_T *, objmethods.ga_len);
!       if (cl->class_obj_methods == NULL)
!           goto cleanup;
!       mch_memmove(cl->class_obj_methods, objmethods.ga_data,
!                                       sizeof(ufunc_T *) * objmethods.ga_len);
!       vim_free(objmethods.ga_data);
! 
!       // Set the class pointer on all the object methods.
!       for (int i = 0; i < objmethods.ga_len; ++i)
!       {
!           ufunc_T *fp = cl->class_obj_methods[i];
!           fp->uf_class = cl;
!           fp->uf_flags |= FC_OBJECT;  // TODO: not for class method
        }
  
        cl->class_type.tt_type = VAR_CLASS;
--- 508,542 ----
            }
        }
  
!       // loop 1: class functions, loop 2: object methods
!       for (int loop = 1; loop <= 2; ++loop)
!       {
!           garray_T *gap = loop == 1 ? &classfunctions : &objmethods;
!           int      *fcount = loop == 1 ? &cl->class_class_function_count
!                                        : &cl->class_obj_method_count;
!           ufunc_T ***fup = loop == 1 ? &cl->class_class_functions
!                                      : &cl->class_obj_methods;
! 
!           *fcount = gap->ga_len;
!           if (gap->ga_len == 0)
!           {
!               *fup = NULL;
!               continue;
!           }
!           *fup = ALLOC_MULT(ufunc_T *, gap->ga_len);
!           if (*fup == NULL)
!               goto cleanup;
!           mch_memmove(*fup, gap->ga_data, sizeof(ufunc_T *) * gap->ga_len);
!           vim_free(gap->ga_data);
! 
!           // Set the class pointer on all the object methods.
!           for (int i = 0; i < gap->ga_len; ++i)
!           {
!               ufunc_T *fp = (*fup)[i];
!               fp->uf_class = cl;
!               if (loop == 2)
!                   fp->uf_flags |= FC_OBJECT;
!           }
        }
  
        cl->class_type.tt_type = VAR_CLASS;
***************
*** 539,544 ****
--- 561,567 ----
      if (cl != NULL)
      {
        vim_free(cl->class_name);
+       vim_free(cl->class_class_functions);
        vim_free(cl->class_obj_members);
        vim_free(cl->class_obj_methods);
        vim_free(cl);
***************
*** 565,570 ****
--- 588,601 ----
        func_clear_free(uf, FALSE);
      }
      ga_clear(&objmethods);
+ 
+     for (int i = 0; i < classfunctions.ga_len; ++i)
+     {
+       ufunc_T *uf = ((ufunc_T **)classfunctions.ga_data)[i];
+       func_clear_free(uf, FALSE);
+     }
+     ga_clear(&classfunctions);
+ 
      clear_type_list(&type_list);
  }
  
***************
*** 627,633 ****
  /*
   * Evaluate what comes after a class:
   * - class member: SomeClass.varname
!  * - class method: SomeClass.SomeMethod()
   * - class constructor: SomeClass.new()
   * - object member: someObject.varname
   * - object method: someObject.SomeMethod()
--- 658,664 ----
  /*
   * Evaluate what comes after a class:
   * - class member: SomeClass.varname
!  * - class function: SomeClass.SomeMethod()
   * - class constructor: SomeClass.new()
   * - object member: someObject.varname
   * - object method: someObject.SomeMethod()
***************
*** 664,672 ****
                                             : rettv->vval.v_object->obj_class;
      if (*name_end == '(')
      {
!       for (int i = 0; i < cl->class_obj_method_count; ++i)
        {
!           ufunc_T *fp = cl->class_obj_methods[i];
            // Use a separate pointer to avoid that ASAN complains about
            // uf_name[] only being 4 characters.
            char_u *ufname = (char_u *)fp->uf_name;
--- 695,707 ----
                                             : rettv->vval.v_object->obj_class;
      if (*name_end == '(')
      {
!       int on_class = rettv->v_type == VAR_CLASS;
!       int count = on_class ? cl->class_class_function_count
!                            : cl->class_obj_method_count;
!       for (int i = 0; i < count; ++i)
        {
!           ufunc_T *fp = on_class ? cl->class_class_functions[i]
!                                  : cl->class_obj_methods[i];
            // Use a separate pointer to avoid that ASAN complains about
            // uf_name[] only being 4 characters.
            char_u *ufname = (char_u *)fp->uf_name;
***************
*** 805,813 ****
        goto fail_after_eval;
      len = fname_end - fname;
  
!     for (int i = 0; i < cl->class_obj_method_count; ++i)
      {
!       ufunc_T *fp = cl->class_obj_methods[i];
        // Use a separate pointer to avoid that ASAN complains about
        // uf_name[] only being 4 characters.
        char_u *ufname = (char_u *)fp->uf_name;
--- 840,852 ----
        goto fail_after_eval;
      len = fname_end - fname;
  
!     int count = tv.v_type == VAR_CLASS ? cl->class_class_function_count
!                                      : cl->class_obj_method_count;
!     ufunc_T **funcs = tv.v_type == VAR_CLASS ? cl->class_class_functions
!                                            : cl->class_obj_methods;
!     for (int i = 0; i < count; ++i)
      {
!       ufunc_T *fp = funcs[i];
        // Use a separate pointer to avoid that ASAN complains about
        // uf_name[] only being 4 characters.
        char_u *ufname = (char_u *)fp->uf_name;
***************
*** 824,829 ****
--- 863,897 ----
  }
  
  /*
+  * If "cctx->ctx_ufunc" indicates we are in a class, check if "name" is a 
class
+  * member.  If it is then return TRUE and set "cl_ret" and "idx_ret".
+  */
+     int
+ class_member_exists(
+       char_u  *name,
+       class_T **cl_ret,
+       int     *idx_ret,
+       cctx_T  *cctx)
+ {
+     if (cctx->ctx_ufunc == NULL || cctx->ctx_ufunc->uf_class == NULL)
+       return FALSE;
+     class_T *cl = cctx->ctx_ufunc->uf_class;
+ 
+     for (int idx = 0; idx < cl->class_class_member_count; ++idx)
+     {
+       ocmember_T *m = &cl->class_class_members[idx];
+       if (STRCMP(m->ocm_name, name) == 0)
+       {
+           *cl_ret = cl;
+           *idx_ret = idx;
+           return TRUE;
+       }
+     }
+ 
+     return FALSE;
+ }
+ 
+ /*
   * Make a copy of an object.
   */
      void
*** ../vim-9.0.1122/src/proto/vim9class.pro     2022-12-10 18:42:09.090378817 
+0000
--- src/proto/vim9class.pro     2023-01-01 12:28:33.305662665 +0000
***************
*** 6,11 ****
--- 6,12 ----
  void ex_type(exarg_T *eap);
  int class_object_index(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int 
verbose);
  ufunc_T *find_class_func(char_u **arg);
+ int class_member_exists(char_u *name, class_T **cl_ret, int *idx_ret, cctx_T 
*cctx);
  void copy_object(typval_T *from, typval_T *to);
  void object_unref(object_T *obj);
  void copy_class(typval_T *from, typval_T *to);
*** ../vim-9.0.1122/src/structs.h       2022-12-29 20:56:20.021538298 +0000
--- src/structs.h       2022-12-31 20:59:34.665726295 +0000
***************
*** 1493,1501 ****
      ocmember_T        *class_class_members;   // allocated
      typval_T  *class_members_tv;      // allocated array of class member vals
  
!     // class methods: "static def SomeMethod()"
!     int               class_class_method_count;
!     ufunc_T   **class_class_methods;  // allocated
  
      // object members: "this.varname"
      int               class_obj_member_count;
--- 1493,1501 ----
      ocmember_T        *class_class_members;   // allocated
      typval_T  *class_members_tv;      // allocated array of class member vals
  
!     // class functions: "static def SomeMethod()"
!     int               class_class_function_count;
!     ufunc_T   **class_class_functions;        // allocated
  
      // object members: "this.varname"
      int               class_obj_member_count;
*** ../vim-9.0.1122/src/vim9expr.c      2022-12-29 20:56:20.025538293 +0000
--- src/vim9expr.c      2023-01-01 12:47:25.878330043 +0000
***************
*** 587,593 ****
        }
        else
        {
!           lvar_T lvar;
  
            if (lookup_local(*arg, len, &lvar, cctx) == OK)
            {
--- 587,594 ----
        }
        else
        {
!           lvar_T  lvar;
!           class_T *cl = NULL;
  
            if (lookup_local(*arg, len, &lvar, cctx) == OK)
            {
***************
*** 602,607 ****
--- 603,612 ----
                else
                    gen_load = TRUE;
            }
+           else if (class_member_exists(name, &cl, &idx, cctx))
+           {
+               res = generate_CLASSMEMBER(cctx, TRUE, cl, idx);
+           }
            else
            {
                // "var" can be script-local even without using "s:" if it
*** ../vim-9.0.1122/src/testdir/test_vim9_class.vim     2022-12-31 
19:00:58.845477088 +0000
--- src/testdir/test_vim9_class.vim     2023-01-01 12:56:33.794406510 +0000
***************
*** 377,383 ****
           static _secret = 7
           public static  anybody = 42
  
!          def AddToCounter(nr: number)
             counter += nr
           enddef
        endclass
--- 377,383 ----
           static _secret = 7
           public static  anybody = 42
  
!          static def AddToCounter(nr: number)
             counter += nr
           enddef
        endclass
***************
*** 402,407 ****
--- 402,433 ----
    END
    v9.CheckScriptSuccess(lines)
  enddef
+ 
+ def Test_class_function()
+   var lines =<< trim END
+       vim9script
+       class Value
+         this.value = 0
+         static objects = 0
+ 
+         def new(v: number)
+           this.value = v
+           ++objects
+         enddef
+ 
+         static def GetCount(): number
+           return objects
+         enddef
+       endclass
+ 
+       assert_equal(0, Value.GetCount())
+       var v1 = Value.new(2)
+       assert_equal(1, Value.GetCount())
+       var v2 = Value.new(7)
+       assert_equal(2, Value.GetCount())
+   END
+   v9.CheckScriptSuccess(lines)
+ enddef
  
  def Test_class_object_to_string()
    var lines =<< trim END
*** ../vim-9.0.1122/src/version.c       2022-12-31 19:00:58.845477088 +0000
--- src/version.c       2023-01-01 12:57:07.734409940 +0000
***************
*** 697,698 ****
--- 697,700 ----
  {   /* Add new patch number below this line */
+ /**/
+     1123,
  /**/

-- 
If you only have a hammer, you tend to see every problem as a nail.
If you only have MS-Windows, you tend to solve every problem by rebooting.

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///                                                                      \\\
\\\        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
 \\\            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/20230101125909.B86881C0AA3%40moolenaar.net.

Raspunde prin e-mail lui