summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2022-12-08 15:32:33 +0000
committerBram Moolenaar <Bram@vim.org>2022-12-08 15:32:33 +0000
commit00b28d6c23d8e662cab27e461825777c0a2e387a (patch)
treeba11ed85b20cf03491e0f24b6d5bf348d2c19388
parent038e6d20e680ce8c850d07f6b035c4e1904c1201 (diff)
patch 9.0.1031: Vim9 class is not implemented yetv9.0.1031
Problem: Vim9 class is not implemented yet. Solution: Add very basic class support.
-rw-r--r--runtime/doc/vim9class.txt9
-rw-r--r--src/errors.h24
-rw-r--r--src/eval.c47
-rw-r--r--src/evalfunc.c14
-rw-r--r--src/evalvars.c2
-rw-r--r--src/if_py_both.h2
-rw-r--r--src/json.c2
-rw-r--r--src/proto/userfunc.pro3
-rw-r--r--src/proto/vim9class.pro6
-rw-r--r--src/proto/vim9instr.pro1
-rw-r--r--src/proto/vim9script.pro2
-rw-r--r--src/proto/vim9type.pro1
-rw-r--r--src/structs.h67
-rw-r--r--src/testdir/Make_all.mak2
-rw-r--r--src/testdir/test_vim9_class.vim145
-rw-r--r--src/testing.c2
-rw-r--r--src/typval.c46
-rw-r--r--src/userfunc.c71
-rw-r--r--src/version.c2
-rw-r--r--src/vim.h2
-rw-r--r--src/vim9.h12
-rw-r--r--src/vim9class.c493
-rw-r--r--src/vim9compile.c77
-rw-r--r--src/vim9execute.c52
-rw-r--r--src/vim9expr.c2
-rw-r--r--src/vim9instr.c22
-rw-r--r--src/vim9script.c11
-rw-r--r--src/vim9type.c19
-rw-r--r--src/viminfo.c2
29 files changed, 1066 insertions, 74 deletions
diff --git a/runtime/doc/vim9class.txt b/runtime/doc/vim9class.txt
index cb86b4d136..b018a105c3 100644
--- a/runtime/doc/vim9class.txt
+++ b/runtime/doc/vim9class.txt
@@ -336,6 +336,9 @@ Defining a class ~
A class is defined between `:class` and `:endclass`. The whole class is
defined in one script file. It is not possible to add to a class later.
+A class can only be defined in a |Vim9| script file. *E1315*
+A class cannot be defined inside a function.
+
It is possible to define more than one class in a script file. Although it
usually is better to export only one main class. It can be useful to define
types, enums and helper classes though.
@@ -369,9 +372,9 @@ A class can extend one other class.
*implements*
A class can implement one or more interfaces.
*specifies*
-A class can declare it's interface, the object members and methods, with a
+A class can declare its interface, the object members and methods, with a
named interface. This avoids the need for separately specifying the
-interface, which is often done an many languages, especially Java.
+interface, which is often done in many languages, especially Java.
Defining an interface ~
@@ -634,7 +637,7 @@ directly writing you get an error, which makes you wonder if you actually want
to allow that. This helps writing code with fewer mistakes.
-Making object membes private with an underscore ~
+Making object members private with an underscore ~
When an object member is private, it can only be read and changed inside the
class (and in sub-classes), then it cannot be used outside of the class.
diff --git a/src/errors.h b/src/errors.h
index 8d1feb39b6..6230527a70 100644
--- a/src/errors.h
+++ b/src/errors.h
@@ -3346,4 +3346,28 @@ EXTERN char e_not_allowed_to_add_or_remove_entries_str[]
#ifdef FEAT_EVAL
EXTERN char e_class_name_must_start_with_uppercase_letter_str[]
INIT(= N_("E1314: Class name must start with an uppercase letter: %s"));
+EXTERN char e_white_space_required_after_class_name_str[]
+ INIT(= N_("E1315: White space required after class name: %s"));
+EXTERN char e_class_can_only_be_defined_in_vim9_script[]
+ INIT(= N_("E1316: Class can only be defined in Vim9 script"));
+EXTERN char e_invalid_object_member_declaration_str[]
+ INIT(= N_("E1317: Invalid object member declaration: %s"));
+EXTERN char e_not_valid_command_in_class_str[]
+ INIT(= N_("E1318: Not a valid command in a class: %s"));
+EXTERN char e_using_class_as_number[]
+ INIT(= N_("E1319: Using a class as a Number"));
+EXTERN char e_using_object_as_number[]
+ INIT(= N_("E1320: Using an object as a Number"));
+EXTERN char e_using_class_as_float[]
+ INIT(= N_("E1321: Using a class as a Float"));
+EXTERN char e_using_object_as_float[]
+ INIT(= N_("E1322: Using an object as a Float"));
+EXTERN char e_using_class_as_string[]
+ INIT(= N_("E1323: Using a class as a String"));
+EXTERN char e_using_object_as_string[]
+ INIT(= N_("E1324: Using an object as a String"));
+EXTERN char e_method_not_found_on_class_str_str[]
+ INIT(= N_("E1325: Method not found on class \"%s\": %s"));
+EXTERN char e_member_not_found_on_object_str_str[]
+ INIT(= N_("E1326: Member not found on object \"%s\": %s"));
#endif
diff --git a/src/eval.c b/src/eval.c
index 20883d4309..917e0a852c 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -1548,7 +1548,7 @@ set_var_lval(
{
cc = *endp;
*endp = NUL;
- if (in_vim9script() && check_reserved_name(lp->ll_name) == FAIL)
+ if (in_vim9script() && check_reserved_name(lp->ll_name, NULL) == FAIL)
return;
if (lp->ll_blob != NULL)
@@ -1724,6 +1724,8 @@ tv_op(typval_T *tv1, typval_T *tv2, char_u *op)
case VAR_JOB:
case VAR_CHANNEL:
case VAR_INSTR:
+ case VAR_CLASS:
+ case VAR_OBJECT:
break;
case VAR_BLOB:
@@ -3850,12 +3852,25 @@ handle_predefined(char_u *s, int len, typval_T *rettv)
return OK;
}
break;
+ case 10: if (STRNCMP(s, "null_class", 10) == 0)
+ {
+ rettv->v_type = VAR_CLASS;
+ rettv->vval.v_class = NULL;
+ return OK;
+ }
+ break;
case 11: if (STRNCMP(s, "null_string", 11) == 0)
{
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
return OK;
}
+ if (STRNCMP(s, "null_object", 11) == 0)
+ {
+ rettv->v_type = VAR_OBJECT;
+ rettv->vval.v_object = NULL;
+ return OK;
+ }
break;
case 12:
if (STRNCMP(s, "null_channel", 12) == 0)
@@ -4685,6 +4700,8 @@ check_can_index(typval_T *rettv, int evaluate, int verbose)
case VAR_JOB:
case VAR_CHANNEL:
case VAR_INSTR:
+ case VAR_CLASS:
+ case VAR_OBJECT:
if (verbose)
emsg(_(e_cannot_index_special_variable));
return FAIL;
@@ -4788,6 +4805,8 @@ eval_index_inner(
case VAR_JOB:
case VAR_CHANNEL:
case VAR_INSTR:
+ case VAR_CLASS:
+ case VAR_OBJECT:
break; // not evaluating, skipping over subscript
case VAR_NUMBER:
@@ -5781,6 +5800,16 @@ echo_string_core(
r = (char_u *)"instructions";
break;
+ case VAR_CLASS:
+ *tofree = NULL;
+ r = (char_u *)"class";
+ break;
+
+ case VAR_OBJECT:
+ *tofree = NULL;
+ r = (char_u *)"object";
+ break;
+
case VAR_FLOAT:
*tofree = NULL;
vim_snprintf((char *)numbuf, NUMBUFLEN, "%g", tv->vval.v_float);
@@ -6588,6 +6617,20 @@ handle_subscript(
ret = FAIL;
}
}
+ else if (**arg == '.' && (rettv->v_type == VAR_CLASS
+ || rettv->v_type == VAR_OBJECT))
+ {
+ // class member: SomeClass.varname
+ // class method: SomeClass.SomeMethod()
+ // class constructor: SomeClass.new()
+ // object member: someObject.varname
+ // object method: someObject.SomeMethod()
+ if (class_object_index(arg, rettv, evalarg, verbose) == FAIL)
+ {
+ clear_tv(rettv);
+ ret = FAIL;
+ }
+ }
else
break;
}
@@ -6644,6 +6687,8 @@ item_copy(
case VAR_JOB:
case VAR_CHANNEL:
case VAR_INSTR:
+ case VAR_CLASS:
+ case VAR_OBJECT:
copy_tv(from, to);
break;
case VAR_LIST:
diff --git a/src/evalfunc.c b/src/evalfunc.c
index 84df6a62aa..3db4bb7b24 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -3770,6 +3770,12 @@ f_empty(typval_T *argvars, typval_T *rettv)
case VAR_SPECIAL:
n = argvars[0].vval.v_number != VVAL_TRUE;
break;
+ case VAR_CLASS:
+ n = argvars[0].vval.v_class != NULL;
+ break;
+ case VAR_OBJECT:
+ n = argvars[0].vval.v_object != NULL;
+ break;
case VAR_BLOB:
n = argvars[0].vval.v_blob == NULL
@@ -7267,6 +7273,8 @@ f_len(typval_T *argvars, typval_T *rettv)
case VAR_JOB:
case VAR_CHANNEL:
case VAR_INSTR:
+ case VAR_CLASS:
+ case VAR_OBJECT:
emsg(_(e_invalid_type_for_len));
break;
}
@@ -10183,7 +10191,9 @@ f_substitute(typval_T *argvars, typval_T *rettv)
if (argvars[2].v_type == VAR_FUNC
|| argvars[2].v_type == VAR_PARTIAL
- || argvars[2].v_type == VAR_INSTR)
+ || argvars[2].v_type == VAR_INSTR
+ || argvars[2].v_type == VAR_CLASS
+ || argvars[2].v_type == VAR_OBJECT)
expr = &argvars[2];
else
sub = tv_get_string_buf_chk(&argvars[2], subbuf);
@@ -10617,6 +10627,8 @@ 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_UNKNOWN:
case VAR_ANY:
case VAR_VOID:
diff --git a/src/evalvars.c b/src/evalvars.c
index 31cc760bf0..d9466691f4 100644
--- a/src/evalvars.c
+++ b/src/evalvars.c
@@ -2264,6 +2264,8 @@ item_lock(typval_T *tv, int deep, int lock, int check_refcount)
case VAR_JOB:
case VAR_CHANNEL:
case VAR_INSTR:
+ case VAR_CLASS:
+ case VAR_OBJECT:
break;
case VAR_BLOB:
diff --git a/src/if_py_both.h b/src/if_py_both.h
index 5589af678e..8dd7f09b55 100644
--- a/src/if_py_both.h
+++ b/src/if_py_both.h
@@ -6422,6 +6422,8 @@ ConvertToPyObject(typval_T *tv)
case VAR_CHANNEL:
case VAR_JOB:
case VAR_INSTR:
+ case VAR_CLASS:
+ case VAR_OBJECT:
Py_INCREF(Py_None);
return Py_None;
case VAR_BOOL:
diff --git a/src/json.c b/src/json.c
index 80120acb30..9c084b4605 100644
--- a/src/json.c
+++ b/src/json.c
@@ -308,6 +308,8 @@ json_encode_item(garray_T *gap, typval_T *val, int copyID, int options)
case VAR_JOB:
case VAR_CHANNEL:
case VAR_INSTR:
+ case VAR_CLASS:
+ case VAR_OBJECT:
semsg(_(e_cannot_json_encode_str), vartype_name(val->v_type));
return FAIL;
diff --git a/src/proto/userfunc.pro b/src/proto/userfunc.pro
index ee17049ce6..5c865ce8de 100644
--- a/src/proto/userfunc.pro
+++ b/src/proto/userfunc.pro
@@ -7,6 +7,7 @@ char_u *register_cfunc(cfunc_T cb, cfunc_free_T cb_free, void *state);
int get_lambda_tv(char_u **arg, typval_T *rettv, int types_optional, evalarg_T *evalarg);
char_u *deref_func_name(char_u *name, int *lenp, partial_T **partialp, type_T **type, int no_autoload, int new_function, int *found_var);
void emsg_funcname(char *ermsg, char_u *name);
+int get_func_arguments(char_u **arg, evalarg_T *evalarg, int partial_argc, typval_T *argvars, int *argcount);
int get_func_tv(char_u *name, int len, typval_T *rettv, char_u **arg, evalarg_T *evalarg, funcexe_T *funcexe);
char_u *fname_trans_sid(char_u *name, char_u *fname_buf, char_u **tofree, int *error);
void func_name_with_sid(char_u *name, int sid, char_u *buffer);
@@ -45,7 +46,7 @@ char_u *get_scriptlocal_funcname(char_u *funcname);
char_u *alloc_printable_func_name(char_u *fname);
char_u *save_function_name(char_u **name, int *is_global, int skip, int flags, funcdict_T *fudi);
void list_functions(regmatch_T *regmatch);
-ufunc_T *define_function(exarg_T *eap, char_u *name_arg, garray_T *lines_to_free);
+ufunc_T *define_function(exarg_T *eap, char_u *name_arg, garray_T *lines_to_free, class_T *class_arg);
void ex_function(exarg_T *eap);
ufunc_T *find_func_by_name(char_u *name, compiletype_T *compile_type);
void ex_defcompile(exarg_T *eap);
diff --git a/src/proto/vim9class.pro b/src/proto/vim9class.pro
index 4e55178bda..4c6e12dab7 100644
--- a/src/proto/vim9class.pro
+++ b/src/proto/vim9class.pro
@@ -1,6 +1,12 @@
/* vim9class.c */
void ex_class(exarg_T *eap);
+type_T *class_member_type(class_T *cl, char_u *name, char_u *name_end, int *member_idx);
void ex_interface(exarg_T *eap);
void ex_enum(exarg_T *eap);
void ex_type(exarg_T *eap);
+int class_object_index(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int verbose);
+void copy_object(typval_T *from, typval_T *to);
+void object_unref(object_T *obj);
+void copy_class(typval_T *from, typval_T *to);
+void class_unref(typval_T *tv);
/* vim: set ft=c : */
diff --git a/src/proto/vim9instr.pro b/src/proto/vim9instr.pro
index 87914f36d2..04d76822fe 100644
--- a/src/proto/vim9instr.pro
+++ b/src/proto/vim9instr.pro
@@ -3,6 +3,7 @@ isn_T *generate_instr(cctx_T *cctx, isntype_T isn_type);
isn_T *generate_instr_drop(cctx_T *cctx, isntype_T isn_type, int drop);
isn_T *generate_instr_type(cctx_T *cctx, isntype_T isn_type, type_T *type);
isn_T *generate_instr_debug(cctx_T *cctx);
+int generate_CONSTRUCT(cctx_T *cctx, class_T *cl);
int may_generate_2STRING(int offset, int tolerant, cctx_T *cctx);
int generate_add_instr(cctx_T *cctx, vartype_T vartype, type_T *type1, type_T *type2, exprtype_T expr_type);
vartype_T operator_type(type_T *type1, type_T *type2);
diff --git a/src/proto/vim9script.pro b/src/proto/vim9script.pro
index 0e98039e8b..9a0bcdf37c 100644
--- a/src/proto/vim9script.pro
+++ b/src/proto/vim9script.pro
@@ -19,5 +19,5 @@ void update_vim9_script_var(int create, dictitem_T *di, char_u *name, int flags,
void hide_script_var(scriptitem_T *si, int idx, int func_defined);
svar_T *find_typval_in_script(typval_T *dest, scid_T sid, int must_find);
int check_script_var_type(svar_T *sv, typval_T *value, char_u *name, where_T where);
-int check_reserved_name(char_u *name);
+int check_reserved_name(char_u *name, cctx_T *cctx);
/* vim: set ft=c : */
diff --git a/src/proto/vim9type.pro b/src/proto/vim9type.pro
index 3c860928b1..98c6091e98 100644
--- a/src/proto/vim9type.pro
+++ b/src/proto/vim9type.pro
@@ -1,4 +1,5 @@
/* vim9type.c */
+type_T *get_type_ptr(garray_T *type_gap);
type_T *copy_type(type_T *type, garray_T *type_gap);
void clear_type_list(garray_T *gap);
type_T *alloc_type(type_T *type);
diff --git a/src/structs.h b/src/structs.h
index 497a35306e..f7ae16b758 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -1406,6 +1406,9 @@ typedef struct {
typedef struct isn_S isn_T; // instruction
typedef struct dfunc_S dfunc_T; // :def function
+typedef struct type_S type_T;
+typedef struct ufunc_S ufunc_T;
+
typedef struct jobvar_S job_T;
typedef struct readq_S readq_T;
typedef struct writeq_S writeq_T;
@@ -1415,6 +1418,8 @@ typedef struct channel_S channel_T;
typedef struct cctx_S cctx_T;
typedef struct ectx_S ectx_T;
typedef struct instr_S instr_T;
+typedef struct class_S class_T;
+typedef struct object_S object_T;
typedef enum
{
@@ -1434,16 +1439,18 @@ typedef enum
VAR_JOB, // "v_job" is used
VAR_CHANNEL, // "v_channel" is used
VAR_INSTR, // "v_instr" is used
+ VAR_CLASS, // "v_class" is used
+ VAR_OBJECT, // "v_object" is used
} vartype_T;
// A type specification.
-typedef struct type_S type_T;
struct type_S {
vartype_T tt_type;
int8_T tt_argcount; // for func, incl. vararg, -1 for unknown
int8_T tt_min_argcount; // number of non-optional arguments
char_u tt_flags; // TTFLAG_ values
type_T *tt_member; // for list, dict, func return type
+ // for class: class_T
type_T **tt_args; // func argument types, allocated
};
@@ -1452,6 +1459,38 @@ typedef struct {
type_T *type_decl; // declared type or equal to type_current
} type2_T;
+/*
+ * Entry for an object member variable.
+ */
+typedef struct {
+ char_u *om_name; // allocated
+ type_T *om_type;
+} objmember_T;
+
+// "class_T": used for v_class of typval of VAR_CLASS
+struct class_S
+{
+ char_u *class_name; // allocated
+ int class_refcount;
+
+ int class_obj_member_count;
+ objmember_T *class_obj_members; // allocated
+
+ int class_obj_method_count;
+ ufunc_T **class_obj_methods; // allocated
+ ufunc_T *class_new_func; // new() function that was created
+
+ garray_T class_type_list; // used for type pointers
+ type_T class_type;
+};
+
+// Used for v_object of typval of VAR_OBJECT.
+// The member variables follow in an array of typval_T.
+struct object_S {
+ class_T *obj_class; // class this object is created for
+ int obj_refcount;
+};
+
#define TTFLAG_VARARGS 0x01 // func args ends with "..."
#define TTFLAG_BOOL_OK 0x02 // can be converted to bool
#define TTFLAG_STATIC 0x04 // one of the static types, e.g. t_any
@@ -1467,17 +1506,19 @@ typedef struct
union
{
varnumber_T v_number; // number value
- float_T v_float; // floating number value
- char_u *v_string; // string value (can be NULL!)
- list_T *v_list; // list value (can be NULL!)
- dict_T *v_dict; // dict value (can be NULL!)
+ float_T v_float; // floating point number value
+ char_u *v_string; // string value (can be NULL)
+ list_T *v_list; // list value (can be NULL)
+ dict_T *v_dict; // dict value (can be NULL)
partial_T *v_partial; // closure: function with args
#ifdef FEAT_JOB_CHANNEL
- job_T *v_job; // job value (can be NULL!)
- channel_T *v_channel; // channel value (can be NULL!)
+ job_T *v_job; // job value (can be NULL)
+ channel_T *v_channel; // channel value (can be NULL)
#endif
- blob_T *v_blob; // blob value (can be NULL!)
+ blob_T *v_blob; // blob value (can be NULL)
instr_T *v_instr; // instructions to execute
+ class_T *v_class; // class value (can be NULL)
+ object_T *v_object; // object value (can be NULL)
} vval;
} typval_T;
@@ -1663,7 +1704,7 @@ typedef enum {
* Structure to hold info for a user function.
* When adding a field check copy_lambda_to_global_func().
*/
-typedef struct
+struct ufunc_S
{
int uf_varargs; // variable nr of arguments (old style)
int uf_flags; // FC_ flags
@@ -1671,6 +1712,9 @@ typedef struct
int uf_cleared; // func_clear() was already called
def_status_T uf_def_status; // UF_NOT_COMPILED, UF_TO_BE_COMPILED, etc.
int uf_dfunc_idx; // only valid if uf_def_status is UF_COMPILED
+
+ class_T *uf_class; // for object method and constructor
+
garray_T uf_args; // arguments, including optional arguments
garray_T uf_def_args; // default argument expressions
int uf_args_visible; // normally uf_args.ga_len, less when
@@ -1731,7 +1775,7 @@ typedef struct
char_u uf_name[4]; // name of function (actual size equals name);
// can start with <SNR>123_ (<SNR> is K_SPECIAL
// KS_EXTRA KE_SNR)
-} ufunc_T;
+};
// flags used in uf_flags
#define FC_ABORT 0x01 // abort function on error
@@ -1750,6 +1794,9 @@ typedef struct
// copy_lambda_to_global_func()
#define FC_LAMBDA 0x2000 // one line "return {expr}"
+#define FC_OBJECT 010000 // object method
+#define FC_NEW 030000 // constructor (also an object method)
+
#define MAX_FUNC_ARGS 20 // maximum number of function arguments
#define VAR_SHORT_LEN 20 // short variable name length
#define FIXVAR_CNT 12 // number of fixed variables
diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak
index 1ac29b1b6a..886bb08ad8 100644
--- a/src/testdir/Make_all.mak
+++ b/src/testdir/Make_all.mak
@@ -37,6 +37,7 @@ SCRIPTS_TINY_OUT = \
TEST_VIM9 = \
test_vim9_assign \
test_vim9_builtin \
+ test_vim9_class \
test_vim9_cmd \
test_vim9_disassemble \
test_vim9_expr \
@@ -48,6 +49,7 @@ TEST_VIM9 = \
TEST_VIM9_RES = \
test_vim9_assign.res \
test_vim9_builtin.res \
+ test_vim9_class.res \
test_vim9_cmd.res \
test_vim9_disassemble.res \
test_vim9_expr.res \
diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim
new file mode 100644
index 0000000000..6742ea757c
--- /dev/null
+++ b/src/testdir/test_vim9_class.vim
@@ -0,0 +1,145 @@
+" Test Vim9 classes
+
+source check.vim
+import './vim9.vim' as v9
+
+def Test_class_basic()
+ var lines =<< trim END
+ class NotWorking
+ endclass
+ END
+ v9.CheckScriptFailure(lines, 'E1316:')
+
+ lines =<< trim END
+ vim9script
+ class notWorking
+ endclass
+ END
+ v9.CheckScriptFailure(lines, 'E1314:')
+
+ lines =<< trim END
+ vim9script
+ class Not@working
+ endclass
+ END
+ v9.CheckScriptFailure(lines, 'E1315:')
+
+ lines =<< trim END
+ vim9script
+ abstract noclass Something
+ endclass
+ END
+ v9.CheckScriptFailure(lines, 'E475:')
+
+ lines =<< trim END
+ vim9script
+ abstract classy Something
+ endclass
+ END
+ v9.CheckScriptFailure(lines, 'E475:')
+
+ lines =<< trim END
+ vim9script
+ class Something
+ endcl
+ END
+ v9.CheckScriptFailure(lines, 'E1065:')
+
+ lines =<< trim END
+ vim9script
+ class Something
+ endclass school's out
+ END
+ v9.CheckScriptFailure(lines, 'E488:')
+
+ lines =<< trim END
+ vim9script
+ class Something
+ endclass | echo 'done'
+ END
+ v9.CheckScriptFailure(lines, 'E488:')
+
+ lines =<< trim END
+ vim9script
+ class Something
+ this
+ endclass
+ END
+ v9.CheckScriptFailure(lines, 'E1317:')
+
+ lines =<< trim END
+ vim9script
+ class Something
+ this.
+ endclass
+ END
+ v9.CheckScriptFailure(lines, 'E1317:')
+
+ lines =<< trim END
+ vim9script
+ class Something
+ this .count
+ endclass
+ END
+ v9.CheckScriptFailure(lines, 'E1317:')
+
+ lines =<< trim END
+ vim9script
+ class Something
+ this. count
+ endclass
+ END
+ v9.CheckScriptFailure(lines, 'E1317:')
+
+ lines =<< trim END
+ vim9script
+ class Something
+ this.count: number
+ that.count
+ endclass
+ END
+ v9.CheckScriptFailure(lines, 'E1318: Not a valid command in a class: that.count')
+
+ lines =<< trim END
+ vim9script
+ class Something
+ this.count
+ endclass
+ END
+ v9.CheckScriptFailure(lines, 'E1022:')
+
+ lines =<< trim END
+ vim9script
+ class Something
+ this.count : number
+ endclass
+ END
+ v9.CheckScriptFailure(lines, 'E1059:')
+
+ lines =<< trim END
+ vim9script
+ class Something
+ this.count:number
+ endclass
+ END
+ v9.CheckScriptFailure(lines, 'E1069:')
+
+ lines =<< trim END
+ vim9script
+
+ class TextPosition
+ this.lnum: number
+ this.col: number
+ endclass
+
+ # # FIXME: this works but leaks memory
+ # # use the automatically generated new() method
+ # var pos = TextPosition.new(2, 12)
+ # assert_equal(2, pos.lnum)
+ # assert_equal(12, pos.col)
+ END
+ v9.CheckScriptSuccess(lines)
+enddef
+
+
+" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
diff --git a/src/testing.c b/src/testing.c
index d76d098ee0..74b6b0e81b 100644
--- a/src/testing.c
+++ b/src/testing.c
@@ -1101,6 +1101,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
diff --git a/src/typval.c b/src/typval.c
index b1a0060467..6faebe4015 100644
--- a/src/typval.c
+++ b/src/typval.c
@@ -84,6 +84,13 @@ free_tv(typval_T *varp)
channel_unref(varp->vval.v_channel);
break;
#endif
+ case VAR_CLASS:
+ class_unref(varp);
+ break;
+ case VAR_OBJECT:
+ object_unref(varp->vval.v_object);
+ break;
+
case VAR_NUMBER:
case VAR_FLOAT:
case VAR_ANY:
@@ -153,6 +160,12 @@ clear_tv(typval_T *varp)
case VAR_INSTR:
VIM_CLEAR(varp->vval.v_instr);
break;
+ case VAR_CLASS:
+ class_unref(varp);
+ break;
+ case VAR_OBJECT:
+ object_unref(varp->vval.v_object);
+ break;
case VAR_UNKNOWN:
case VAR_ANY:
case VAR_VOID:
@@ -234,6 +247,12 @@ tv_get_bool_or_number_chk(typval_T *varp, int *denote, int want_bool)
case VAR_BLOB:
emsg(_(e_using_blob_as_number));
break;
+ case VAR_CLASS:
+ emsg(_(e_using_class_as_number));
+ break;
+ case VAR_OBJECT:
+ emsg(_(e_using_object_as_number));
+ break;
case VAR_VOID:
emsg(_(e_cannot_use_void_value));
break;
@@ -333,6 +352,12 @@ tv_get_float_chk(typval_T *varp, int *error)
case VAR_BLOB:
emsg(_(e_using_blob_as_float));
break;
+ case VAR_CLASS:
+ emsg(_(e_using_class_as_float));
+ break;
+ case VAR_OBJECT:
+ emsg(_(e_using_object_as_float));
+ break;
case VAR_VOID:
emsg(_(e_cannot_use_void_value));
break;
@@ -1029,6 +1054,12 @@ tv_get_string_buf_chk_strict(typval_T *varp, char_u *buf, int strict)
case VAR_BLOB:
emsg(_(e_using_blob_as_string));
break;
+ case VAR_CLASS:
+ emsg(_(e_using_class_as_string));
+ break;
+ case VAR_OBJECT:
+ emsg(_(e_using_object_as_string));
+ break;
case VAR_JOB:
#ifdef FEAT_JOB_CHANNEL
if (in_vim9script())
@@ -1158,6 +1189,14 @@ copy_tv(typval_T *from, typval_T *to)
to->vval.v_instr = from->vval.v_instr;
break;
+ case VAR_CLASS:
+ copy_class(from, to);
+ break;
+
+ case VAR_OBJECT:
+ copy_object(from, to);
+ break;
+
case VAR_STRING:
case VAR_FUNC:
if (from->vval.v_string == NULL)
@@ -1878,6 +1917,13 @@ tv_equal(
case VAR_INSTR:
return tv1->vval.v_instr == tv2->vval.v_instr;
+ case VAR_CLASS:
+ return tv1->vval.v_class == tv2->vval.v_class;
+
+ case VAR_OBJECT:
+ // TODO: compare values
+ return tv1->vval.v_object == tv2->vval.v_object;
+
case VAR_PARTIAL:
return tv1->vval.v_partial == tv2->vval.v_partial;
diff --git a/src/userfunc.c b/src/userfunc.c
index 492c6721bc..5db0b709c2 100644
--- a/src/userfunc.c
+++ b/src/userfunc.c
@@ -214,6 +214,8 @@ get_function_args(
garray_T *default_args,
int skip,
exarg_T *eap, // can be NULL
+ class_T *class_arg,
+ garray_T *newlines, // function body lines
garray_T *lines_to_free)
{
int mustend = FALSE;
@@ -292,6 +294,51 @@ get_function_args(
}
}
}
+ else if (class_arg != NULL && STRNCMP(p, "this.", 5) == 0)
+ {
+ // this.memberName
+ p += 5;
+ arg = p;
+ while (ASCII_ISALNUM(*p) || *p == '_')
+ ++p;
+
+ // TODO: check the argument is indeed a member
+ if (newargs != NULL && ga_grow(newargs, 1) == FAIL)
+ return FAIL;
+ if (newargs != NULL)
+ {
+ ((char_u **)(newargs->ga_data))[newargs->ga_len] =
+ vim_strnsave(arg, p - arg);
+ newargs->ga_len++;
+
+ if (argtypes != NULL && ga_grow(argtypes, 1) == OK)
+ {
+ // TODO: use the actual type
+ ((char_u **)argtypes->ga_data)[argtypes->ga_len++] =
+ vim_strsave((char_u *)"any");
+
+ // Add a line to the function body for the assignment.
+ if (ga_grow(newlines, 1) == OK)
+ {
+ // "this.name = name"
+ int len = 5 + (p - arg) + 3 + (p - arg) + 1;
+ char_u *assignment = alloc(len);
+ if (assignment != NULL)
+ {
+ c = *p;
+ *p = NUL;
+ vim_snprintf((char *)assignment, len,
+ "this.%s = %s", arg, arg);
+ *p = c;
+ ((char_u **)(newlines->ga_data))[
+ newlines->ga_len++] = assignment;
+ }
+ }
+ }
+ }
+ if (*p == ',')
+ ++p;
+ }
else
{
char_u *np;
@@ -1389,7 +1436,7 @@ get_lambda_tv(
s = *arg + 1;
ret = get_function_args(&s, equal_arrow ? ')' : '-', NULL,
types_optional ? &argtypes : NULL, types_optional, evalarg,
- NULL, &default_args, TRUE, NULL, NULL);
+ NULL, &default_args, TRUE, NULL, NULL, NULL, NULL);
if (ret == FAIL || skip_arrow(s, equal_arrow, &ret_type, NULL) == NULL)
{
if (types_optional)
@@ -1406,7 +1453,7 @@ get_lambda_tv(
ret = get_function_args(arg, equal_arrow ? ')' : '-', pnewargs,
types_optional ? &argtypes : NULL, types_optional, evalarg,
&varargs, &default_args,
- FALSE, NULL, NULL);
+ FALSE, NULL, NULL, NULL, NULL);
if (ret == FAIL
|| (s = skip_arrow(*arg, equal_arrow, &ret_type,
equal_arrow || vim9script ? &white_error : NULL)) == NULL)
@@ -1733,7 +1780,7 @@ emsg_funcname(char *ermsg, char_u *name)
* Return them in "*argvars[MAX_FUNC_ARGS + 1]" and the count in "argcount".
* On failure FAIL is returned but the "argvars[argcount]" are still set.
*/
- static int
+ int
get_func_arguments(
char_u **arg,
evalarg_T *evalarg,
@@ -1809,7 +1856,7 @@ get_func_tv(
funcexe_T *funcexe) // various values
{
char_u *argp;
- int ret = OK;
+ int ret;
typval_T argvars[MAX_FUNC_ARGS + 1]; // vars for arguments
int argcount = 0; // number of arguments found
int vim9script = in_vim9script();
@@ -4370,10 +4417,15 @@ list_functions(regmatch_T *regmatch)
* When "name_arg" is not NULL this is a nested function, using "name_arg" for
* the function name.
* "lines_to_free" is a list of strings to be freed later.
+ * If "class_arg" is not NULL then the function is defi