Patch 9.0.1152
Problem:    Class "implements" argument not implemented.
Solution:   Implement "implements" argument.  Add basic checks for when a
            class implements an interface.
Files:      src/vim9class.c, src/alloc.c, src/errors.h, src/evalvars.c,
            src/structs.h, src/eval.c, src/testdir/test_vim9_class.vim


*** ../vim-9.0.1151/src/vim9class.c     2023-01-05 19:59:14.003418087 +0000
--- src/vim9class.c     2023-01-06 18:33:06.526908945 +0000
***************
*** 227,241 ****
        semsg(_(e_white_space_required_after_name_str), arg);
        return;
      }
  
      // TODO:
      //    generics: <Tkey, Tentry>
-     //    extends SomeClass
-     //    implements SomeInterface
-     //    specifies SomeInterface
-     //    check that nothing follows
      //          handle "is_export" if it is set
  
      garray_T  type_list;          // list of pointers to allocated types
      ga_init2(&type_list, sizeof(type_T *), 10);
  
--- 227,276 ----
        semsg(_(e_white_space_required_after_name_str), arg);
        return;
      }
+     char_u *name_start = arg;
  
      // TODO:
      //    generics: <Tkey, Tentry>
      //          handle "is_export" if it is set
  
+     // Names for "implements SomeInterface"
+     garray_T  ga_impl;
+     ga_init2(&ga_impl, sizeof(char_u *), 5);
+ 
+     arg = skipwhite(name_end);
+     while (*arg != NUL && *arg != '#' && *arg != '\n')
+     {
+       // TODO:
+       //    extends SomeClass
+       //    specifies SomeInterface
+       if (STRNCMP(arg, "implements", 10) == 0 && IS_WHITE_OR_NUL(arg[10]))
+       {
+           arg = skipwhite(arg + 10);
+           char_u *impl_end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
+           if (!IS_WHITE_OR_NUL(*impl_end))
+           {
+               semsg(_(e_white_space_required_after_name_str), arg);
+               goto early_ret;
+           }
+           char_u *iname = vim_strnsave(arg, impl_end - arg);
+           if (iname == NULL)
+               goto early_ret;
+           if (ga_add_string(&ga_impl, iname) == FAIL)
+           {
+               vim_free(iname);
+               goto early_ret;
+           }
+           arg = skipwhite(impl_end);
+       }
+       else
+       {
+           semsg(_(e_trailing_characters_str), arg);
+ early_ret:
+           ga_clear_strings(&ga_impl);
+           return;
+       }
+     }
+ 
      garray_T  type_list;          // list of pointers to allocated types
      ga_init2(&type_list, sizeof(type_T *), 10);
  
***************
*** 438,443 ****
--- 473,586 ----
      }
      vim_free(theline);
  
+     // Check a few things before defining the class.
+     if (success && ga_impl.ga_len > 0)
+     {
+       // Check all "implements" entries are valid and correct.
+       for (int i = 0; i < ga_impl.ga_len && success; ++i)
+       {
+           char_u *impl = ((char_u **)ga_impl.ga_data)[i];
+           typval_T tv;
+           tv.v_type = VAR_UNKNOWN;
+           if (eval_variable(impl, 0, 0, &tv, NULL,
+                                  EVAL_VAR_VERBOSE + EVAL_VAR_IMPORT) == FAIL)
+           {
+               semsg(_(e_interface_name_not_found_str), impl);
+               success = FALSE;
+               break;
+           }
+ 
+           if (tv.v_type != VAR_CLASS
+                   || tv.vval.v_class == NULL
+                   || (tv.vval.v_class->class_flags & CLASS_INTERFACE) == 0)
+           {
+               semsg(_(e_not_valid_interface_str), impl);
+               success = FALSE;
+           }
+ 
+           // check the members of the interface match the members of the class
+           class_T *ifcl = tv.vval.v_class;
+           for (int loop = 1; loop <= 2 && success; ++loop)
+           {
+               // loop == 1: check class members
+               // loop == 2: check object members
+               int if_count = loop == 1 ? ifcl->class_class_member_count
+                                        : ifcl->class_obj_member_count;
+               if (if_count == 0)
+                   continue;
+               ocmember_T *if_ms = loop == 1 ? ifcl->class_class_members
+                                              : ifcl->class_obj_members;
+               ocmember_T *cl_ms = (ocmember_T *)(loop == 1
+                                                   ? classmembers.ga_data
+                                                   : objmembers.ga_data);
+               int cl_count = loop == 1 ? classmembers.ga_len
+                                                          : objmembers.ga_len;
+               for (int if_i = 0; if_i < if_count; ++if_i)
+               {
+                   int cl_i;
+                   for (cl_i = 0; cl_i < cl_count; ++cl_i)
+                   {
+                       ocmember_T *m = &cl_ms[cl_i];
+                       if (STRCMP(if_ms[if_i].ocm_name, m->ocm_name) == 0)
+                       {
+                           // TODO: check type
+                           break;
+                       }
+                   }
+                   if (cl_i == cl_count)
+                   {
+                       semsg(_(e_member_str_of_interface_str_not_implemented),
+                                                  if_ms[if_i].ocm_name, impl);
+                       success = FALSE;
+                       break;
+                   }
+               }
+           }
+ 
+           // check the functions/methods of the interface match the
+           // functions/methods of the class
+           for (int loop = 1; loop <= 2 && success; ++loop)
+           {
+               // loop == 1: check class functions
+               // loop == 2: check object methods
+               int if_count = loop == 1 ? ifcl->class_class_function_count
+                                        : ifcl->class_obj_method_count;
+               if (if_count == 0)
+                   continue;
+               ufunc_T **if_fp = loop == 1 ? ifcl->class_class_functions
+                                           : ifcl->class_obj_methods;
+               ufunc_T **cl_fp = (ufunc_T **)(loop == 1
+                                               ? classfunctions.ga_data
+                                               : objmethods.ga_data);
+               int cl_count = loop == 1 ? classfunctions.ga_len
+                                                          : objmethods.ga_len;
+               for (int if_i = 0; if_i < if_count; ++if_i)
+               {
+                   char_u *if_name = if_fp[if_i]->uf_name;
+                   int cl_i;
+                   for (cl_i = 0; cl_i < cl_count; ++cl_i)
+                   {
+                       char_u *cl_name = cl_fp[cl_i]->uf_name;
+                       if (STRCMP(if_name, cl_name) == 0)
+                       {
+                           // TODO: check return and argument types
+                           break;
+                       }
+                   }
+                   if (cl_i == cl_count)
+                   {
+                       
semsg(_(e_function_str_of_interface_str_not_implemented),
+                                                               if_name, impl);
+                       success = FALSE;
+                       break;
+                   }
+               }
+           }
+ 
+           clear_tv(&tv);
+       }
+     }
+ 
      class_T *cl = NULL;
      if (success)
      {
***************
*** 450,459 ****
            cl->class_flags = CLASS_INTERFACE;
  
        cl->class_refcount = 1;
!       cl->class_name = vim_strnsave(arg, name_end - arg);
        if (cl->class_name == NULL)
            goto cleanup;
  
        // Add class and object members to "cl".
        if (add_members_to_class(&classmembers,
                                    &cl->class_class_members,
--- 593,615 ----
            cl->class_flags = CLASS_INTERFACE;
  
        cl->class_refcount = 1;
!       cl->class_name = vim_strnsave(name_start, name_end - name_start);
        if (cl->class_name == NULL)
            goto cleanup;
  
+       if (ga_impl.ga_len > 0)
+       {
+           // Move the "implements" names into the class.
+           cl->class_interface_count = ga_impl.ga_len;
+           cl->class_interfaces = ALLOC_MULT(char_u *, ga_impl.ga_len);
+           if (cl->class_interfaces == NULL)
+               goto cleanup;
+           for (int i = 0; i < ga_impl.ga_len; ++i)
+               cl->class_interfaces[i] = ((char_u **)ga_impl.ga_data)[i];
+           CLEAR_POINTER(ga_impl.ga_data);
+           ga_impl.ga_len = 0;
+       }
+ 
        // Add class and object members to "cl".
        if (add_members_to_class(&classmembers,
                                    &cl->class_class_members,
***************
*** 499,505 ****
                have_new = TRUE;
                break;
            }
!       if (!have_new)
        {
            // No new() method was defined, add the default constructor.
            garray_T fga;
--- 655,661 ----
                have_new = TRUE;
                break;
            }
!       if (is_class && !have_new)
        {
            // No new() method was defined, add the default constructor.
            garray_T fga;
***************
*** 589,594 ****
--- 745,751 ----
        // - Fill hashtab with object members and methods ?
  
        // Add the class to the script-local variables.
+       // TODO: handle other context, e.g. in a function
        typval_T tv;
        tv.v_type = VAR_CLASS;
        tv.vval.v_class = cl;
***************
*** 607,612 ****
--- 764,771 ----
        vim_free(cl);
      }
  
+     ga_clear_strings(&ga_impl);
+ 
      for (int round = 1; round <= 2; ++round)
      {
        garray_T *gap = round == 1 ? &classmembers : &objmembers;
***************
*** 986,991 ****
--- 1145,1154 ----
        // be freed.
        VIM_CLEAR(cl->class_name);
  
+       for (int i = 0; i < cl->class_interface_count; ++i)
+           vim_free(((char_u **)cl->class_interfaces)[i]);
+       vim_free(cl->class_interfaces);
+ 
        for (int i = 0; i < cl->class_class_member_count; ++i)
        {
            ocmember_T *m = &cl->class_class_members[i];
*** ../vim-9.0.1151/src/alloc.c 2022-10-14 13:11:10.128828896 +0100
--- src/alloc.c 2023-01-06 14:30:22.030967550 +0000
***************
*** 813,819 ****
  
  /*
   * Add string "p" to "gap".
!  * When out of memory "p" is freed and FAIL is returned.
   */
      int
  ga_add_string(garray_T *gap, char_u *p)
--- 813,819 ----
  
  /*
   * Add string "p" to "gap".
!  * When out of memory FAIL is returned (caller may want to free "p").
   */
      int
  ga_add_string(garray_T *gap, char_u *p)
*** ../vim-9.0.1151/src/errors.h        2023-01-05 19:59:14.003418087 +0000
--- src/errors.h        2023-01-06 18:32:59.238924811 +0000
***************
*** 3414,3417 ****
--- 3414,3425 ----
        INIT(= N_("E1344: Cannot initialize a member in an interface"));
  EXTERN char e_not_valid_command_in_interface_str[]
        INIT(= N_("E1345: Not a valid command in an interface: %s"));
+ EXTERN char e_interface_name_not_found_str[]
+       INIT(= N_("E1346: Interface name not found: %s"));
+ EXTERN char e_not_valid_interface_str[]
+       INIT(= N_("E1347: Not a valid interface: %s"));
+ EXTERN char e_member_str_of_interface_str_not_implemented[]
+       INIT(= N_("E1348: Member \"%s\" of interface \"%s\" not implemented"));
+ EXTERN char e_function_str_of_interface_str_not_implemented[]
+       INIT(= N_("E1349: Function \"%s\" of interface \"%s\" not 
implemented"));
  #endif
*** ../vim-9.0.1151/src/evalvars.c      2023-01-03 10:54:03.665703997 +0000
--- src/evalvars.c      2023-01-06 15:36:26.422097646 +0000
***************
*** 2913,2919 ****
      int
  eval_variable(
      char_u    *name,
!     int               len,            // length of "name"
      scid_T    sid,            // script ID for imported item or zero
      typval_T  *rettv,         // NULL when only checking existence
      dictitem_T        **dip,          // non-NULL when typval's dict item is 
needed
--- 2913,2919 ----
      int
  eval_variable(
      char_u    *name,
!     int               len,            // length of "name" or zero
      scid_T    sid,            // script ID for imported item or zero
      typval_T  *rettv,         // NULL when only checking existence
      dictitem_T        **dip,          // non-NULL when typval's dict item is 
needed
***************
*** 2923,2934 ****
      typval_T  *tv = NULL;
      int               found = FALSE;
      hashtab_T *ht = NULL;
!     int               cc;
      type_T    *type = NULL;
  
!     // truncate the name, so that we can use strcmp()
!     cc = name[len];
!     name[len] = NUL;
  
      // Check for local variable when debugging.
      if ((tv = lookup_debug_var(name)) == NULL)
--- 2923,2937 ----
      typval_T  *tv = NULL;
      int               found = FALSE;
      hashtab_T *ht = NULL;
!     int               cc = 0;
      type_T    *type = NULL;
  
!     if (len > 0)
!     {
!       // truncate the name, so that we can use strcmp()
!       cc = name[len];
!       name[len] = NUL;
!     }
  
      // Check for local variable when debugging.
      if ((tv = lookup_debug_var(name)) == NULL)
***************
*** 3095,3101 ****
        }
      }
  
!     name[len] = cc;
  
      return ret;
  }
--- 3098,3105 ----
        }
      }
  
!     if (len > 0)
!       name[len] = cc;
  
      return ret;
  }
*** ../vim-9.0.1151/src/structs.h       2023-01-05 20:14:39.259710536 +0000
--- src/structs.h       2023-01-06 16:07:00.287262332 +0000
***************
*** 1494,1499 ****
--- 1494,1503 ----
      int               class_refcount;
      int               class_copyID;           // used by garbage collection
  
+     // interfaces declared for the class
+     int               class_interface_count;
+     char_u    **class_interfaces;     // allocated array of names
+ 
      // class members: "static varname"
      int               class_class_member_count;
      ocmember_T        *class_class_members;   // allocated
*** ../vim-9.0.1151/src/eval.c  2023-01-05 13:16:00.304020639 +0000
--- src/eval.c  2023-01-06 16:26:48.483175162 +0000
***************
*** 5676,5682 ****
        case VAR_CLASS:
            {
                class_T *cl = tv->vval.v_class;
!               if (cl != NULL && cl->class_copyID != copyID)
                {
                    cl->class_copyID = copyID;
                    for (int i = 0; !abort
--- 5676,5683 ----
        case VAR_CLASS:
            {
                class_T *cl = tv->vval.v_class;
!               if (cl != NULL && cl->class_copyID != copyID
!                                 && (cl->class_flags && CLASS_INTERFACE) == 0)
                {
                    cl->class_copyID = copyID;
                    for (int i = 0; !abort
*** ../vim-9.0.1151/src/testdir/test_vim9_class.vim     2023-01-05 
19:59:14.007418126 +0000
--- src/testdir/test_vim9_class.vim     2023-01-06 18:35:33.470596207 +0000
***************
*** 612,616 ****
--- 612,669 ----
    v9.CheckScriptFailure(lines, 'E1345: Not a valid command in an interface: 
return 5')
  enddef
  
+ def Test_class_implements_interface()
+   var lines =<< trim END
+       vim9script
+ 
+       interface Some
+         static count: number
+         def Method(nr: number)
+       endinterface
+ 
+       class SomeImpl implements Some
+         static count: number
+         def Method(nr: number)
+           echo nr
+         enddef
+       endclass
+   END
+   v9.CheckScriptSuccess(lines)
+ 
+   lines =<< trim END
+       vim9script
+ 
+       interface Some
+         static counter: number
+         def Method(nr: number)
+       endinterface
+ 
+       class SomeImpl implements Some
+         static count: number
+         def Method(nr: number)
+           echo nr
+         enddef
+       endclass
+   END
+   v9.CheckScriptFailure(lines, 'E1348: Member "counter" of interface "Some" 
not implemented')
+ 
+   lines =<< trim END
+       vim9script
+ 
+       interface Some
+         static count: number
+         def Methods(nr: number)
+       endinterface
+ 
+       class SomeImpl implements Some
+         static count: number
+         def Method(nr: number)
+           echo nr
+         enddef
+       endclass
+   END
+   v9.CheckScriptFailure(lines, 'E1349: Function "Methods" of interface "Some" 
not implemented')
+ enddef
+ 
  
  " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
*** ../vim-9.0.1151/src/version.c       2023-01-05 20:14:39.263710543 +0000
--- src/version.c       2023-01-06 18:21:33.688692756 +0000
***************
*** 697,698 ****
--- 697,700 ----
  {   /* Add new patch number below this line */
+ /**/
+     1152,
  /**/

-- 
CART DRIVER: Bring out your dead!
   There are legs stick out of windows and doors.  Two MEN are fighting in the
   mud - covered from head to foot in it.  Another MAN is on his hands in
   knees shovelling mud into his mouth.  We just catch sight of a MAN falling
   into a well.
                 "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD

 /// 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/20230106184326.27FCA1C143C%40moolenaar.net.

Raspunde prin e-mail lui