]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.1.0219: Vim9: No enum support v9.1.0219
authorYegappan Lakshmanan <yegappan@yahoo.com>
Thu, 28 Mar 2024 09:36:42 +0000 (10:36 +0100)
committerChristian Brabandt <cb@256bit.org>
Thu, 28 Mar 2024 09:38:28 +0000 (10:38 +0100)
Problem:  No enum support
Solution: Implement enums for Vim9 script
          (Yegappan Lakshmanan)

closes: #14224

Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
25 files changed:
runtime/doc/builtin.txt
runtime/doc/eval.txt
runtime/doc/tags
runtime/doc/todo.txt
runtime/doc/version9.txt
runtime/doc/vim9class.txt
src/errors.h
src/eval.c
src/evalfunc.c
src/evalvars.c
src/ex_cmds.h
src/proto/vim9class.pro
src/structs.h
src/testdir/Make_all.mak
src/testdir/test_vim9_class.vim
src/testdir/test_vim9_enum.vim [new file with mode: 0644]
src/testing.c
src/typval.c
src/version.c
src/vim.h
src/vim9class.c
src/vim9compile.c
src/vim9execute.c
src/vim9expr.c
src/vim9type.c

index ec702201d5937887346e4d8a538997b8ab43a2ea..1f7a4f1bd76e0fbc2f7ae07d334a83503baa13b5 100644 (file)
@@ -1,4 +1,4 @@
-*builtin.txt*  For Vim version 9.1.  Last change: 2024 Mar 23
+*builtin.txt*  For Vim version 9.1.  Last change: 2024 Mar 28
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -9598,6 +9598,8 @@ string({expr})    Return {expr} converted to a String.  If {expr} is a Number,
                        Dictionary      {key: value, key: value}
                        Class           class SomeName
                        Object          object of SomeName {lnum: 1, col: 3}
+                       Enum            enum EnumName
+                       EnumValue       enum.value
 
                When a |List| or |Dictionary| has a recursive reference it is
                replaced by "[...]" or "{...}".  Using eval() on the result
@@ -10461,6 +10463,8 @@ type({expr})    The result is a Number representing the type of {expr}.
                        Class:     12  |v:t_class|
                        Object:    13  |v:t_object|
                        Typealias: 14  |v:t_typealias|
+                       Enum:      15  |v:t_enum|
+                       EnumValue: 16  |v:t_enumvalue|
                For backward compatibility, this method can be used: >
                        :if type(myvar) == type(0)
                        :if type(myvar) == type("")
index f2ff3a883f974582943575b9c015f0765345bca1..fd37d974ee70e33d4c54a7ab2354ab717d090bf8 100644 (file)
@@ -1,4 +1,4 @@
-*eval.txt*     For Vim version 9.1.  Last change: 2024 Mar 20
+*eval.txt*     For Vim version 9.1.  Last change: 2024 Mar 28
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -2601,6 +2601,10 @@ v:t_class        Value of |class| type.  Read-only.  See: |type()|
 v:t_object     Value of |object| type.  Read-only.  See: |type()|
                                        *v:t_typealias* *t_typealias-variable*
 v:t_typealias  Value of |typealias| type.  Read-only.  See: |type()|
+                                       *v:t_enum* *t_enum-variable*
+v:t_enum       Value of |enum| type.  Read-only.  See: |type()|
+                                       *v:t_enumvalue* *t_enumvalue-variable*
+v:t_enumvalue  Value of |enumvalue| type.  Read-only.  See: |type()|
 
                                *v:termresponse* *termresponse-variable*
 v:termresponse The escape sequence returned by the terminal for the |t_RV|
index 4ff7095e0b8b4478827802ba98ab646adc61f062..528538ebe9fc378019a5f3a944d24257a37387c8 100644 (file)
@@ -4520,7 +4520,20 @@ E1410    vim9class.txt   /*E1410*
 E1411  vim9class.txt   /*E1411*
 E1412  vim9class.txt   /*E1412*
 E1413  vim9class.txt   /*E1413*
+E1414  vim9class.txt   /*E1414*
+E1415  vim9class.txt   /*E1415*
+E1416  vim9class.txt   /*E1416*
+E1417  vim9class.txt   /*E1417*
+E1418  vim9class.txt   /*E1418*
+E1419  vim9class.txt   /*E1419*
 E142   message.txt     /*E142*
+E1420  vim9class.txt   /*E1420*
+E1421  vim9class.txt   /*E1421*
+E1422  vim9class.txt   /*E1422*
+E1423  vim9class.txt   /*E1423*
+E1424  vim9class.txt   /*E1424*
+E1425  vim9class.txt   /*E1425*
+E1426  vim9class.txt   /*E1426*
 E143   autocmd.txt     /*E143*
 E144   various.txt     /*E144*
 E145   starting.txt    /*E145*
@@ -6874,6 +6887,12 @@ encryption       editing.txt     /*encryption*
 end    intro.txt       /*end*
 end-of-file    pattern.txt     /*end-of-file*
 enlightened-terminal   syntax.txt      /*enlightened-terminal*
+enum   vim9class.txt   /*enum*
+enum-constructor       vim9class.txt   /*enum-constructor*
+enum-name      vim9class.txt   /*enum-name*
+enum-ordinal   vim9class.txt   /*enum-ordinal*
+enum-values    vim9class.txt   /*enum-values*
+enumvalue      vim9class.txt   /*enumvalue*
 environ()      builtin.txt     /*environ()*
 eol-and-eof    editing.txt     /*eol-and-eof*
 erlang.vim     syntax.txt      /*erlang.vim*
@@ -10290,6 +10309,8 @@ t_dl    term.txt        /*t_dl*
 t_ds   term.txt        /*t_ds*
 t_ed   version4.txt    /*t_ed*
 t_el   version4.txt    /*t_el*
+t_enum-variable        eval.txt        /*t_enum-variable*
+t_enumvalue-variable   eval.txt        /*t_enumvalue-variable*
 t_f1   version4.txt    /*t_f1*
 t_f10  version4.txt    /*t_f10*
 t_f2   version4.txt    /*t_f2*
@@ -10863,6 +10884,8 @@ v:t_bool        eval.txt        /*v:t_bool*
 v:t_channel    eval.txt        /*v:t_channel*
 v:t_class      eval.txt        /*v:t_class*
 v:t_dict       eval.txt        /*v:t_dict*
+v:t_enum       eval.txt        /*v:t_enum*
+v:t_enumvalue  eval.txt        /*v:t_enumvalue*
 v:t_float      eval.txt        /*v:t_float*
 v:t_func       eval.txt        /*v:t_func*
 v:t_job        eval.txt        /*v:t_job*
index 2b4a70a3a634a20530b99fd46a5d4a02867d2f3f..8712008cfea2f7c7c4bbac303f288692529af9fc 100644 (file)
@@ -1,4 +1,4 @@
-*todo.txt*      For Vim version 9.1.  Last change: 2024 Mar 03
+*todo.txt*      For Vim version 9.1.  Last change: 2024 Mar 28
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -144,7 +144,6 @@ Further Vim9 improvements:
   - More efficient way for interface member index than iterating over list?
   - a variant of type() that returns a different type for each class?
       list<number> and list<string> should also differ.
-- implement :enum
 - Promise class, could be used to wait on a popup close callback?
 - class local to a function
 - Use Vim9 for more runtime files.
index 8ea30a4c8a074102dced77ec5e81f6ce6c0e8cd3..587cdd5322b7c1bbe4ba45b1d2542cffd872dc1d 100644 (file)
@@ -41548,6 +41548,8 @@ Vim9 script
 Add support for internal builtin functions with vim9 objects, see
 |builtin-object-methods|
 
+Enum support for Vim9 script |:enum|
+
 Other improvements                             *new-other-9.2*
 ------------------
 
index a00a5b787de8d4985238aa5a63c01b75aac1e011..8820d77b54b5a45f1d6eab70c0ea47738c65cfb8 100644 (file)
@@ -1,4 +1,4 @@
-*vim9class.txt*        For Vim version 9.1.  Last change: 2024 Mar 03
+*vim9class.txt*        For Vim version 9.1.  Last change: 2024 Mar 28
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -904,19 +904,125 @@ aliased: >
 
 8.  Enum                                       *Vim9-enum* *:enum* *:endenum*
 
-{not implemented yet}
-
+                                               *enum* *E1418* *E1419* *E1420*
 An enum is a type that can have one of a list of values.  Example: >
 
-       :enum Color
-               White
-               Red
-               Green
-               Blue
-               Black
-       :endenum
+    :enum Color
+       White,
+       Red,
+       Green, Blue, Black
+    :endenum
+<
+                                               *enumvalue* *E1422*
+The enum values are separated by commas.  More than one enum value can be
+listed in a single line.  The final enum value should not be followed by a
+comma.
+
+An enum value is accessed using the enum name followed by the value name: >
+
+    var a: Color = Color.Blue
+<
+Enums are treated as classes, where each enum value is essentially an instance
+of that class.  Unlike typical object instantiation with the |new()| method,
+enum instances cannot be created this way.
+
+An enum can only be defined in a |Vim9| script file.   *E1414*
+An enum cannot be defined inside a function.
+
+                                                       *E1415*
+An enum name must start with an uppercase letter.  The name of an enum value
+in an enum can start with an upper or lowercase letter.
+
+                                                       *E1416*
+An enum can implement an interface but cannot extend a class: >
+
+    enum MyEnum implements MyIntf
+       Value1,
+       Value2
+
+       def SomeMethod()
+       enddef
+    endenum
+<
+                                                       *enum-constructor*
+The enum value objects in an enum are constructed like any other objects using
+the |new()| method.  Arguments can be passed to the enum constructor by
+specifying them after the enum value name, just like calling a function.  The
+default constructor doesn't have any arguments.
+
+                                                       *E1417*
+An enum can contain class variables, class methods, object variables and
+object methods.  The methods in an enum cannot be |:abstract| methods.
+
+The following example shows an enum with object variables and methods: >
+
+    vim9script
+    enum Planet
+       Earth(1, false),
+       Jupiter(95, true),
+       Saturn(146, true)
+
+       var moons: number
+       var has_rings: bool
+       def GetMoons(): number
+           return this.moons
+       enddef
+    endenum
+    echo Planet.Jupiter.GetMoons()
+    echo Planet.Earth.has_rings
+<
+                                               *E1421* *E1423* *E1424* *E1425*
+Enums and their values are immutable. They cannot be modified after
+declaration and cannot be utilized as numerical or string types.
+
+                                               *enum-name*
+Each enum value object has a "name" instance variable which contains the name
+of the enum value.  This is a readonly variable.
+
+                                               *enum-ordinal* *E1426*
+Each enum value has an associated ordinal number starting with 0.  The ordinal
+number of an enum value can be accessed using the "ordinal" instance variable.
+This is a readonly variable.  Note that if the ordering of the enum values in
+an enum is changed, then their ordinal values will also change.
+
+                                               *enum-values*
+All the values in an enum can be accessed using the "values" class variable
+which is a List of the enum objects.  This is a readonly variable.
+
+Example: >
+    enum Planet
+       Mercury,
+       Venus,
+       Earth
+    endenum
+
+    echo Planet.Mercury
+    echo Planet.Venus.name
+    echo Planet.Venus.ordinal
+    for p in Planet.values
+       # ...
+    endfor
+<
+An enum is a class with class variables for the enum value objects and object
+variables for the enum value name and the enum value ordinal: >
+
+    enum Planet
+       Mercury,
+       Venus
+    endenum
+<
+The above enum definition is equivalent to the following class definition: >
+
+    class Planet
+      public static final Mercury: Planet = Planet.new('Mercury', 0)
+      public static final Venus: Planet = Planet.new('Venus', 1)
 
+      public static const values: list<Planet> = [Planet.Mercury, Planet.Venus]
 
+      public const name: string
+      public const ordinal: number
+    endclass
+<
 ==============================================================================
 
 9.  Rationale
index 5dccc63b7a9c8b06b605ffa994d84b3199bdfa10..18d3f0cb6135a213ccc0f8412d19ec9e8089673f 100644 (file)
@@ -3585,8 +3585,38 @@ EXTERN char e_builtin_object_method_str_not_supported[]
        INIT(= N_("E1412: Builtin object method \"%s\" not supported"));
 EXTERN char e_builtin_class_method_not_supported[]
        INIT(= N_("E1413: Builtin class method not supported"));
-#endif
-// E1415 - E1499 unused (reserved for Vim9 class support)
+EXTERN char e_enum_can_only_be_defined_in_vim9_script[]
+       INIT(= N_("E1414: Enum can only be defined in Vim9 script"));
+EXTERN char e_enum_name_must_start_with_uppercase_letter_str[]
+       INIT(= N_("E1415: Enum name must start with an uppercase letter: %s"));
+EXTERN char e_enum_cannot_extend_class[]
+       INIT(= N_("E1416: Enum cannot extend a class or enum"));
+EXTERN char e_abstract_cannot_be_used_in_enum[]
+       INIT(= N_("E1417: Abstract cannot be used in an Enum"));
+EXTERN char e_invalid_enum_value_declaration_str[]
+       INIT(= N_("E1418: Invalid enum value declaration: %s"));
+EXTERN char e_not_valid_command_in_enum_str[]
+       INIT(= N_("E1419: Not a valid command in an Enum: %s"));
+EXTERN char e_missing_endenum[]
+       INIT(= N_("E1420: Missing :endenum"));
+EXTERN char e_using_enum_as_value_str[]
+       INIT(= N_("E1421: Enum \"%s\" cannot be used as a value"));
+EXTERN char e_enum_value_str_not_found_in_enum_str[]
+       INIT(= N_("E1422: Enum value \"%s\" not found in enum \"%s\""));
+EXTERN char e_enumvalue_str_cannot_be_modified[]
+       INIT(= N_("E1423: Enum value \"%s.%s\" cannot be modified"));
+EXTERN char e_using_enum_str_as_number[]
+       INIT(= N_("E1424: Using an Enum \"%s\" as a Number"));
+EXTERN char e_using_enum_str_as_string[]
+       INIT(= N_("E1425: Using an Enum \"%s\" as a String"));
+EXTERN char e_enum_str_ordinal_cannot_be_modified[]
+       INIT(= N_("E1426: Enum \"%s\" ordinal value cannot be modified"));
+EXTERN char e_enum_str_name_cannot_be_modified[]
+       INIT(= N_("E1427: Enum \"%s\" name cannot be modified"));
+EXTERN char e_duplicate_enum_str[]
+       INIT(= N_("E1428: Duplicate enum value: %s"));
+#endif
+// E1429 - E1499 unused (reserved for Vim9 class support)
 EXTERN char e_cannot_mix_positional_and_non_positional_str[]
        INIT(= N_("E1500: Cannot mix positional and non-positional arguments: %s"));
 EXTERN char e_fmt_arg_nr_unused_str[]
index 69b837402cb03ae4726db66d04c2bead2a130943..2647e7a5911540505c0b2705634abffca8143eb0 100644 (file)
@@ -1119,7 +1119,18 @@ get_lval_check_access(
                if (*p == '[' || *p == '.')
                    break;
                if ((flags & GLV_READ_ONLY) == 0)
-                   msg = e_variable_is_not_writable_str;
+               {
+                   if (IS_ENUM(cl))
+                   {
+                       if (om->ocm_type->tt_type == VAR_OBJECT)
+                           semsg(_(e_enumvalue_str_cannot_be_modified),
+                                   cl->class_name, om->ocm_name);
+                       else
+                           msg = e_variable_is_not_writable_str;
+                   }
+                   else
+                       msg = e_variable_is_not_writable_str;
+               }
                break;
            case VIM_ACCESS_ALL:
                break;
@@ -6310,9 +6321,15 @@ echo_string_core(
        case VAR_CLASS:
            {
                class_T *cl = tv->vval.v_class;
-               size_t len = 6 + (cl == NULL ? 9 : STRLEN(cl->class_name)) + 1;
+               char *s = "class";
+               if (IS_INTERFACE(cl))
+                   s = "interface";
+               else if (IS_ENUM(cl))
+                   s = "enum";
+               size_t len = STRLEN(s) + 1 +
+                   (cl == NULL ? 9 : STRLEN(cl->class_name)) + 1;
                r = *tofree = alloc(len);
-               vim_snprintf((char *)r, len, "class %s",
+               vim_snprintf((char *)r, len, "%s %s", s,
                            cl == NULL ? "[unknown]" : (char *)cl->class_name);
            }
            break;
index 14650caf6b2ed52b7c2f41c33eb6bc475434b478..8c27986b42df4fc60734150cbae4979b2909d37b 100644 (file)
@@ -11486,15 +11486,31 @@ f_type(typval_T *argvars, typval_T *rettv)
        case VAR_CHANNEL: n = VAR_TYPE_CHANNEL; break;
        case VAR_BLOB:    n = VAR_TYPE_BLOB; break;
        case VAR_INSTR:   n = VAR_TYPE_INSTR; break;
-       case VAR_CLASS:   n = VAR_TYPE_CLASS; break;
-       case VAR_OBJECT:  n = VAR_TYPE_OBJECT; break;
        case VAR_TYPEALIAS: n = VAR_TYPE_TYPEALIAS; break;
+       case VAR_CLASS:
+           {
+               class_T *cl = argvars[0].vval.v_class;
+               if (IS_ENUM(cl))
+                   n = VAR_TYPE_ENUM;
+               else
+                   n = VAR_TYPE_CLASS;
+               break;
+           }
+       case VAR_OBJECT:
+           {
+               class_T *cl = argvars[0].vval.v_object->obj_class;
+               if (IS_ENUM(cl))
+                   n = VAR_TYPE_ENUMVALUE;
+               else
+                   n = VAR_TYPE_OBJECT;
+               break;
+           }
        case VAR_UNKNOWN:
        case VAR_ANY:
        case VAR_VOID:
-            internal_error_no_abort("f_type(UNKNOWN)");
-            n = -1;
-            break;
+           internal_error_no_abort("f_type(UNKNOWN)");
+           n = -1;
+           break;
     }
     rettv->vval.v_number = n;
 }
index de9d5b26b33400336fedb071b0aea0eb6db7956d..f16d4757f2de8da10c4283d86c2e3e23e20a6a2a 100644 (file)
@@ -159,6 +159,8 @@ static struct vimvar
     {VV_NAME("maxcol",          VAR_NUMBER), NULL, VV_RO},
     {VV_NAME("python3_version",         VAR_NUMBER), NULL, VV_RO},
     {VV_NAME("t_typealias",     VAR_NUMBER), NULL, VV_RO},
+    {VV_NAME("t_enum",          VAR_NUMBER), NULL, VV_RO},
+    {VV_NAME("t_enumvalue",     VAR_NUMBER), NULL, VV_RO},
 };
 
 // shorthand
@@ -262,6 +264,8 @@ evalvars_init(void)
     set_vim_var_nr(VV_TYPE_CLASS,   VAR_TYPE_CLASS);
     set_vim_var_nr(VV_TYPE_OBJECT,  VAR_TYPE_OBJECT);
     set_vim_var_nr(VV_TYPE_TYPEALIAS,  VAR_TYPE_TYPEALIAS);
+    set_vim_var_nr(VV_TYPE_ENUM,  VAR_TYPE_ENUM);
+    set_vim_var_nr(VV_TYPE_ENUMVALUE,  VAR_TYPE_ENUMVALUE);
 
     set_vim_var_nr(VV_ECHOSPACE,    sc_col - 1);
 
index bd26e81dccb804e3cacd4cdec5b7add1429707bc..cb758aa7c37989f60717096481dbc6692f42ec18 100644 (file)
@@ -595,7 +595,7 @@ EXCMD(CMD_endwhile, "endwhile",     ex_endwhile,
 EXCMD(CMD_enew,                "enew",         ex_edit,
        EX_BANG|EX_TRLBAR,
        ADDR_NONE),
-EXCMD(CMD_enum,                "enum",         ex_enum,
+EXCMD(CMD_enum,                "enum",         ex_class,
        EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK|EX_EXPORT,
        ADDR_NONE),
 EXCMD(CMD_eval,                "eval",         ex_eval,
index 1ed175e69f8a72320a371cf6220ad58e7655e4d3..d3d3b99be3787c1f3061c60326d47a4e73f52f80 100644 (file)
@@ -3,6 +3,7 @@ int object_index_from_itf_index(class_T *itf, int is_method, int idx, class_T *c
 int is_valid_builtin_obj_methodname(char_u *funcname);
 ufunc_T *class_get_builtin_method(class_T *cl, class_builtin_T builtin_method, int *method_idx);
 void ex_class(exarg_T *eap);
+void enum_set_internal_obj_vars(class_T *en, object_T *enval);
 type_T *oc_member_type(class_T *cl, int is_object, char_u *name, char_u *name_end, int *member_idx);
 type_T *oc_member_type_by_idx(class_T *cl, int is_object, int member_idx);
 void ex_enum(exarg_T *eap);
index 47a0050ddbd9968c9fc210fa19b45f4e05fe5e94..0d3f60acb4fcb7d2c854121b60e9b2f073a96285 100644 (file)
@@ -1562,9 +1562,10 @@ struct itf2class_S {
     // array with ints follows
 };
 
-#define CLASS_INTERFACE            1
-#define CLASS_EXTENDED     2       // another class extends this one
-#define CLASS_ABSTRACT     4       // abstract class
+#define CLASS_INTERFACE            0x1
+#define CLASS_EXTENDED     0x2     // another class extends this one
+#define CLASS_ABSTRACT     0x4     // abstract class
+#define CLASS_ENUM         0x8     // enum
 
 // "class_T": used for v_class of typval of VAR_CLASS
 // Also used for an interface (class_flags has CLASS_INTERFACE).
@@ -1613,6 +1614,9 @@ struct class_S
     type_T     class_object_type;      // same as class_type but VAR_OBJECT
 };
 
+#define IS_INTERFACE(cl)       ((cl)->class_flags & CLASS_INTERFACE)
+#define IS_ENUM(cl)            ((cl)->class_flags & CLASS_ENUM)
+
 // Used for v_object of typval of VAR_OBJECT.
 // The member variables follow in an array of typval_T.
 struct object_S
index c4403e67ae6ff33a516553af887078987b04b80e..ab4d42a67cb4385eef77eec0ed2a3966a9e69206 100644 (file)
@@ -40,6 +40,7 @@ TEST_VIM9 = \
        test_vim9_class \
        test_vim9_cmd \
        test_vim9_disassemble \
+       test_vim9_enum \
        test_vim9_expr \
        test_vim9_fails \
        test_vim9_func \
@@ -53,6 +54,7 @@ TEST_VIM9_RES = \
        test_vim9_class.res \
        test_vim9_cmd.res \
        test_vim9_disassemble.res \
+       test_vim9_enum.res \
        test_vim9_expr.res \
        test_vim9_fails.res \
        test_vim9_func.res \
index d6c55bfffaaf35cbc36735c610b3ce8c2a1229d7..f0c6f0cd00e1d6084a39c6d742b4741f7b3bbe57 100644 (file)
@@ -2275,6 +2275,18 @@ def Test_interface_basics()
   v9.CheckScriptSuccess(lines)
 enddef
 
+" Test for using string() with an interface
+def Test_interface_to_string()
+  var lines =<< trim END
+    vim9script
+    interface Intf
+      def Method(nr: number)
+    endinterface
+    assert_equal("interface Intf", string(Intf))
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
 def Test_class_implements_interface()
   var lines =<< trim END
     vim9script
@@ -10391,6 +10403,23 @@ def Test_compound_op_in_objmethod_lambda()
   v9.CheckScriptSuccess(lines)
 enddef
 
+" Test for using test_refcount() with a class and an object
+def Test_class_object_refcount()
+  var lines =<< trim END
+    vim9script
+    class A
+    endclass
+    var a: A = A.new()
+    assert_equal(2, test_refcount(A))
+    assert_equal(1, test_refcount(a))
+    var b = a
+    assert_equal(2, test_refcount(A))
+    assert_equal(2, test_refcount(a))
+    assert_equal(2, test_refcount(b))
+  END
+  v9.CheckScriptSuccess(lines)
+enddef
+
 " call a lambda function in one object from another object
 def Test_lambda_invocation_across_classes()
   var lines =<< trim END
@@ -10420,4 +10449,18 @@ def Test_lambda_invocation_across_classes()
   v9.CheckScriptSuccess(lines)
 enddef
 
+" Test for using a class member which is an object of the current class
+def Test_current_class_object_class_member()
+  var lines =<< trim END
+    vim9script
+    class A
+      public static var obj1: A = A.new(10)
+      var n: number
+    endclass
+    defcompile
+    assert_equal(10, A.obj1.n)
+  END
+  v9.CheckScriptSuccess(lines)
+enddef
+
 " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
diff --git a/src/testdir/test_vim9_enum.vim b/src/testdir/test_vim9_enum.vim
new file mode 100644 (file)
index 0000000..6e10e0c
--- /dev/null
@@ -0,0 +1,1476 @@
+" Test Vim9 enums
+
+source check.vim
+import './vim9.vim' as v9
+
+" Test for parsing an enum definition
+def Test_enum_parse()
+  # enum supported only in a Vim9 script
+  var lines =<< trim END
+    enum Foo
+    endenum
+  END
+  v9.CheckSourceFailure(lines, 'E1414: Enum can only be defined in Vim9 script', 1)
+
+  # First character in an enum name should be capitalized.
+  lines =<< trim END
+    vim9script
+    enum foo
+    endenum
+  END
+  v9.CheckSourceFailure(lines, 'E1415: Enum name must start with an uppercase letter: foo', 2)
+
+  # Only alphanumeric characters are supported in an enum name
+  lines =<< trim END
+    vim9script
+    enum Foo@bar
+    endenum
+  END
+  v9.CheckSourceFailure(lines, 'E1315: White space required after name: Foo@bar', 2)
+
+  # Unsupported keyword (instead of enum)
+  lines =<< trim END
+    vim9script
+    noenum Something
+    endenum
+  END
+  v9.CheckSourceFailure(lines, 'E492: Not an editor command: noenum Something', 2)
+
+  # Only the complete word "enum" should be recognized
+  lines =<< trim END
+    vim9script
+    enums Something
+    endenum
+  END
+  v9.CheckSourceFailure(lines, 'E492: Not an editor command: enums Something', 2)
+
+  # The complete "endenum" should be specified.
+  lines =<< trim END
+    vim9script
+    enum Something
+    enden
+  END
+  v9.CheckSourceFailure(lines, 'E1065: Command cannot be shortened: enden', 3)
+
+  # Only the complete word "endenum" should be recognized
+  lines =<< trim END
+    vim9script
+    enum Something
+    endenums
+  END
+  v9.CheckSourceFailure(lines, 'E1420: Missing :endenum', 4)
+
+  # all lower case should be used for "enum"
+  lines =<< trim END
+    vim9script
+    Enum Something
+    endenum
+  END
+  v9.CheckSourceFailure(lines, 'E492: Not an editor command: Enum Something', 2)
+
+  # all lower case should be used for "endenum"
+  lines =<< trim END
+    vim9script
+    enum Something
+    Endenum
+  END
+  v9.CheckSourceFailure(lines, 'E1420: Missing :endenum', 4)
+
+  # Additional words after "endenum"
+  lines =<< trim END
+    vim9script
+    enum Something
+    endenum school's out
+  END
+  v9.CheckSourceFailure(lines, "E488: Trailing characters: school's out", 3)
+
+  # Additional commands after "endenum"
+  lines =<< trim END
+    vim9script
+    enum Something
+    endenum | echo 'done'
+  END
+  v9.CheckSourceFailure(lines, "E488: Trailing characters: | echo 'done'", 3)
+
+  # Try to define enum in a single command
+  lines =<< trim END
+    vim9script
+    enum Something | endenum
+  END
+  v9.CheckSourceFailure(lines, 'E1420: Missing :endenum', 3)
+
+  # Try to define an enum with the same name as an existing variable
+  lines =<< trim END
+    vim9script
+    var Something: list<number> = [1]
+    enum Something
+    endenum
+  END
+  v9.CheckSourceFailure(lines, 'E1041: Redefining script item: "Something"', 3)
+
+  # Unsupported special character following enum name
+  lines =<< trim END
+    vim9script
+    enum Foo
+      first,
+      second : 20
+    endenum
+  END
+  v9.CheckSourceFailure(lines, 'E1123: Missing comma before argument: : 20', 4)
+
+  # Try initializing an enum item with a number
+  lines =<< trim END
+    vim9script
+    enum Foo
+      first,
+      second = 2
+    endenum
+  END
+  v9.CheckSourceFailure(lines, 'E1123: Missing comma before argument: = 2', 4)
+
+  # Try initializing an enum item with a String
+  lines =<< trim END
+    vim9script
+    enum Foo
+      first,
+      second = 'second'
+    endenum
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, "E1123: Missing comma before argument: = 'second'", 4)
+
+  # Try initializing an enum item with a List
+  lines =<< trim END
+    vim9script
+    enum Foo
+      first,
+      second = []
+    endenum
+  END
+  v9.CheckSourceFailure(lines, 'E1123: Missing comma before argument: = []', 4)
+
+  # Use a colon after name
+  lines =<< trim END
+    vim9script
+    enum Foo
+
+      # first
+      first:
+      second
+    endenum
+  END
+  v9.CheckSourceFailure(lines, 'E1123: Missing comma before argument: :', 5)
+
+  # Use a '=='
+  lines =<< trim END
+    vim9script
+    enum Foo
+      first == 1
+    endenum
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1123: Missing comma before argument: == 1', 3)
+
+  # Missing comma after an enum item
+  lines =<< trim END
+    vim9script
+    enum Planet
+      mercury
+      venus
+    endenum
+  END
+  v9.CheckSourceFailure(lines, 'E1419: Not a valid command in an Enum: venus', 4)
+
+  # Comma at the beginning of an item
+  lines =<< trim END
+    vim9script
+    enum Planet
+      mercury
+      ,venus
+    endenum
+  END
+  v9.CheckSourceFailure(lines, 'E1419: Not a valid command in an Enum: ,venus', 4)
+  # Space before comma
+  lines =<< trim END
+    vim9script
+    enum Planet
+      mercury ,
+      venus
+    endenum
+  END
+  v9.CheckSourceFailure(lines, "E1068: No white space allowed before ','", 3)
+
+  # No space after comma
+  lines =<< trim END
+    vim9script
+    enum Planet
+      mercury,venus
+    endenum
+  END
+  v9.CheckSourceFailure(lines, "E1069: White space required after ',': mercury,venus", 3)
+
+  # no comma between items in the same line
+  lines =<< trim END
+    vim9script
+    enum Planet
+      mercury venus earth
+    endenum
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1123: Missing comma before argument: venus earth', 3)
+
+  # No space after an item and comment between items
+  lines =<< trim END
+    vim9script
+    enum Planet
+      mercury
+
+      # Venus
+      venus
+    endenum
+  END
+  v9.CheckSourceFailure(lines, 'E1419: Not a valid command in an Enum: venus', 6)
+
+  # Comma is supported for the last item
+  lines =<< trim END
+    vim9script
+    enum Planet
+      mercury,
+      venus,
+    endenum
+    var p: Planet
+    p = Planet.mercury
+    p = Planet.venus
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # invalid enum value declaration
+  lines =<< trim END
+    vim9script
+    enum Fruit
+      Apple,
+      $%@
+    endenum
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1418: Invalid enum value declaration: $%@', 4)
+
+  # Duplicate enum value
+  lines =<< trim END
+    vim9script
+    enum A
+      Foo,
+      Bar,
+      Foo
+    endenum
+  END
+  v9.CheckSourceFailure(lines, 'E1428: Duplicate enum value: Foo', 5)
+
+  # Duplicate enum value in the same line
+  lines =<< trim END
+    vim9script
+    enum A
+      Foo, Bar, Foo,
+      Bar
+    endenum
+  END
+  v9.CheckSourceFailure(lines, 'E1428: Duplicate enum value: Foo', 3)
+
+  # Try extending a class when defining an enum
+  lines =<< trim END
+    vim9script
+    class Foo
+    endclass
+    enum Bar extends Foo
+    endenum
+  END
+  v9.CheckSourceFailure(lines, 'E1416: Enum cannot extend a class or enum', 4)
+
+  # Try extending an enum
+  lines =<< trim END
+    vim9script
+    enum Foo
+    endenum
+    enum Bar extends Foo
+    endenum
+  END
+  v9.CheckSourceFailure(lines, 'E1416: Enum cannot extend a class or enum', 4)
+
+  # Try extending an enum using a class
+  lines =<< trim END
+    vim9script
+    enum Foo
+    endenum
+    class Bar extends Foo
+    endclass
+  END
+  v9.CheckSourceFailure(lines, 'E1354: Cannot extend Foo', 5)
+
+  # Try implementing an enum using a class
+  lines =<< trim END
+    vim9script
+    enum Foo
+    endenum
+    class Bar implements Foo
+    endclass
+  END
+  v9.CheckSourceFailure(lines, 'E1347: Not a valid interface: Foo', 5)
+
+  # abstract method is not supported in an enum
+  lines =<< trim END
+    vim9script
+    enum Foo
+      Apple
+      abstract def Bar()
+    endenum
+  END
+  v9.CheckSourceFailure(lines, 'E1417: Abstract cannot be used in an Enum', 4)
+
+  # Define an enum without any enum values but only with an object variable
+  lines =<< trim END
+    vim9script
+    enum Foo
+      final n: number = 10
+    endenum
+  END
+  v9.CheckSourceFailure(lines, 'E1123: Missing comma before argument: n: number = 10', 3)
+enddef
+
+def Test_basic_enum()
+  # Declare a simple enum
+  var lines =<< trim END
+    vim9script
+    enum Foo
+      apple,
+      orange
+    endenum
+    var a: Foo = Foo.apple
+    var b: Foo = Foo.orange
+    assert_equal(a, Foo.apple)
+    assert_equal(b, Foo.orange)
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Multiple enums in a single line
+  lines =<< trim END
+    vim9script
+    enum Foo
+      apple, orange
+    endenum
+    assert_equal('enum<Foo>', typename(Foo.apple))
+    assert_equal('enum<Foo>', typename(Foo.orange))
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Comments and empty lines are supported between enum items
+  lines =<< trim END
+    vim9script
+    enum Foo
+      # Apple
+      apple,
+
+      # Orange
+      orange
+    endenum
+    def Fn()
+      var a: Foo = Foo.apple
+      var b: Foo = Foo.orange
+      assert_equal(a, Foo.apple)
+      assert_equal(b, Foo.orange)
+    enddef
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Try using a non-existing enum value
+  lines =<< trim END
+    vim9script
+    enum Foo
+      apple,
+      orange
+    endenum
+    var a: Foo = Foo.pear
+  END
+  v9.CheckSourceFailure(lines, 'E1422: Enum value "pear" not found in enum "Foo"', 6)
+
+  # Enum function argument
+  lines =<< trim END
+    vim9script
+    enum Foo
+      apple,
+      orange
+    endenum
+    def Fn(a: Foo): Foo
+      return a
+    enddef
+    assert_equal(Foo.apple, Fn(Foo.apple))
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Enum function argument
+  lines =<< trim END
+    vim9script
+    enum Foo
+      apple,
+      orange
+    endenum
+    def Fn(a: Foo): Foo
+      return a
+    enddef
+    Fn({})
+  END
+  v9.CheckSourceFailure(lines, 'E1013: Argument 1: type mismatch, expected enum<Foo> but got dict<any>', 9)
+
+  # Returning an enum in a function returning number
+  lines =<< trim END
+    vim9script
+    enum Foo
+      apple,
+      orange
+    endenum
+    def Fn(): number
+      return Foo.orange
+    enddef
+    Fn()
+  END
+  v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected number but got enum<Foo>', 1)
+
+  # Returning a number in a function returning enum
+  lines =<< trim END
+    vim9script
+    enum Foo
+      apple,
+      orange
+    endenum
+    def Fn(): Foo
+      return 20
+    enddef
+    Fn()
+  END
+  v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected enum<Foo> but got number', 1)
+
+  # Use a List of enums
+  lines =<< trim END
+    vim9script
+    enum Planet
+      Mercury,
+      Venus,
+      Earth
+    endenum
+    var l1: list<Planet> = [Planet.Mercury, Planet.Venus]
+    assert_equal(Planet.Venus, l1[1])
+    def Fn()
+      var l2: list<Planet> = [Planet.Mercury, Planet.Venus]
+      assert_equal(Planet.Venus, l2[1])
+    enddef
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Try using an enum as a value
+  lines =<< trim END
+    vim9script
+    enum Fruit
+      Apple,
+      Orange
+    endenum
+    var a = Fruit
+  END
+  v9.CheckSourceFailure(lines, 'E1421: Enum "Fruit" cannot be used as a value', 6)
+enddef
+
+" Test for type() and typename() of an enum
+def Test_enum_type()
+  var lines =<< trim END
+    vim9script
+    enum Fruit
+      Apple,
+      Orange
+    endenum
+    assert_equal('enum<Fruit>', typename(Fruit))
+    assert_equal('enum<Fruit>', typename(Fruit.Apple))
+    assert_equal(v:t_enum, type(Fruit))
+    assert_equal(v:t_enumvalue, type(Fruit.Apple))
+    assert_equal(15, type(Fruit))
+    assert_equal(16, type(Fruit.Apple))
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Assign an enum to a variable with any type
+  lines =<< trim END
+    vim9script
+    enum Fruit
+      Apple,
+      Orange
+    endenum
+    var a = Fruit.Apple
+    var b: any = Fruit.Orange
+    assert_equal('enum<Fruit>', typename(a))
+    assert_equal('enum<Fruit>', typename(b))
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+" Try modifying an enum or an enum item
+def Test_enum_modify()
+  # Try assigning an unsupported value to an enum
+  var lines =<< trim END
+    vim9script
+    enum Foo
+      apple
+    endenum
+    var a: Foo = 30
+  END
+  v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected enum<Foo> but got number', 5)
+
+  # Try assigning an unsupported value to an enum in a function
+  lines =<< trim END
+    vim9script
+    enum Foo
+      apple
+    endenum
+    def Fn()
+      var a: Foo = 30
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected enum<Foo> but got number', 1)
+
+  # Try assigning a number to an enum
+  lines =<< trim END
+    vim9script
+    enum Foo
+      apple,
+      orange
+    endenum
+    Foo = 10
+  END
+  v9.CheckSourceFailure(lines, 'E1421: Enum "Foo" cannot be used as a value', 6)
+
+  # Try assigning a number to an enum in a function
+  lines =<< trim END
+    vim9script
+    enum Foo
+      apple
+    endenum
+    def Fn()
+      Foo = 10
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected enum<Foo> but got number', 1)
+
+  # Try assigning a number to an enum value
+  lines =<< trim END
+    vim9script
+    enum Foo
+      apple
+    endenum
+    Foo.apple = 20
+  END
+  v9.CheckSourceFailure(lines, 'E1423: Enum value "Foo.apple" cannot be modified', 5)
+
+  # Try assigning a number to an enum value in a function
+  lines =<< trim END
+    vim9script
+    enum Foo
+      apple
+    endenum
+    def Fn()
+      Foo.apple = 20
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1423: Enum value "Foo.apple" cannot be modified', 1)
+
+  # Try assigning one enum to another
+  lines =<< trim END
+    vim9script
+    enum Foo
+    endenum
+    enum Bar
+    endenum
+    Foo = Bar
+  END
+  v9.CheckSourceFailure(lines, 'E1421: Enum "Bar" cannot be used as a value', 6)
+
+  # Try assigning one enum to another in a function
+  lines =<< trim END
+    vim9script
+    enum Foo
+    endenum
+    enum Bar
+    endenum
+    def Fn()
+      Foo = Bar
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1421: Enum "Bar" cannot be used as a value', 1)
+
+  # Try assigning one enum item to another enum item
+  lines =<< trim END
+    vim9script
+    enum Foo
+      Apple
+    endenum
+    enum Bar
+      Orange
+    endenum
+    Foo.Apple = Bar.Orange
+  END
+  v9.CheckSourceFailure(lines, 'E1423: Enum value "Foo.Apple" cannot be modified', 8)
+
+  # Try assigning one enum item to another enum item in a function
+  lines =<< trim END
+    vim9script
+    enum Foo
+      Apple
+    endenum
+    enum Bar
+      Orange
+    endenum
+    def Fn()
+      Foo.Apple = Bar.Orange
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1423: Enum value "Foo.Apple" cannot be modified', 1)
+enddef
+
+" Test for using enum in an expression
+def Test_enum_expr()
+  var lines =<< trim END
+    vim9script
+    enum Color
+      Red, Blue, Green
+    endenum
+    var a: number = 1 + Color
+  END
+  v9.CheckSourceFailure(lines, 'E1421: Enum "Color" cannot be used as a value', 5)
+
+  lines =<< trim END
+    vim9script
+    enum Color
+      Red, Blue, Green
+    endenum
+    var a: number = 1 + Color.Red
+  END
+  v9.CheckSourceFailure(lines, 'E1424: Using an Enum "Color" as a Number', 5)
+
+  lines =<< trim END
+    vim9script
+    enum Color
+      Red, Blue, Green
+    endenum
+    var s: string = "abc" .. Color
+  END
+  v9.CheckSourceFailure(lines, 'E1421: Enum "Color" cannot be used as a value', 5)
+
+  lines =<< trim END
+    vim9script
+    enum Color
+      Red, Blue, Green
+    endenum
+    var s: string = "abc" .. Color.Red
+  END
+  v9.CheckSourceFailure(lines, 'E1425: Using an Enum "Color" as a String', 5)
+enddef
+
+" Using an enum in a lambda function
+def Test_enum_lambda()
+  var lines =<< trim END
+    vim9script
+    enum Planet
+      Mercury,
+      Venus,
+      Earth,
+    endenum
+    var Fn = (p: Planet): Planet => p
+    for [idx, v] in items([Planet.Mercury, Planet.Venus, Planet.Earth])
+      assert_equal(idx, Fn(v).ordinal)
+    endfor
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+" Comparison using enums
+def Test_enum_compare()
+  var lines =<< trim END
+    vim9script
+    enum Planet
+      Mercury,
+      Venus,
+      Earth,
+    endenum
+    enum Fruit
+      Apple,
+      Orange
+    endenum
+
+    var p: Planet = Planet.Venus
+    var f: Fruit = Fruit.Orange
+    assert_equal(true, p == Planet.Venus)
+    assert_equal(false, p == Planet.Earth)
+    assert_equal(false, p == f)
+    assert_equal(true, Planet.Mercury == Planet.Mercury)
+    assert_equal(true, Planet.Venus != Planet.Earth)
+    assert_equal(true, Planet.Mercury != Fruit.Apple)
+
+    def Fn1()
+      var p2: Planet = Planet.Venus
+      var f2: Fruit = Fruit.Orange
+      assert_equal(true, p2 == Planet.Venus)
+      assert_equal(false, p2 == Planet.Earth)
+      assert_equal(false, p2 == f2)
+    enddef
+    Fn1()
+
+    # comparison using "is" and "isnot"
+    assert_equal(true, p is Planet.Venus)
+    assert_equal(true, p isnot Planet.Earth)
+    assert_equal(false, p is Fruit.Orange)
+    assert_equal(true, p isnot Fruit.Orange)
+    def Fn2(arg: Planet)
+      assert_equal(true, arg is Planet.Venus)
+      assert_equal(true, arg isnot Planet.Earth)
+      assert_equal(false, arg is Fruit.Orange)
+      assert_equal(true, arg isnot Fruit.Orange)
+    enddef
+    Fn2(p)
+
+    class A
+    endclass
+    var o: A = A.new()
+    assert_equal(false, p == o)
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for using an enum as a default argument to a function
+def Test_enum_default_arg()
+  var lines =<< trim END
+    vim9script
+    enum Day
+      Monday, Tuesday, Wednesday
+    endenum
+    def Fn(d: Day = Day.Tuesday): Day
+      return d
+    enddef
+    assert_equal(Day.Tuesday, Fn())
+    assert_equal(Day.Wednesday, Fn(Day.Wednesday))
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for enum garbage collection
+func Test_enum_garbagecollect()
+  let lines =<< trim END
+    vim9script
+    enum Car
+      Honda, Ford, Tesla
+    endenum
+    assert_equal(1, Car.Ford.ordinal)
+    call test_garbagecollect_now()
+    assert_equal(1, Car.Ford.ordinal)
+    var c: Car = Car.Tesla
+    assert_equal(2, c.ordinal)
+    call test_garbagecollect_now()
+    assert_equal(2, c.ordinal)
+  END
+  call v9.CheckSourceSuccess(lines)
+
+  " garbage collection with a variable of type any
+  let lines =<< trim END
+    vim9script
+    enum Car
+      Honda, Ford, Tesla
+    endenum
+    call test_garbagecollect_now()
+    var c: any = Car.Tesla
+    call test_garbagecollect_now()
+    assert_equal(Car.Tesla, c)
+  END
+  call v9.CheckSourceSuccess(lines)
+
+  " garbage collection with function arguments and return types
+  let lines =<< trim END
+    vim9script
+    enum Car
+      Honda, Ford, Tesla
+    endenum
+    def Fn(a: Car): Car
+      assert_equal(Car.Ford, a)
+      return Car.Tesla
+    enddef
+    call test_garbagecollect_now()
+    var b: Car = Car.Ford
+    call test_garbagecollect_now()
+    assert_equal(Car.Tesla, Fn(b))
+    call test_garbagecollect_now()
+  END
+  call v9.CheckSourceSuccess(lines)
+endfunc
+
+" Test for the enum values class variable
+def Test_enum_values()
+  var lines =<< trim END
+    vim9script
+    enum Car
+      Honda, Ford, Tesla
+    endenum
+    var l: list<Car> = Car.values
+    assert_equal(Car.Ford, l[1])
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # empty enum
+  lines =<< trim END
+    vim9script
+    enum Car
+    endenum
+    assert_equal([], Car.values)
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # single value
+  lines =<< trim END
+    vim9script
+    enum Car
+      Honda
+    endenum
+    assert_equal([Car.Honda], Car.values)
+  END
+  v9.CheckSourceSuccess(lines)
+
+  lines =<< trim END
+    vim9script
+    enum A
+      Red,
+      Blue
+      static def GetValues(): list<A>
+       return values
+      enddef
+    endenum
+    assert_equal([A.Red, A.Blue], A.GetValues())
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Other class variables in an enum should not be added to 'values'
+  lines =<< trim END
+    vim9script
+    enum LogLevel
+      Error, Warn
+      static const x: number = 22
+    endenum
+    assert_equal([LogLevel.Error, LogLevel.Warn], LogLevel.values)
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Other class variable of enum type should not be added to 'values'
+  lines =<< trim END
+    vim9script
+    enum LogLevel
+      Error, Warn
+      static const x: LogLevel = LogLevel.Warn
+    endenum
+    assert_equal([LogLevel.Error, LogLevel.Warn], LogLevel.values)
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+" Test comments in enums
+def Test_enum_comments()
+  var lines =<< trim END
+    vim9script
+    enum Car  # cars
+      # before enum
+      Honda,  # honda
+      # before enum
+      Ford    # ford
+    endenum
+    assert_equal(1, Car.Ford.ordinal)
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Test for using an unsupported comment
+  lines =<< trim END
+    vim9script
+    enum Car
+      Honda,
+      Ford,
+      #{
+    endenum
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1170: Cannot use #{ to start a comment', 4)
+enddef
+
+" Test string() with enums
+def Test_enum_string()
+  var lines =<< trim END
+    vim9script
+    enum Car
+      Honda,
+      Ford
+    endenum
+    assert_equal("enum Car", string(Car))
+    assert_equal("Car.Honda", string(Car.Honda))
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for importing an enum
+def Test_enum_import()
+  var lines =<< trim END
+    vim9script
+    export enum Star
+      Gemini,
+      Orion,
+      Pisces
+    endenum
+  END
+  writefile(lines, 'Xenumexport.vim', 'D')
+
+  lines =<< trim END
+    vim9script
+    import './Xenumexport.vim' as mod
+
+    var s1: mod.Star = mod.Star.Orion
+    assert_equal(true, s1 == mod.Star.Orion)
+    assert_equal(2, mod.Star.Pisces.ordinal)
+    var l1: list<mod.Star> = mod.Star.values
+    assert_equal("Star.Orion", string(l1[1]))
+    assert_equal(s1, l1[1])
+
+    def Fn()
+      var s2: mod.Star = mod.Star.Orion
+      assert_equal(true, s2 == mod.Star.Orion)
+      assert_equal(2, mod.Star.Pisces.ordinal)
+      var l2: list<mod.Star> = mod.Star.values
+      assert_equal("Star.Orion", string(l2[1]))
+      assert_equal(s2, l2[1])
+    enddef
+    Fn()
+  END
+  v9.CheckScriptSuccess(lines)
+enddef
+
+" Test for using test_refcount() with enum
+def Test_enum_refcount()
+  var lines =<< trim END
+    vim9script
+    enum Foo
+    endenum
+    assert_equal(1, test_refcount(Foo))
+
+    enum Star
+      Gemini,
+      Orion,
+    endenum
+    assert_equal(3, test_refcount(Star))
+    assert_equal(2, test_refcount(Star.Gemini))
+    assert_equal(2, test_refcount(Star.Orion))
+
+    var s: Star
+    assert_equal(3, test_refcount(Star))
+    assert_equal(-1, test_refcount(s))
+    s = Star.Orion
+    assert_equal(3, test_refcount(Star))
+    assert_equal(3, test_refcount(s))
+    assert_equal(2, test_refcount(Star.Gemini))
+    var t = s
+    assert_equal(3, test_refcount(Star))
+    assert_equal(4, test_refcount(s))
+    assert_equal(4, test_refcount(Star.Orion))
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for defining an enum with additional object variables and methods
+def Test_enum_enhanced()
+  var lines =<< trim END
+    vim9script
+    enum Vehicle
+      car(4, 5, 400),
+      bus(6, 50, 800),
+      bicycle(2, 1, 0)
+
+      final tires: number
+      final passengers: number
+      final carbonPerKilometer: number
+
+      def new(t: number, p: number, cpk: number)
+        this.tires = t
+        this.passengers = p
+        this.carbonPerKilometer = cpk
+      enddef
+
+      def CarbonFootprint(): float
+        return round(this.carbonPerKilometer / this.passengers)
+      enddef
+
+      def IsTwoWheeled(): bool
+        return this == Vehicle.bicycle
+      enddef
+
+      def CompareTo(other: Vehicle): float
+         return this.CarbonFootprint() - other.CarbonFootprint()
+      enddef
+    endenum
+
+    var v: Vehicle = Vehicle.bus
+    assert_equal([6, 50, 800], [v.tires, v.passengers, v.carbonPerKilometer])
+    assert_equal(true, Vehicle.bicycle.IsTwoWheeled())
+    assert_equal(false, Vehicle.car.IsTwoWheeled())
+    assert_equal(16.0, Vehicle.bus.CarbonFootprint())
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for the enum value 'name' variable
+def Test_enum_name()
+  # Check the names of enum values
+  var lines =<< trim END
+    vim9script
+    enum Planet
+      Mercury,
+      Venus,
+      Earth
+    endenum
+    assert_equal('Mercury', Planet.Mercury.name)
+    assert_equal('Venus', Planet.Venus.name)
+    assert_equal('Earth', Planet.Earth.name)
+    assert_equal('string', typename(Planet.Earth.name))
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Check the name of enum items in the constructor
+  lines =<< trim END
+    vim9script
+    enum Planet
+      Mercury("Mercury"),
+      Venus("Venus"),
+      Earth("Earth")
+
+      def new(s: string)
+        assert_equal(s, this.name)
+      enddef
+    endenum
+    defcompile
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Try assigning to the name of an enum
+  lines =<< trim END
+    vim9script
+    enum Fruit
+      Apple
+    endenum
+    Fruit.Apple.name = 'foo'
+  END
+  v9.CheckSourceFailure(lines, 'E1335: Variable "name" in class "Fruit" is not writable', 5)
+
+  # Try assigning to the name of an enum in a function
+  lines =<< trim END
+    vim9script
+    enum Fruit
+      Apple
+    endenum
+    def Fn()
+      Fruit.Apple.name = 'bar'
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1423: Enum value "Fruit.name" cannot be modified', 1)
+
+  # Try to overwrite an enum value name in the enum constructor
+  lines =<< trim END
+    vim9script
+    enum Planet
+      Mercury,
+      Venus
+
+      def new()
+        this.name = 'foo'
+      enddef
+    endenum
+  END
+  v9.CheckSourceFailure(lines, 'E1427: Enum "Planet" name cannot be modified', 1)
+
+  # Try to declare an object variable named 'name'
+  lines =<< trim END
+    vim9script
+    enum Planet
+      Mercury
+      var name: string
+    endenum
+  END
+  v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: name', 4)
+enddef
+
+" Test for the enum value 'ordinal' variable
+def Test_enum_ordinal()
+  # Check the ordinal values of enum items
+  var lines =<< trim END
+    vim9script
+    enum Planet
+      Mercury,
+      Venus,
+      Earth
+    endenum
+    assert_equal(0, Planet.Mercury.ordinal)
+    assert_equal(1, Planet.Venus.ordinal)
+    assert_equal(2, Planet.Earth.ordinal)
+    assert_equal('number', typename(Planet.Earth.ordinal))
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Check the ordinal value of enum items in the constructor
+  lines =<< trim END
+    vim9script
+    enum Planet
+      Mercury(0),
+      Venus(1),
+      Earth(2)
+
+      def new(v: number)
+        assert_equal(v, this.ordinal)
+      enddef
+    endenum
+    defcompile
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Try assigning to the ordinal value of an enum
+  lines =<< trim END
+    vim9script
+    enum Fruit
+      Apple
+    endenum
+    Fruit.Apple.ordinal = 20
+  END
+  v9.CheckSourceFailure(lines, 'E1335: Variable "ordinal" in class "Fruit" is not writable', 5)
+
+  # Try assigning to the ordinal value of an enum in a function
+  lines =<< trim END
+    vim9script
+    enum Fruit
+      Apple
+    endenum
+    def Fn()
+      Fruit.Apple.ordinal = 20
+    enddef
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E1423: Enum value "Fruit.ordinal" cannot be modified', 1)
+
+  # Try to overwrite an enum value ordinal in the enum constructor
+  lines =<< trim END
+    vim9script
+    enum Planet
+      Mercury,
+      Venus
+
+      def new()
+        this.ordinal = 20
+      enddef
+    endenum
+  END
+  v9.CheckSourceFailure(lines, 'E1426: Enum "Planet" ordinal value cannot be modified', 1)
+
+  # Try to declare an object variable named 'ordinal'
+  lines =<< trim END
+    vim9script
+    enum Planet
+      Mercury
+      var ordinal: number
+    endenum
+  END
+  v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: ordinal', 4)
+enddef
+
+" Test for trying to create a new enum object using the constructor
+def Test_enum_invoke_constructor()
+  var lines =<< trim END
+    vim9script
+    enum Foo
+    endenum
+    var f: Foo = Foo.new()
+  END
+  v9.CheckSourceFailure(lines, 'E1325: Method "new" not found in class "Foo"', 4)
+
+  lines =<< trim END
+    vim9script
+    enum Fruit
+      Apple,
+      Orange
+    endenum
+    var f: Fruit = Fruit.new()
+  END
+  v9.CheckSourceFailure(lines, 'E1325: Method "new" not found in class "Fruit"', 6)
+
+  lines =<< trim END
+    vim9script
+    enum Fruit
+      Apple,
+      Orange
+      def newFruit()
+      enddef
+    endenum
+    var f: Fruit = Fruit.newFruit()
+  END
+  v9.CheckSourceFailure(lines, 'E1325: Method "newFruit" not found in class "Fruit"', 8)
+
+  lines =<< trim END
+    vim9script
+    enum Fruit
+      Apple,
+      Orange
+    endenum
+    def Fn()
+      var f: Fruit = Fruit.new()
+    enddef
+    Fn()
+  END
+  v9.CheckSourceFailure(lines, 'E1325: Method "new" not found in class "Fruit"', 1)
+
+  # error in the enum constructor
+  lines =<< trim END
+    vim9script
+    enum Planet
+      earth
+      def new()
+        x = 123
+      enddef
+    endenum
+  END
+  v9.CheckSourceFailureList(lines, ['E1100:', 'E1100:'], 1)
+enddef
+
+" Test for checking "this" in an enum constructor
+def Test_enum_this_in_constructor()
+  var lines =<< trim END
+    vim9script
+    enum A
+      Red("A.Red"),
+      Blue("A.Blue"),
+      Green("A.Green")
+
+      def new(s: string)
+        assert_equal(s, string(this))
+      enddef
+    endenum
+    defcompile
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for using member variables in an enum object
+def Test_enum_object_variable()
+  var lines =<< trim END
+    vim9script
+    enum Planet
+      Jupiter(95),
+      Saturn(146)
+
+      var moons: number
+    endenum
+    assert_equal(95, Planet.Jupiter.moons)
+    assert_equal(146, Planet.Saturn.moons)
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Use a final object variable
+  lines =<< trim END
+    vim9script
+    enum Planet
+      Jupiter(95),
+      Saturn(146)
+
+      final moons: number
+      def new(n: number)
+        this.moons = n
+      enddef
+    endenum
+    assert_equal(95, Planet.Jupiter.moons)
+    assert_equal(146, Planet.Saturn.moons)
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Use a const object variable
+  lines =<< trim END
+    vim9script
+    enum Planet
+      Mars(false),
+      Jupiter(true)
+
+      const has_ring: bool
+      def new(r: bool)
+        this.has_ring = r
+      enddef
+    endenum
+    assert_equal(false, Planet.Mars.has_ring)
+    assert_equal(true, Planet.Jupiter.has_ring)
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Use a regular object variable
+  lines =<< trim END
+    vim9script
+    enum Fruit
+      Apple,
+      Orange
+
+      final farm: string = 'SunValley'
+    endenum
+    assert_equal('SunValley', Fruit.Apple.farm)
+    assert_equal('SunValley', Fruit.Apple.farm)
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Invoke the default constructor with an object variable
+  lines =<< trim END
+    vim9script
+    enum Fruit
+      Apple('foo'),
+      Orange('bar')
+
+      final t: string
+    endenum
+    assert_equal('foo', Fruit.Apple.t)
+    assert_equal('bar', Fruit.Orange.t)
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Invoke the default constructor with an argument but without the object
+  # variable
+  lines =<< trim END
+    vim9script
+    enum Fruit
+      Apple,
+      Orange('bar')
+    endenum
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E118: Too many arguments for function: new', 5)
+
+  # Define a default constructor with an argument, but don't pass it in when
+  # defining the enum value
+  lines =<< trim END
+    vim9script
+    enum Fruit
+      Apple(5),
+      Orange
+
+      def new(t: number)
+      enddef
+    endenum
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, 'E119: Not enough arguments for function: new', 8)
+enddef
+
+" Test for using a custom constructor with an enum
+def Test_enum_custom_constructor()
+  # space before "("
+  var lines =<< trim END
+    vim9script
+    enum Fruit
+      Apple(10),
+      Orange (20)
+
+      def new(t: number)
+      enddef
+    endenum
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, "E1068: No white space allowed before '(': Orange (20)", 4)
+
+  # no closing ")"
+  lines =<< trim END
+    vim9script
+    enum Fruit
+      Apple(10),
+      Orange (20
+
+      def new(t: number)
+      enddef
+    endenum
+    defcompile
+  END
+  v9.CheckSourceFailure(lines, "E1068: No white space allowed before '(': Orange (20", 4)
+
+  # Specify constructor arguments split across multiple lines
+  lines =<< trim END
+    vim9script
+    enum Fruit
+      Apple(10,
+            'foo'), Orange(20,
+            'bar'),
+      Pear(30,
+           'baz'), Mango(40,
+           'qux')
+
+      final n: number
+      final s: string
+      def new(t: number, str: string)
+        this.n = t
+        this.s = str
+      enddef
+    endenum
+    defcompile
+    assert_equal([10, 'foo'], [Fruit.Apple.n, Fruit.Apple.s])
+    assert_equal([20, 'bar'], [Fruit.Orange.n, Fruit.Orange.s])
+    assert_equal([30, 'baz'], [Fruit.Pear.n, Fruit.Pear.s])
+    assert_equal([40, 'qux'], [Fruit.Mango.n, Fruit.Mango.s])
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # specify multiple enums with constructor arguments in a single line
+  lines =<< trim END
+    vim9script
+    enum Fruit
+      Apple(10, 'foo'), Orange(20, 'bar'), Pear(30, 'baz'), Mango(40, 'qux')
+      const n: number
+      const s: string
+    endenum
+    defcompile
+    assert_equal([10, 'foo'], [Fruit.Apple.n, Fruit.Apple.s])
+    assert_equal([20, 'bar'], [Fruit.Orange.n, Fruit.Orange.s])
+    assert_equal([30, 'baz'], [Fruit.Pear.n, Fruit.Pear.s])
+    assert_equal([40, 'qux'], [Fruit.Mango.n, Fruit.Mango.s])
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for using class variables in an enum class
+def Test_enum_class_variable()
+  var lines =<< trim END
+    vim9script
+    enum Fruit
+      Apple,
+      Orange
+
+      static var farm: string = 'SunValley'
+    endenum
+    assert_equal('SunValley', Fruit.farm)
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+" Test for converting an enum value to a string and then back to an enum value
+def Test_enum_eval()
+  var lines =<< trim END
+    vim9script
+    enum Color
+      Red,
+      Blue
+    endenum
+    var s: string = string(Color.Blue)
+    var e = eval(s)
+    assert_equal(Color.Blue, e)
+    assert_equal(1, e.ordinal)
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
+" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
index 0d731daa7672759ac29031cabb7168f2f47e184d..33de3a5cd836a98d1b9eaf69526299ae1dbcc8a2 100644 (file)
@@ -1091,9 +1091,8 @@ f_test_refcount(typval_T *argvars, typval_T *rettv)
        case VAR_SPECIAL:
        case VAR_STRING:
        case VAR_INSTR:
-       case VAR_CLASS:
-       case VAR_OBJECT:
            break;
+
        case VAR_JOB:
 #ifdef FEAT_JOB_CHANNEL
            if (argvars[0].vval.v_job != NULL)
@@ -1132,6 +1131,14 @@ f_test_refcount(typval_T *argvars, typval_T *rettv)
            if (argvars[0].vval.v_dict != NULL)
                retval = argvars[0].vval.v_dict->dv_refcount - 1;
            break;
+       case VAR_CLASS:
+           if (argvars[0].vval.v_class != NULL)
+               retval = argvars[0].vval.v_class->class_refcount - 1;
+           break;
+       case VAR_OBJECT:
+           if (argvars[0].vval.v_object != NULL)
+               retval = argvars[0].vval.v_object->obj_refcount - 1;
+           break;
        case VAR_TYPEALIAS:
            if (argvars[0].vval.v_typealias != NULL)
                retval = argvars[0].vval.v_typealias->ta_refcount - 1;
index 62958f6302a612703fc9d35b557589fd66ca3bef..6a73719b7136c8455decc2e98360cce92cc726c5 100644 (file)
@@ -266,7 +266,13 @@ tv_get_bool_or_number_chk(
            check_typval_is_value(varp);
            break;
        case VAR_OBJECT:
-           emsg(_(e_using_object_as_number));
+           {
+               class_T *cl = varp->vval.v_object->obj_class;
+               if (cl != NULL && IS_ENUM(cl))
+                   semsg(_(e_using_enum_str_as_number), cl->class_name);
+               else
+                   emsg(_(e_using_object_as_number));
+           }
            break;
        case VAR_VOID:
            emsg(_(e_cannot_use_void_value));
@@ -1139,7 +1145,13 @@ tv_get_string_buf_chk_strict(typval_T *varp, char_u *buf, int strict)
            check_typval_is_value(varp);
            break;
        case VAR_OBJECT:
-           emsg(_(e_using_object_as_string));
+           {
+               class_T *cl = varp->vval.v_object->obj_class;
+               if (cl != NULL && IS_ENUM(cl))
+                   semsg(_(e_using_enum_str_as_string), cl->class_name);
+               else
+                   emsg(_(e_using_object_as_string));
+           }
            break;
        case VAR_JOB:
 #ifdef FEAT_JOB_CHANNEL
index 4523359d735c23aab586a7cf2eea7c5d2ee0a258..bad5a93943c85eda68e93c989e0f3776d37465fe 100644 (file)
@@ -704,6 +704,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    219,
 /**/
     218,
 /**/
index 63a31880dcc08ff1238db45f40759336914ee1cc..cb5accd1442d0ffd1acdccf2ae286b3b0f824a2b 100644 (file)
--- a/src/vim.h
+++ b/src/vim.h
@@ -2164,7 +2164,9 @@ typedef int sock_T;
 #define VV_MAXCOL      105
 #define VV_PYTHON3_VERSION 106
 #define VV_TYPE_TYPEALIAS 107
-#define VV_LEN         108     // number of v: vars
+#define VV_TYPE_ENUM     108
+#define VV_TYPE_ENUMVALUE 109
+#define VV_LEN         110     // number of v: vars
 
 // used for v_number in VAR_BOOL and VAR_SPECIAL
 #define VVAL_FALSE     0L      // VAR_BOOL
@@ -2188,6 +2190,8 @@ typedef int sock_T;
 #define VAR_TYPE_CLASS     12
 #define VAR_TYPE_OBJECT            13
 #define VAR_TYPE_TYPEALIAS  14
+#define VAR_TYPE_ENUM      15
+#define VAR_TYPE_ENUMVALUE  16
 
 #define DICT_MAXNEST 100       // maximum nesting of lists and dicts
 
index 7227c3dc28882a39e4bd509462c75d39ac9fc2d8..b64032309317f5391e6cb711b2aaf7673b514bf5 100644 (file)
@@ -168,7 +168,8 @@ struct oc_newmember_S
 /*
  * Add a member to an object or a class.
  * Returns OK when successful, "init_expr" will be consumed then.
- * Returns FAIL otherwise, caller might need to free "init_expr".
+ * Returns OK on success and FAIL on memory allocation failure (caller might
+ * need to free "init_expr").
  */
     static int
 add_member(
@@ -323,13 +324,15 @@ validate_extends_class(
     }
 
     if (tv.v_type != VAR_CLASS || tv.vval.v_class == NULL
-           || (is_class
-               && (tv.vval.v_class->class_flags & CLASS_INTERFACE) != 0)
-           || (!is_class
-               && (tv.vval.v_class->class_flags & CLASS_INTERFACE) == 0))
-       // a interface cannot extend a class and a class cannot extend an
-       // interface.
+           || (is_class && IS_INTERFACE(tv.vval.v_class))
+           || (!is_class && !IS_INTERFACE(tv.vval.v_class))
+           || (is_class && IS_ENUM(tv.vval.v_class)))
+    {
+       // a class cannot extend an interface
+       // an interface cannot extend a class
+       // a class cannot extend an enum.
        semsg(_(e_cannot_extend_str), extends_name);
+    }
     else
     {
        class_T *extends_cl = tv.vval.v_class;
@@ -793,7 +796,7 @@ validate_implements_classes(
 
        if (tv.v_type != VAR_CLASS
                || tv.vval.v_class == NULL
-               || (tv.vval.v_class->class_flags & CLASS_INTERFACE) == 0)
+               || !IS_INTERFACE(tv.vval.v_class))
        {
            semsg(_(e_not_valid_interface_str), impl);
            success = FALSE;
@@ -1234,14 +1237,14 @@ add_lookup_tables(class_T *cl, class_T *extends_cl, garray_T *objmethods_gap)
  * Add class members to a new class.  Allocate a typval for each class member
  * and initialize it.
  */
-    static void
+    static int
 add_class_members(class_T *cl, exarg_T *eap, garray_T *type_list_gap)
 {
     // Allocate a typval for each class member and initialize it.
     cl->class_members_tv = ALLOC_CLEAR_MULT(typval_T,
                                            cl->class_class_member_count);
     if (cl->class_members_tv == NULL)
-       return;
+       return FAIL;
 
     for (int i = 0; i < cl->class_class_member_count; ++i)
     {
@@ -1250,19 +1253,19 @@ add_class_members(class_T *cl, exarg_T *eap, garray_T *type_list_gap)
        if (m->ocm_init != NULL)
        {
            typval_T *etv = eval_expr(m->ocm_init, eap);
-           if (etv != NULL)
-           {
-               if (m->ocm_type->tt_type == VAR_ANY
-                       && !(m->ocm_flags & OCMFLAG_HAS_TYPE)
-                       && etv->v_type != VAR_SPECIAL)
-                   // If the member variable type is not yet set, then use
-                   // the initialization expression type.
-                   m->ocm_type = typval2type(etv, get_copyID(),
-                                       type_list_gap,
-                                       TVTT_DO_MEMBER|TVTT_MORE_SPECIFIC);
-               *tv = *etv;
-               vim_free(etv);
-           }
+           if (etv == NULL)
+               return FAIL;
+
+           if (m->ocm_type->tt_type == VAR_ANY
+                   && !(m->ocm_flags & OCMFLAG_HAS_TYPE)
+                   && etv->v_type != VAR_SPECIAL)
+               // If the member variable type is not yet set, then use
+               // the initialization expression type.
+               m->ocm_type = typval2type(etv, get_copyID(),
+                       type_list_gap,
+                       TVTT_DO_MEMBER|TVTT_MORE_SPECIFIC);
+           *tv = *etv;
+           vim_free(etv);
        }
        else
        {
@@ -1273,6 +1276,8 @@ add_class_members(class_T *cl, exarg_T *eap, garray_T *type_list_gap)
        if (m->ocm_flags & OCMFLAG_CONST)
            item_lock(tv, DICT_MAXNEST, TRUE, TRUE);
     }
+
+    return OK;
 }
 
 /*
@@ -1284,13 +1289,21 @@ add_default_constructor(
     garray_T   *classfunctions_gap,
     garray_T   *type_list_gap)
 {
-    garray_T fga;
+    garray_T   fga;
+    int                is_enum = IS_ENUM(cl);
 
     ga_init2(&fga, 1, 1000);
     ga_concat(&fga, (char_u *)"new(");
     for (int i = 0; i < cl->class_obj_member_count; ++i)
     {
-       if (i > 0)
+       if (i < 2 && is_enum)
+           // The first two object variables in an enum are the enum value
+           // name and ordinal.  Don't initialize these object variables in
+           // the default constructor as they are already initialized right
+           // after creating the object.
+           continue;
+
+       if (i > (is_enum ? 2 : 0))
            ga_concat(&fga, (char_u *)", ");
        ga_concat(&fga, (char_u *)"this.");
        ocmember_T *m = cl->class_obj_members + i;
@@ -1336,6 +1349,7 @@ add_default_constructor(
  * Add the class methods and object methods to the new class "cl".
  * When extending a class "extends_cl", add the instance methods from the
  * parent class also.
+ * Returns OK on success and FAIL on memory allocation failure.
  */
     static int
 add_classfuncs_objmethods(
@@ -1373,7 +1387,7 @@ add_classfuncs_objmethods(
 
        if (gap->ga_len != 0)
            mch_memmove(*fup, gap->ga_data, sizeof(ufunc_T *) * gap->ga_len);
-       vim_free(gap->ga_data);
+       VIM_CLEAR(gap->ga_data);
        if (loop == 1)
            cl->class_class_function_count_child = gap->ga_len;
        else
@@ -1422,6 +1436,8 @@ add_classfuncs_objmethods(
            if (loop == 2)
                fp->uf_flags |= FC_OBJECT;
        }
+
+       ga_clear(gap);
     }
 
     return OK;
@@ -1471,6 +1487,246 @@ find_class_name_end(char_u *arg)
     return end;
 }
 
+/*
+ * Returns TRUE if the enum value "varname" is already defined.
+ */
+    static int
+is_duplicate_enum(
+    garray_T   *enum_gap,
+    char_u     *varname,
+    char_u     *varname_end)
+{
+    char_u     *name = vim_strnsave(varname, varname_end - varname);
+    int                dup = FALSE;
+
+    for (int i = 0; i < enum_gap->ga_len; ++i)
+    {
+       ocmember_T *m = ((ocmember_T *)enum_gap->ga_data) + i;
+       if (STRCMP(name, m->ocm_name) == 0)
+       {
+           semsg(_(e_duplicate_enum_str), name);
+           dup = TRUE;
+           break;
+       }
+    }
+
+    vim_free(name);
+    return dup;
+}
+
+/*
+ * Parse the enum values in "line" separated by comma and add them to "gap".
+ * If the last enum value is found, then "enum_end" is set to TRUE.
+ */
+    static int
+enum_parse_values(
+    exarg_T    *eap,
+    class_T    *en,
+    char_u     *line,
+    garray_T   *gap,
+    int                *num_enum_values,
+    int                *enum_end)
+{
+    evalarg_T  evalarg;
+    char_u     *p = line;
+    char       initexpr_buf[1024];
+    char_u     last_char = NUL;
+    int                rc = OK;
+
+    fill_evalarg_from_eap(&evalarg, eap, FALSE);
+
+    int                did_emsg_before = did_emsg;
+    while (*p != NUL)
+    {
+       // ignore comment
+       if (*p == '#')
+           break;
+
+       if (!eval_isnamec1(*p))
+       {
+           semsg(_(e_invalid_enum_value_declaration_str), p);
+           break;
+       }
+
+       char_u *eni_name_start = p;
+       char_u *eni_name_end = to_name_end(p, FALSE);
+
+       if (is_duplicate_enum(gap, eni_name_start, eni_name_end))
+           break;
+
+       p = skipwhite(eni_name_end);
+
+       char_u  *init_expr = NULL;
+       if (*p == '(')
+       {
+           if (VIM_ISWHITE(p[-1]))
+           {
+               semsg(_(e_no_white_space_allowed_before_str_str), "(", line);
+               break;
+           }
+
+           char_u *expr_start, *expr_end;
+
+           p = eni_name_start;
+           (void)skip_expr_concatenate(&p, &expr_start, &expr_end, &evalarg);
+
+           while (*expr_start && *expr_start != '(')
+               expr_start++;
+
+           if (expr_end > expr_start)
+               init_expr = vim_strnsave(expr_start, expr_end - expr_start);
+       }
+
+       if (init_expr == NULL)
+           vim_snprintf(initexpr_buf, sizeof(initexpr_buf), "%s.new()",
+                                                   en->class_name);
+       else
+       {
+           vim_snprintf(initexpr_buf, sizeof(initexpr_buf), "%s.new%s",
+                                           en->class_name, init_expr);
+           vim_free(init_expr);
+       }
+       if (add_member(gap, eni_name_start, eni_name_end, FALSE,
+                               TRUE, TRUE, TRUE, &en->class_object_type,
+                               vim_strsave((char_u *)initexpr_buf)) == FAIL)
+           break;
+
+       ++*num_enum_values;
+
+       if (*p != '#')
+           last_char = *p;
+
+       if (*p != NUL && *p != ',')
+           break;
+
+       if (*p == ',')
+       {
+           if (!IS_WHITE_OR_NUL(p[1]))
+           {
+               semsg(_(e_white_space_required_after_str_str), ",", line);
+               break;
+           }
+           if (VIM_ISWHITE(p[-1]))
+           {
+               semsg(_(e_no_white_space_allowed_before_str_str), ",", line);
+               break;
+           }
+           p = skipwhite(p + 1);
+       }
+    }
+
+    if (*p != NUL && *p != '#')
+    {
+       if (did_emsg == did_emsg_before)
+           semsg(_(e_missing_comma_before_argument_str), p);
+       rc = FAIL;
+    }
+
+    if (last_char != ',')
+       // last enum value should not be terminated by ","
+       *enum_end = TRUE;
+
+    // Free the memory pointed by expr_start.
+    clear_evalarg(&evalarg, NULL);
+
+    return rc;
+}
+
+/*
+ * Add the "values" class variable (List of enum value objects) to the enum
+ * class "en"
+ */
+    static int
+enum_add_values_member(
+    class_T    *en,
+    garray_T   *gap,
+    int                num_enum_values,
+    garray_T   *type_list_gap)
+{
+    garray_T   fga;
+    int                rc = FAIL;
+
+    ga_init2(&fga, 1, 1000);
+    ga_concat(&fga, (char_u *)"[");
+    for (int i = 0; i < num_enum_values; ++i)
+    {
+       ocmember_T *m = ((ocmember_T *)gap->ga_data) + i;
+
+       if (i > 0)
+           ga_concat(&fga, (char_u *)", ");
+       ga_concat(&fga, en->class_name);
+       ga_concat(&fga, (char_u *)".");
+       ga_concat(&fga, (char_u *)m->ocm_name);
+    }
+    ga_concat(&fga, (char_u *)"]");
+    ga_append(&fga, NUL);
+
+    char_u *varname = (char_u *)"values";
+
+    type_T *type = get_type_ptr(type_list_gap);
+    if (type == NULL)
+       goto done;
+
+    type->tt_type = VAR_LIST;
+    type->tt_member = get_type_ptr(type_list_gap);
+    if (type->tt_member != NULL)
+    {
+       type->tt_member->tt_type = VAR_OBJECT;
+       type->tt_member->tt_class = en;
+    }
+
+    rc = add_member(gap, varname, varname + 6, FALSE, FALSE, TRUE, TRUE, type,
+                                       vim_strsave((char_u *)fga.ga_data));
+
+done:
+    vim_free(fga.ga_data);
+
+    return rc;
+}
+
+/*
+ * Clear the constructor method names in a enum class, so that an enum class
+ * cannot be instantiated.
+ */
+    static void
+enum_clear_constructors(class_T *en)
+{
+    for (int i = 0; i < en->class_class_function_count; ++i)
+    {
+       ufunc_T *fp = en->class_class_functions[i];
+
+       if (fp->uf_flags & FC_NEW)
+           *fp->uf_name = NUL;
+    }
+}
+
+/*
+ * Initialize the name and ordinal object variable in the enum value "enval" in
+ * the enum "en".  These values are set during the enum value object creation.
+ */
+    void
+enum_set_internal_obj_vars(class_T *en, object_T *enval)
+{
+    int        i;
+
+    for (i = 0; i < en->class_class_member_count; ++i)
+    {
+       typval_T *en_tv = en->class_members_tv + i;
+       if (en_tv != NULL && en_tv->v_type == VAR_UNKNOWN)
+           break;
+    }
+
+    // First object variable is the name
+    ocmember_T *value_ocm = en->class_class_members + i;
+    typval_T *name_tv = (typval_T *)(enval + 1);
+    name_tv->v_type = VAR_STRING;
+    name_tv->vval.v_string = vim_strsave(value_ocm->ocm_name);
+
+    // Second object variable is the ordinal
+    typval_T *ord_tv = (typval_T *)(name_tv + 1);
+    ord_tv->v_type = VAR_NUMBER;
+    ord_tv->vval.v_number = i;
+}
 
 /*
  * Handle ":class" and ":abstract class" up to ":endclass".
@@ -1479,10 +1735,12 @@ find_class_name_end(char_u *arg)
     void
 ex_class(exarg_T *eap)
 {
-    int                is_class = eap->cmdidx == CMD_class;  // FALSE for :interface
+    int                is_class = eap->cmdidx == CMD_class;
+    int                is_abstract = eap->cmdidx == CMD_abstract;
+    int                is_enum = eap->cmdidx == CMD_enum;
+    int                is_interface;
     long       start_lnum = SOURCING_LNUM;
     char_u     *arg = eap->arg;
-    int                is_abstract = eap->cmdidx == CMD_abstract;
 
     if (is_abstract)
     {
@@ -1495,12 +1753,16 @@ ex_class(exarg_T *eap)
        is_class = TRUE;
     }
 
+    is_interface = !is_class && !is_enum;
+
     if (!current_script_is_vim9()
                || (cmdmod.cmod_flags & CMOD_LEGACY)
                || !getline_equal(eap->ea_getline, eap->cookie, getsourceline))
     {
        if (is_class)
            emsg(_(e_class_can_only_be_defined_in_vim9_script));
+       else if (is_enum)
+           emsg(_(e_enum_can_only_be_defined_in_vim9_script));
        else
            emsg(_(e_interface_can_only_be_defined_in_vim9_script));
        return;
@@ -1510,6 +1772,8 @@ ex_class(exarg_T *eap)
     {
        if (is_class)
            semsg(_(e_class_name_must_start_with_uppercase_letter_str), arg);
+       else if (is_enum)
+           semsg(_(e_enum_name_must_start_with_uppercase_letter_str), arg);
        else
            semsg(_(e_interface_name_must_start_with_uppercase_letter_str),
                                                                          arg);
@@ -1523,11 +1787,6 @@ ex_class(exarg_T *eap)
     }
     char_u *name_start = arg;
 
-    // "export class" gets used when creating the class, don't use "is_export"
-    // for the items inside the class.
-    int class_export = is_export;
-    is_export = FALSE;
-
     // TODO:
     //    generics: <Tkey, Tentry>
 
@@ -1545,6 +1804,11 @@ ex_class(exarg_T *eap)
        //    specifies SomeInterface
        if (STRNCMP(arg, "extends", 7) == 0 && IS_WHITE_OR_NUL(arg[7]))
        {
+           if (is_enum)
+           {
+               emsg(_(e_enum_cannot_extend_class));
+               goto early_ret;
+           }
            if (extends != NULL)
            {
                emsg(_(e_duplicate_extends));
@@ -1567,7 +1831,7 @@ ex_class(exarg_T *eap)
        else if (STRNCMP(arg, "implements", 10) == 0
                                                   && IS_WHITE_OR_NUL(arg[10]))
        {
-           if (!is_class)
+           if (is_interface)
            {
                emsg(_(e_interface_cannot_use_implements));
                goto early_ret;
@@ -1652,11 +1916,15 @@ early_ret:
     class_T *cl = NULL;
     class_T *extends_cl = NULL;  // class from "extends" argument
     class_T **intf_classes = NULL;
+    int            num_enum_values = 0;
 
     cl = ALLOC_CLEAR_ONE(class_T);
     if (cl == NULL)
        goto cleanup;
-    if (!is_class)
+
+    if (is_enum)
+       cl->class_flags = CLASS_ENUM;
+    else if (is_interface)
        cl->class_flags = CLASS_INTERFACE;
     else if (is_abstract)
        cl->class_flags = CLASS_ABSTRACT;
@@ -1666,22 +1934,48 @@ early_ret:
     if (cl->class_name == NULL)
        goto cleanup;
 
+    cl->class_type.tt_type = VAR_CLASS;
+    cl->class_type.tt_class = cl;
+    cl->class_object_type.tt_type = VAR_OBJECT;
+    cl->class_object_type.tt_class = cl;
+
     // Add the class to the script-local variables.
     // TODO: handle other context, e.g. in a function
     // TODO: does uf_hash need to be cleared?
     typval_T tv;
     tv.v_type = VAR_CLASS;
     tv.vval.v_class = cl;
-    is_export = class_export;
     SOURCING_LNUM = start_lnum;
     int rc = set_var_const(cl->class_name, current_sctx.sc_sid,
                                                NULL, &tv, FALSE, 0, 0);
     if (rc == FAIL)
        goto cleanup;
 
+    if (is_enum)
+    {
+       // All the enum classes have the name and ordinal object variables.
+       char_u *varname = (char_u *)"name";
+       if (add_member(&objmembers, varname, varname + 4, FALSE, FALSE, TRUE,
+                   TRUE, &t_string, NULL) == FAIL)
+           goto cleanup;
+
+       varname = (char_u *)"ordinal";
+       if (add_member(&objmembers, varname, varname + 7, FALSE, FALSE, TRUE,
+                   TRUE, &t_number, NULL) == FAIL)
+           goto cleanup;
+    }
+
+    // "export class" gets used when creating the class, don't use "is_export"
+    // for the items inside the class.
+    is_export = FALSE;
+
+    // When parsing an enum definition, this denotes whether all the enumerated
+    // values are parsed or not.
+    int                enum_end = FALSE;
+
     /*
      * Go over the body of the class/interface until "endclass" or
-     * "endinterface" is found.
+     * "endinterface" or "endenum" is found.
      */
     char_u *theline = NULL;
     int success = FALSE;
@@ -1704,10 +1998,32 @@ early_ret:
        }
 
        char_u *p = line;
-       char *end_name = is_class ? "endclass" : "endinterface";
-       if (checkforcmd(&p, end_name, is_class ? 4 : 5))
+
+       char    *end_name;
+       int     shortlen;
+       int     fullen;
+       if (is_class)
+       {
+           end_name = "endclass";
+           shortlen = 4;
+           fullen = 8;
+       }
+       else if (is_enum)
+       {
+           end_name = "endenum";
+           shortlen = 4;
+           fullen = 7;
+       }
+       else
        {
-           if (STRNCMP(line, end_name, is_class ? 8 : 12) != 0)
+           end_name = "endinterface";
+           shortlen = 5;
+           fullen = 12;
+       }
+
+       if (checkforcmd(&p, end_name, shortlen))
+       {
+           if (STRNCMP(line, end_name, fullen) != 0)
                semsg(_(e_command_cannot_be_shortened_str), line);
            else if (*p == '|' || !ends_excmd2(line, p))
                semsg(_(e_trailing_characters_str), p);
@@ -1715,13 +2031,34 @@ early_ret:
                success = TRUE;
            break;
        }
-       char *wrong_name = is_class ? "endinterface" : "endclass";
-       if (checkforcmd(&p, wrong_name, is_class ? 5 : 4))
+
+       int     wrong_endname = FALSE;
+       if (is_class)
+           wrong_endname = checkforcmd(&p, "endinterface", 5)
+                                       || checkforcmd(&p, "endenum", 4);
+       else if (is_enum)
+           wrong_endname = checkforcmd(&p, "endclass", 4)
+                                       || checkforcmd(&p, "endinterface", 5);
+       else
+           wrong_endname = checkforcmd(&p, "endclass", 4)
+                                       || checkforcmd(&p, "endenum", 4);
+       if (wrong_endname)
        {
            semsg(_(e_invalid_command_str_expected_str), line, end_name);
            break;
        }
 
+       if (is_enum && !enum_end)
+       {
+           // In an enum, all the enumerated values are at the beginning
+           // separated by comma.  The class and object variables/methods
+           // follow the values.
+           if (enum_parse_values(eap, cl, line, &classmembers,
+                                       &num_enum_values, &enum_end) == FAIL)
+               break;
+           continue;
+       }
+
        int has_public = FALSE;
        if (checkforcmd(&p, "public", 3))
        {
@@ -1730,7 +2067,7 @@ early_ret:
                semsg(_(e_command_cannot_be_shortened_str), line);
                break;
            }
-           if (!is_class)
+           if (is_interface)
            {
                emsg(_(e_public_variable_not_supported_in_interface));
                break;
@@ -1756,7 +2093,14 @@ early_ret:
                break;
            }
 
-           if (!is_class)
+           if (is_enum)
+           {
+               // "abstract" not supported in an enum
+               emsg(_(e_abstract_cannot_be_used_in_enum));
+               break;
+           }
+
+           if (is_interface)
            {
                // "abstract" not supported in an interface
                emsg(_(e_abstract_cannot_be_used_in_interface));
@@ -1789,7 +2133,7 @@ early_ret:
                break;
            }
 
-           if (!is_class)
+           if (is_interface)
            {
                emsg(_(e_static_member_not_supported_in_interface));
                break;
@@ -1812,7 +2156,7 @@ early_ret:
            has_var = TRUE;
        else if (checkforcmd(&p, "final", 5))
        {
-           if (!is_class)
+           if (is_interface)
            {
                emsg(_(e_final_variable_not_supported_in_interface));
                break;
@@ -1821,7 +2165,7 @@ early_ret:
        }
        else if (checkforcmd(&p, "const", 5))
        {
-           if (!is_class)
+           if (is_interface)
            {
                emsg(_(e_const_variable_not_supported_in_interface));
                break;
@@ -1867,7 +2211,7 @@ early_ret:
                break;
            }
 
-           if (!is_class && *varname == '_')
+           if (is_interface && *varname == '_')
            {
                // private variables are not supported in an interface
                semsg(_(e_protected_variable_not_supported_in_interface),
@@ -1877,7 +2221,7 @@ early_ret:
 
            if (parse_member(eap, line, varname, has_public,
                          &varname_end, &has_type, &type_list, &type,
-                         is_class ? &init_expr: NULL) == FAIL)
+                         !is_interface ? &init_expr: NULL) == FAIL)
                break;
 
            if (is_reserved_varname(varname, varname_end)
@@ -1930,7 +2274,7 @@ early_ret:
                break;
            }
 
-           if (!is_class && *p == '_')
+           if (is_interface && *p == '_')
            {
                // private methods are not supported in an interface
                semsg(_(e_protected_method_not_supported_in_interface), p);
@@ -1953,10 +2297,10 @@ early_ret:
 
            ga_init2(&lines_to_free, sizeof(char_u *), 50);
            int class_flags;
-           if (is_class)
-               class_flags = abstract_method ? CF_ABSTRACT_METHOD : CF_CLASS;
-           else
+           if (is_interface)
                class_flags = CF_INTERFACE;
+           else
+               class_flags = abstract_method ? CF_ABSTRACT_METHOD : CF_CLASS;
            ufunc_T *uf = define_function(&ea, NULL, &lines_to_free,
                        class_flags, objmembers.ga_data, objmembers.ga_len);
            ga_clear_strings(&lines_to_free);
@@ -2011,15 +2355,25 @@ early_ret:
        {
            if (is_class)
                semsg(_(e_not_valid_command_in_class_str), line);
+           else if (is_enum)
+               semsg(_(e_not_valid_command_in_enum_str), line);
            else
                semsg(_(e_not_valid_command_in_interface_str), line);
            break;
        }
     }
+
+    if (theline == NULL && !success && is_enum)
+       emsg(_(e_missing_endenum));
+
     vim_free(theline);
 
+    if (success && is_enum)
+       // Add the enum "values" class variable.
+       enum_add_values_member(cl, &classmembers, num_enum_values, &type_list);
+
     /*
-     * Check a few things before defining the class.
+     * Check a few things
      */
 
     // Check the "extends" class is valid.
@@ -2067,7 +2421,8 @@ early_ret:
 
     if (success)
     {
-       // "endclass" encountered without failures: Create the class.
+       // "endclass" or "endinterface" or "endenum" encountered without any
+       // failures
 
        if (extends_cl != NULL)
        {
@@ -2114,10 +2469,6 @@ early_ret:
                goto cleanup;
        }
 
-       // Allocate a typval for each class member and initialize it.
-       if (is_class && cl->class_class_member_count > 0)
-           add_class_members(cl, eap, &type_list);
-
        int     have_new = FALSE;
        ufunc_T *class_func = NULL;
        for (int i = 0; i < classfunctions.ga_len; ++i)
@@ -2133,7 +2484,7 @@ early_ret:
        if (have_new)
            // The return type of new() is an object of class "cl"
            class_func->uf_ret_type->tt_class = cl;
-       else if (is_class && !is_abstract && !have_new)
+       else if ((is_class || is_enum) && !is_abstract && !have_new)
            // No new() method was defined, add the default constructor.
            add_default_constructor(cl, &classfunctions, &type_list);
 
@@ -2144,13 +2495,21 @@ early_ret:
 
        update_builtin_method_index(cl);
 
-       cl->class_type.tt_type = VAR_CLASS;
-       cl->class_type.tt_class = cl;
-       cl->class_object_type.tt_type = VAR_OBJECT;
-       cl->class_object_type.tt_class = cl;
+       class_created(cl);
+
+       // Allocate a typval for each class member and initialize it.
+       if ((is_class || is_enum) && cl->class_class_member_count > 0)
+           if (add_class_members(cl, eap, &type_list) == FAIL)
+               goto cleanup;
+
        cl->class_type_list = type_list;
 
-       class_created(cl);
+       if (is_enum)
+       {
+           // clear the constructor method names, so that an enum class cannot
+           // be instantiated
+           enum_clear_constructors(cl);
+       }
 
        // TODO:
        // - Fill hashtab with object members and methods ?
@@ -2264,15 +2623,6 @@ oc_member_type_by_idx(
     return m[member_idx].ocm_type;
 }
 
-/*
- * Handle ":enum" up to ":endenum".
- */
-    void
-ex_enum(exarg_T *eap UNUSED)
-{
-    // TODO
-}
-
 /*
  * Type aliases (:type)
  */
@@ -3334,8 +3684,14 @@ member_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len)
            semsg(_(e_object_variable_str_accessible_only_using_object_str),
                    varname, cl->class_name);
        else
-           semsg(_(e_class_variable_str_not_found_in_class_str),
-                   varname, cl->class_name);
+       {
+           if (IS_ENUM(cl))
+               semsg(_(e_enum_value_str_not_found_in_enum_str),
+                       varname, cl->class_name);
+           else
+               semsg(_(e_class_variable_str_not_found_in_class_str),
+                       varname, cl->class_name);
+       }
     }
     vim_free(varname);
 }
@@ -3480,8 +3836,17 @@ object_string(
        garray_T ga;
        ga_init2(&ga, 1, 50);
 
-       ga_concat(&ga, (char_u *)"object of ");
        class_T *cl = obj == NULL ? NULL : obj->obj_class;
+       if (cl != NULL && IS_ENUM(cl))
+       {
+           ga_concat(&ga, cl->class_name);
+           char_u *name = ((typval_T *)(obj + 1))->vval.v_string;
+           ga_concat(&ga, (char_u *)".");
+           ga_concat(&ga, name);
+           return ga.ga_data;
+       }
+
+       ga_concat(&ga, (char_u *)"object of ");
        ga_concat(&ga, cl == NULL ? (char_u *)"[unknown]"
                : cl->class_name);
        if (cl != NULL)
index 8f67d8a8f6212a338d071778959cf441ab3ae431..0a2b0689d20cfbd02848ae836b5cac313ac16854 100644 (file)
@@ -1614,6 +1614,13 @@ lhs_class_member_modifiable(lhs_T *lhs, char_u   *var_start, cctx_T *cctx)
        return FALSE;
     }
 
+    if (IS_ENUM(cl))
+    {
+       semsg(_(e_enumvalue_str_cannot_be_modified), cl->class_name,
+               m->ocm_name);
+       return FALSE;
+    }
+
     // If it is private member variable, then accessing it outside the
     // class is not allowed.
     // If it is a read only class variable, then it can be modified
@@ -2037,6 +2044,25 @@ compile_lhs(
                return FAIL;
            }
 
+           if (IS_ENUM(cl))
+           {
+               if (!inside_class(cctx, cl))
+               {
+                   semsg(_(e_enumvalue_str_cannot_be_modified),
+                           cl->class_name, m->ocm_name);
+                   return FALSE;
+               }
+               if (lhs->lhs_type->tt_type == VAR_OBJECT &&
+                       lhs->lhs_member_idx < 2)
+               {
+                   char *msg = lhs->lhs_member_idx == 0 ?
+                       e_enum_str_name_cannot_be_modified :
+                       e_enum_str_ordinal_cannot_be_modified;
+                   semsg(_(msg), cl->class_name);
+                   return FALSE;
+               }
+           }
+
            // If it is private member variable, then accessing it outside the
            // class is not allowed.
            // If it is a read only class variable, then it can be modified
@@ -2304,7 +2330,7 @@ compile_load_lhs_with_index(lhs_T *lhs, char_u *var_start, cctx_T *cctx)
            if (compile_load_lhs(lhs, var_start, lhs->lhs_type, cctx) == FAIL)
                return FAIL;
        }
-       if (cl->class_flags & CLASS_INTERFACE)
+       if (IS_INTERFACE(cl))
            return generate_GET_ITF_MEMBER(cctx, cl, lhs->lhs_member_idx, type);
        return generate_GET_OBJ_MEMBER(cctx, lhs->lhs_member_idx, type);
     }
@@ -2453,7 +2479,7 @@ compile_assign_unlet(
                {
                    class_T *cl = lhs->lhs_type->tt_class;
 
-                   if (cl->class_flags & CLASS_INTERFACE)
+                   if (IS_INTERFACE(cl))
                    {
                        // "this.value": load "this" object and get the value
                        // at index for an object or class member get the type
@@ -3375,6 +3401,14 @@ compile_def_function(
            for (int i = 0; i < ufunc->uf_class->class_obj_member_count; ++i)
            {
                ocmember_T *m = &ufunc->uf_class->class_obj_members[i];
+
+               if (i < 2 && IS_ENUM(ufunc->uf_class))
+                   // The first two object variables in an enum are the name
+                   // and the ordinal.  These are set by the ISN_CONSTRUCT
+                   // instruction.  So don't generate instructions to set
+                   // these variables.
+                   continue;
+
                if (m->ocm_init != NULL)
                {
                    char_u *expr = m->ocm_init;
@@ -3481,8 +3515,7 @@ compile_def_function(
 
     // Compiling a function in an interface is done to get the function type.
     // No code is actually compiled.
-    if (ufunc->uf_class != NULL
-                          && (ufunc->uf_class->class_flags & CLASS_INTERFACE))
+    if (ufunc->uf_class != NULL && IS_INTERFACE(ufunc->uf_class))
     {
        ufunc->uf_def_status = UF_NOT_COMPILED;
        ret = OK;
index 3e6aed081564d7432c1426f2efc775118ebe452c..053f6178a8177762fb76a8464a63c5243371b8c8 100644 (file)
@@ -3258,6 +3258,12 @@ exec_instructions(ectx_T *ectx)
                ++tv->vval.v_object->obj_class->class_refcount;
                tv->vval.v_object->obj_refcount = 1;
                object_created(tv->vval.v_object);
+
+               // When creating an enum value object, initialize the name and
+               // ordinal object variables.
+               class_T *en = tv->vval.v_object->obj_class;
+               if (IS_ENUM(en))
+                   enum_set_internal_obj_vars(en, tv->vval.v_object);
                break;
 
            // execute Ex command line
index 9d67aead59380cc6f3a308cb88da79b101c00b61..43b13d82c7c361a8ef24148d204c54c0a09c4b62 100644 (file)
@@ -446,7 +446,7 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
        if (m_idx >= 0)
        {
            ufunc_T *fp = cl->class_obj_methods[m_idx];
-           // Private methods are not accessible outside the class
+           // Private object methods are not accessible outside the class
            if (*name == '_' && !inside_class(cctx, cl))
            {
                semsg(_(e_cannot_access_protected_method_str), fp->uf_name);
@@ -488,7 +488,7 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
        if (m_idx >= 0)
        {
            ufunc_T *fp = cl->class_class_functions[m_idx];
-           // Private methods are not accessible outside the class
+           // Private class methods are not accessible outside the class
            if (*name == '_' && !inside_class(cctx, cl))
            {
                semsg(_(e_cannot_access_protected_method_str), fp->uf_name);
@@ -2462,7 +2462,8 @@ compile_subscript(
                return FAIL;
            ppconst->pp_is_const = FALSE;
 
-           if ((type = get_type_on_stack(cctx, 0)) != &t_unknown
+           type = get_type_on_stack(cctx, 0);
+           if (type != &t_unknown
                    && (type->tt_type == VAR_CLASS
                                               || type->tt_type == VAR_OBJECT))
            {
index 0d004c8c6dc49e0117b3c4ef2fa86a2b6faab871..4d93ba72775528bd23fd770a1981baaa502951ef 100644 (file)
@@ -719,7 +719,7 @@ check_typval_type(type_T *expected, typval_T *actual_tv, where_T where)
 
     if (expected == NULL)
        return OK;  // didn't expect anything.
-                   //
+
     ga_init2(&type_list, sizeof(type_T *), 10);
 
     // A null_function and null_partial are special cases, they can be used to
@@ -1739,8 +1739,15 @@ type_name(type_T *type, char **tofree)
 
     if (type->tt_type == VAR_OBJECT || type->tt_type == VAR_CLASS)
     {
-       char_u *class_name = type->tt_class == NULL ? (char_u *)"Unknown"
-                                   : type->tt_class->class_name;
+       char_u *class_name;
+       if (type->tt_class != NULL)
+       {
+           class_name = type->tt_class->class_name;
+           if (IS_ENUM(type->tt_class))
+               name = "enum";
+       }
+       else
+           class_name = (char_u *)"Unknown";
        size_t len = STRLEN(name) + STRLEN(class_name) + 3;
        *tofree = alloc(len);
        if (*tofree != NULL)
@@ -1869,18 +1876,26 @@ check_typval_is_value(typval_T *tv)
 {
     if (tv == NULL)
        return OK;
-    if (tv->v_type == VAR_CLASS)
-    {
-       if (tv->vval.v_class != NULL)
-           semsg(_(e_using_class_as_value_str), tv->vval.v_class->class_name);
-       else
-           emsg(e_using_class_as_var_val);
-       return FAIL;
-    }
-    else if (tv->v_type == VAR_TYPEALIAS)
+
+    switch (tv->v_type)
     {
-        semsg(_(e_using_typealias_as_value_str), tv->vval.v_typealias->ta_name);
-       return FAIL;
+       case VAR_CLASS:
+           {
+               class_T *cl = tv->vval.v_class;
+               if (IS_ENUM(cl))
+                   semsg(_(e_using_enum_as_value_str), cl->class_name);
+               else
+                   semsg(_(e_using_class_as_value_str), cl->class_name);
+           }
+           return FAIL;
+
+       case VAR_TYPEALIAS:
+           semsg(_(e_using_typealias_as_value_str),
+                   tv->vval.v_typealias->ta_name);
+           return FAIL;
+
+       default:
+           break;
     }
     return OK;
 }
@@ -1893,17 +1908,25 @@ check_type_is_value(type_T *type)
 {
     if (type == NULL)
        return OK;
-    if (type->tt_type == VAR_CLASS)
+    switch (type->tt_type)
     {
-        semsg(_(e_using_class_as_value_str), type->tt_class->class_name);
-       return FAIL;
-    }
-    else if (type->tt_type == VAR_TYPEALIAS)
-    {
-       // TODO: Not sure what could be done here to get a name.
-       //       Maybe an optional argument?
-        emsg(_(e_using_typealias_as_var_val));
-       return FAIL;
+       case VAR_CLASS:
+           if (IS_ENUM(type->tt_class))
+               semsg(_(e_using_enum_as_value_str),
+                       type->tt_class->class_name);
+           else
+               semsg(_(e_using_class_as_value_str),
+                       type->tt_class->class_name);
+           return FAIL;
+
+       case VAR_TYPEALIAS:
+           // TODO: Not sure what could be done here to get a name.
+           //       Maybe an optional argument?
+           emsg(_(e_using_typealias_as_var_val));
+           return FAIL;
+
+       default:
+           break;
     }
     return OK;
 }