]> granicus.if.org Git - vim/commitdiff
patch 9.0.1150: :interface is not implemented yet v9.0.1150
authorBram Moolenaar <Bram@vim.org>
Thu, 5 Jan 2023 19:59:18 +0000 (19:59 +0000)
committerBram Moolenaar <Bram@vim.org>
Thu, 5 Jan 2023 19:59:18 +0000 (19:59 +0000)
Problem:    :interface is not implemented yet.
Solution:   Implement the basics of :interface.

12 files changed:
src/errors.h
src/ex_cmds.h
src/ex_docmd.c
src/proto/userfunc.pro
src/proto/vim9class.pro
src/testdir/test_vim9_class.vim
src/userfunc.c
src/version.c
src/vim.h
src/vim9class.c
src/vim9compile.c
src/vim9execute.c

index 581470803682527469cab2649c7cb0e4b4bfa96d..b86b1c33f29b2b482ba6121d991cb50ccdcb3872 100644 (file)
@@ -3346,8 +3346,8 @@ EXTERN char e_not_allowed_to_add_or_remove_entries_str[]
 #ifdef FEAT_EVAL
 EXTERN char e_class_name_must_start_with_uppercase_letter_str[]
        INIT(= N_("E1314: Class name must start with an uppercase letter: %s"));
-EXTERN char e_white_space_required_after_class_name_str[]
-       INIT(= N_("E1315: White space required after class name: %s"));
+EXTERN char e_white_space_required_after_name_str[]
+       INIT(= N_("E1315: White space required after name: %s"));
 EXTERN char e_class_can_only_be_defined_in_vim9_script[]
        INIT(= N_("E1316: Class can only be defined in Vim9 script"));
 EXTERN char e_invalid_object_member_declaration_str[]
@@ -3406,4 +3406,12 @@ EXTERN char e_argument_already_declared_in_class_str[]
        INIT(= N_("E1340: Argument already declared in the class: %s"));
 EXTERN char e_variable_already_declared_in_class_str[]
        INIT(= N_("E1341: Variable already declared in the class: %s"));
+EXTERN char e_interface_can_only_be_defined_in_vim9_script[]
+       INIT(= N_("E1342: Interface can only be defined in Vim9 script"));
+EXTERN char e_interface_name_must_start_with_uppercase_letter_str[]
+       INIT(= N_("E1343: Interface name must start with an uppercase letter: %s"));
+EXTERN char e_cannot_initialize_member_in_interface[]
+       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"));
 #endif
index c6f85ce104758832ee59020848e075b87294de3d..1c71bd31a34f49752707f84c7da14ec0abf69554 100644 (file)
@@ -756,7 +756,7 @@ EXCMD(CMD_inoremenu,        "inoremenu",    ex_menu,
 EXCMD(CMD_intro,       "intro",        ex_intro,
        EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK,
        ADDR_NONE),
-EXCMD(CMD_interface,   "interface",    ex_interface,
+EXCMD(CMD_interface,   "interface",    ex_class,
        EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK,
        ADDR_NONE),
 EXCMD(CMD_isearch,     "isearch",      ex_findpat,
index 4755952a518639f9122545417c483e70c27e1186..64ab247846657a8d55fa4e887c04833b85912a3c 100644 (file)
@@ -288,7 +288,6 @@ static void ex_tag_cmd(exarg_T *eap, char_u *name);
 # define ex_execute            ex_ni
 # define ex_finally            ex_ni
 # define ex_incdec             ex_ni
-# define ex_interface          ex_ni
 # define ex_finish             ex_ni
 # define ex_function           ex_ni
 # define ex_if                 ex_ni
index e3470f68c7fa087410ddc77197b0d773150ecbcc..a87113faa248f61ebb96be23c567e043a97de43c 100644 (file)
@@ -46,7 +46,7 @@ char_u *get_scriptlocal_funcname(char_u *funcname);
 char_u *alloc_printable_func_name(char_u *fname);
 char_u *save_function_name(char_u **name, int *is_global, int skip, int flags, funcdict_T *fudi);
 void list_functions(regmatch_T *regmatch);
-ufunc_T *define_function(exarg_T *eap, char_u *name_arg, garray_T *lines_to_free, int in_class);
+ufunc_T *define_function(exarg_T *eap, char_u *name_arg, garray_T *lines_to_free, int class_flags);
 void ex_function(exarg_T *eap);
 ufunc_T *find_func_by_name(char_u *name, compiletype_T *compile_type);
 void ex_defcompile(exarg_T *eap);
index 66c6107a1accfe2867d79e06fe7a80228da062e3..35485ed005be3ede5ff4681da399b1d5f17ff13e 100644 (file)
@@ -1,7 +1,6 @@
 /* vim9class.c */
 void ex_class(exarg_T *eap);
 type_T *class_member_type(class_T *cl, char_u *name, char_u *name_end, int *member_idx);
-void ex_interface(exarg_T *eap);
 void ex_enum(exarg_T *eap);
 void ex_type(exarg_T *eap);
 int class_object_index(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int verbose);
index b98b96f58e89c497e3433ac05380ba3cb58e4845..8c37a609062550aabd1c12fcec14acd12f737af5 100644 (file)
@@ -552,5 +552,65 @@ def Test_class_object_to_string()
   v9.CheckScriptSuccess(lines)
 enddef
 
+def Test_interface_basics()
+  var lines =<< trim END
+      vim9script
+      interface Something
+        this.value: string
+        static count: number
+        def GetCount(): number
+      endinterface
+  END
+  v9.CheckScriptSuccess(lines)
+
+  lines =<< trim END
+      interface SomethingWrong
+        static count = 7
+      endinterface
+  END
+  v9.CheckScriptFailure(lines, 'E1342:')
+
+  lines =<< trim END
+      vim9script
+
+      interface Some
+        static count: number
+        def Method(count: number)
+      endinterface
+  END
+  # TODO: this should give an error for "count" shadowing
+  v9.CheckScriptSuccess(lines)
+
+  lines =<< trim END
+      vim9script
+      interface somethingWrong
+        static count = 7
+      endinterface
+  END
+  v9.CheckScriptFailure(lines, 'E1343: Interface name must start with an uppercase letter: somethingWrong')
+
+  lines =<< trim END
+      vim9script
+      interface SomethingWrong
+        this.value: string
+        static count = 7
+        def GetCount(): number
+      endinterface
+  END
+  v9.CheckScriptFailure(lines, 'E1344:')
+
+  lines =<< trim END
+      vim9script
+      interface SomethingWrong
+        this.value: string
+        static count: number
+        def GetCount(): number
+          return 5
+        enddef
+      endinterface
+  END
+  v9.CheckScriptFailure(lines, 'E1345: Not a valid command in an interface: return 5')
+enddef
+
 
 " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
index 46b6c91778882f1d449dd94029442aad30319ee4..032c99cff02e658b86cf4933ddc26da74ad67491 100644 (file)
@@ -215,7 +215,7 @@ get_function_args(
     garray_T   *default_args,
     int                skip,
     exarg_T    *eap,           // can be NULL
-    int                in_class,       // TRUE when inside a class
+    int                in_class,       // non-zero when inside a class or interface
     garray_T   *newlines,      // function body lines
     garray_T   *lines_to_free)
 {
@@ -4462,7 +4462,9 @@ list_functions(regmatch_T *regmatch)
  * When "name_arg" is not NULL this is a nested function, using "name_arg" for
  * the function name.
  * "lines_to_free" is a list of strings to be freed later.
- * If "in_class" is TRUE then the function is defined inside a class.
+ * If "class_flags" has CF_CLASS then the function is defined inside a class.
+ * With CF_INTERFACE the function is define inside an interface, only the
+ * ":def"/":function" line is expected, no function body.
  * Returns a pointer to the function or NULL if no function defined.
  */
     ufunc_T *
@@ -4470,7 +4472,7 @@ define_function(
        exarg_T     *eap,
        char_u      *name_arg,
        garray_T    *lines_to_free,
-       int         in_class)
+       int         class_flags)
 {
     int                j;
     int                c;
@@ -4545,7 +4547,7 @@ define_function(
 
     /*
      * Get the function name.  There are these situations:
-     * func        normal function name, also when "in_class" is TRUE
+     * func        normal function name, also when "class_flags" is non-zero
      *             "name" == func, "fudi.fd_dict" == NULL
      * dict.func    new dictionary entry
      *             "name" == NULL, "fudi.fd_dict" set,
@@ -4586,7 +4588,7 @@ define_function(
        }
 
        int tfn_flags = TFN_NO_AUTOLOAD | TFN_NEW_FUNC
-                                              | (in_class ? TFN_IN_CLASS : 0);
+                                      | (class_flags != 0 ? TFN_IN_CLASS : 0);
        name = save_function_name(&p, &is_global, eap->skip, tfn_flags, &fudi);
        paren = (vim_strchr(p, '(') != NULL);
        if (name == NULL && (fudi.fd_dict == NULL || !paren) && !eap->skip)
@@ -4789,7 +4791,7 @@ define_function(
     if (get_function_args(&p, ')', &newargs,
                         eap->cmdidx == CMD_def ? &argtypes : NULL, FALSE,
                         NULL, &varargs, &default_args, eap->skip,
-                        eap, in_class, &newlines, lines_to_free) == FAIL)
+                        eap, class_flags, &newlines, lines_to_free) == FAIL)
        goto errret_2;
     whitep = p;
 
@@ -4899,7 +4901,9 @@ define_function(
 
     // Do not define the function when getting the body fails and when
     // skipping.
-    if (get_function_body(eap, &newlines, line_arg, lines_to_free) == FAIL
+    if (((class_flags & CF_INTERFACE) == 0
+               && get_function_body(eap, &newlines, line_arg, lines_to_free)
+                                                                      == FAIL)
            || eap->skip)
        goto erret;
 
@@ -4934,7 +4938,7 @@ define_function(
        if (name == NULL)
            goto erret;
     }
-    else if (!in_class)
+    else if (class_flags == 0)
     {
        hashtab_T       *ht;
        char_u          *find_name = name;
@@ -5159,7 +5163,7 @@ define_function(
            hi = hash_find(&func_hashtab, name);
            hi->hi_key = UF2HIKEY(fp);
        }
-       else if (!in_class && hash_add(&func_hashtab,
+       else if (class_flags == 0 && hash_add(&func_hashtab,
                                         UF2HIKEY(fp), "add function") == FAIL)
        {
            free_fp = TRUE;
@@ -5251,7 +5255,7 @@ ex_function(exarg_T *eap)
     garray_T lines_to_free;
 
     ga_init2(&lines_to_free, sizeof(char_u *), 50);
-    (void)define_function(eap, NULL, &lines_to_free, FALSE);
+    (void)define_function(eap, NULL, &lines_to_free, 0);
     ga_clear_strings(&lines_to_free);
 }
 
index d90b673d85d566577a3f1188f19785655a50ef18..8177cabf1b77b081f52b9fc7333b007bac56acfe 100644 (file)
@@ -695,6 +695,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1150,
 /**/
     1149,
 /**/
index f197fda96074d1db9f790f0ab8435f8577914461..33122d7b5650e20463006d6c79576a18ba5b2827 100644 (file)
--- a/src/vim.h
+++ b/src/vim.h
@@ -2854,4 +2854,8 @@ long elapsed(DWORD start_tick);
 
 #define MAX_LSHIFT_BITS (varnumber_T)((sizeof(uvarnumber_T) * 8) - 1)
 
+// Flags used by "class_flags" of define_function()
+#define CF_CLASS       1       // inside a class
+#define CF_INTERFACE   2       // inside an interface
+
 #endif // VIM__H
index 2717a7905a60bcbf4615600a598f49dae6edbf50..682211a59f1c7472f0b122ae6ff9debffc989c0b 100644 (file)
@@ -26,7 +26,7 @@
  * Returns OK or FAIL.  When OK then "varname_end" is set to just after the
  * variable name and "type_ret" is set to the decleared or detected type.
  * "init_expr" is set to the initialisation expression (allocated), if there is
- * one.
+ * one.  For an interface "init_expr" is NULL.
  */
     static int
 parse_member(
@@ -119,7 +119,14 @@ parse_member(
 
     *type_ret = type;
     if (expr_end > expr_start)
+    {
+       if (init_expr == NULL)
+       {
+           emsg(_(e_cannot_initialize_member_in_interface));
+           return FAIL;
+       }
        *init_expr = vim_strnsave(expr_start, expr_end - expr_start);
+    }
     return OK;
 }
 
@@ -175,15 +182,21 @@ add_members_to_class(
 
 /*
  * Handle ":class" and ":abstract class" up to ":endclass".
+ * Handle ":interface" up to ":endinterface".
  */
     void
 ex_class(exarg_T *eap)
 {
+    int is_class = eap->cmdidx == CMD_class;  // FALSE for :interface
+
     if (!current_script_is_vim9()
                || (cmdmod.cmod_flags & CMOD_LEGACY)
                || !getline_equal(eap->getline, eap->cookie, getsourceline))
     {
-       emsg(_(e_class_can_only_be_defined_in_vim9_script));
+       if (is_class)
+           emsg(_(e_class_can_only_be_defined_in_vim9_script));
+       else
+           emsg(_(e_interface_can_only_be_defined_in_vim9_script));
        return;
     }
 
@@ -201,13 +214,17 @@ ex_class(exarg_T *eap)
 
     if (!ASCII_ISUPPER(*arg))
     {
-       semsg(_(e_class_name_must_start_with_uppercase_letter_str), arg);
+       if (is_class)
+           semsg(_(e_class_name_must_start_with_uppercase_letter_str), arg);
+       else
+           semsg(_(e_interface_name_must_start_with_uppercase_letter_str),
+                                                                         arg);
        return;
     }
     char_u *name_end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
     if (!IS_WHITE_OR_NUL(*name_end))
     {
-       semsg(_(e_white_space_required_after_class_name_str), arg);
+       semsg(_(e_white_space_required_after_name_str), arg);
        return;
     }
 
@@ -239,7 +256,8 @@ ex_class(exarg_T *eap)
     ga_init2(&objmethods, sizeof(ufunc_T *), 10);
 
     /*
-     * Go over the body of the class until "endclass" is found.
+     * Go over the body of the class/interface until "endclass" or
+     * "endinterface" is found.
      */
     char_u *theline = NULL;
     int success = FALSE;
@@ -262,9 +280,10 @@ ex_class(exarg_T *eap)
        }
 
        char_u *p = line;
-       if (checkforcmd(&p, "endclass", 4))
+       char *end_name = is_class ? "endclass" : "endinterface";
+       if (checkforcmd(&p, end_name, is_class ? 4 : 5))
        {
-           if (STRNCMP(line, "endclass", 8) != 0)
+           if (STRNCMP(line, end_name, is_class ? 8 : 12) != 0)
                semsg(_(e_command_cannot_be_shortened_str), line);
            else if (*p == '|' || !ends_excmd2(line, p))
                semsg(_(e_trailing_characters_str), p);
@@ -272,6 +291,12 @@ ex_class(exarg_T *eap)
                success = TRUE;
            break;
        }
+       char *wrong_name = is_class ? "endinterface" : "endclass";
+       if (checkforcmd(&p, wrong_name, is_class ? 5 : 4))
+       {
+           semsg(_(e_invalid_command_str), line);
+           break;
+       }
 
        int has_public = FALSE;
        if (checkforcmd(&p, "public", 3))
@@ -320,7 +345,8 @@ ex_class(exarg_T *eap)
            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)
+                         &varname_end, &type_list, &type,
+                         is_class ? &init_expr: NULL) == FAIL)
                break;
            if (add_member(&objmembers, varname, varname_end,
                                          has_public, type, init_expr) == FAIL)
@@ -358,7 +384,8 @@ ex_class(exarg_T *eap)
            ea.cookie = eap->cookie;
 
            ga_init2(&lines_to_free, sizeof(char_u *), 50);
-           ufunc_T *uf = define_function(&ea, NULL, &lines_to_free, TRUE);
+           ufunc_T *uf = define_function(&ea, NULL, &lines_to_free,
+                                          is_class ? CF_CLASS : CF_INTERFACE);
            ga_clear_strings(&lines_to_free);
 
            if (uf != NULL)
@@ -389,7 +416,8 @@ ex_class(exarg_T *eap)
            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)
+                     &varname_end, &type_list, &type,
+                     is_class ? &init_expr : NULL) == FAIL)
                break;
            if (add_member(&classmembers, varname, varname_end,
                                      has_public, type, init_expr) == FAIL)
@@ -401,7 +429,10 @@ ex_class(exarg_T *eap)
 
        else
        {
-           semsg(_(e_not_valid_command_in_class_str), line);
+           if (is_class)
+               semsg(_(e_not_valid_command_in_class_str), line);
+           else
+               semsg(_(e_not_valid_command_in_interface_str), line);
            break;
        }
     }
@@ -415,6 +446,9 @@ ex_class(exarg_T *eap)
        cl = ALLOC_CLEAR_ONE(class_T);
        if (cl == NULL)
            goto cleanup;
+       if (!is_class)
+           cl->class_flags = CLASS_INTERFACE;
+
        cl->class_refcount = 1;
        cl->class_name = vim_strnsave(arg, name_end - arg);
        if (cl->class_name == NULL)
@@ -429,7 +463,7 @@ ex_class(exarg_T *eap)
                                    &cl->class_obj_member_count) == FAIL)
            goto cleanup;
 
-       if (cl->class_class_member_count > 0)
+       if (is_class && cl->class_class_member_count > 0)
        {
            // Allocate a typval for each class member and initialize it.
            cl->class_members_tv = ALLOC_CLEAR_MULT(typval_T,
@@ -491,7 +525,8 @@ ex_class(exarg_T *eap)
            garray_T lines_to_free;
            ga_init2(&lines_to_free, sizeof(char_u *), 50);
 
-           ufunc_T *nf = define_function(&fea, NULL, &lines_to_free, TRUE);
+           ufunc_T *nf = define_function(&fea, NULL, &lines_to_free,
+                                          is_class ? CF_CLASS : CF_INTERFACE);
 
            ga_clear_strings(&lines_to_free);
            vim_free(fga.ga_data);
@@ -633,15 +668,6 @@ class_member_type(
     return &t_any;
 }
 
-/*
- * Handle ":interface" up to ":endinterface".
- */
-    void
-ex_interface(exarg_T *eap UNUSED)
-{
-    // TODO
-}
-
 /*
  * Handle ":enum" up to ":endenum".
  */
index 1f264371216c55b6b57fb4d8b5d1da0b28eaf246..d8e4ae6ed547611b48955db68b895cb020cc2367 100644 (file)
@@ -991,7 +991,7 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx, garray_T *lines_to_free)
     int save_KeyTyped = KeyTyped;
     KeyTyped = FALSE;
 
-    ufunc = define_function(eap, lambda_name, lines_to_free, FALSE);
+    ufunc = define_function(eap, lambda_name, lines_to_free, 0);
 
     KeyTyped = save_KeyTyped;
 
index 087a8f4bd5a2829247da3ab416903cc565dbb405..3e5bbe21e450bd61b16f20b21ca6bda3f6331e38 100644 (file)
@@ -4280,7 +4280,7 @@ exec_instructions(ectx_T *ectx)
                    CLEAR_FIELD(ea);
                    ea.cmd = ea.arg = iptr->isn_arg.string;
                    ga_init2(&lines_to_free, sizeof(char_u *), 50);
-                   define_function(&ea, NULL, &lines_to_free, FALSE);
+                   define_function(&ea, NULL, &lines_to_free, 0);
                    ga_clear_strings(&lines_to_free);
                }
                break;