diff options
author | Bram Moolenaar <Bram@vim.org> | 2022-12-08 15:32:33 +0000 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2022-12-08 15:32:33 +0000 |
commit | 00b28d6c23d8e662cab27e461825777c0a2e387a (patch) | |
tree | ba11ed85b20cf03491e0f24b6d5bf348d2c19388 | |
parent | 038e6d20e680ce8c850d07f6b035c4e1904c1201 (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.txt | 9 | ||||
-rw-r--r-- | src/errors.h | 24 | ||||
-rw-r--r-- | src/eval.c | 47 | ||||
-rw-r--r-- | src/evalfunc.c | 14 | ||||
-rw-r--r-- | src/evalvars.c | 2 | ||||
-rw-r--r-- | src/if_py_both.h | 2 | ||||
-rw-r--r-- | src/json.c | 2 | ||||
-rw-r--r-- | src/proto/userfunc.pro | 3 | ||||
-rw-r--r-- | src/proto/vim9class.pro | 6 | ||||
-rw-r--r-- | src/proto/vim9instr.pro | 1 | ||||
-rw-r--r-- | src/proto/vim9script.pro | 2 | ||||
-rw-r--r-- | src/proto/vim9type.pro | 1 | ||||
-rw-r--r-- | src/structs.h | 67 | ||||
-rw-r--r-- | src/testdir/Make_all.mak | 2 | ||||
-rw-r--r-- | src/testdir/test_vim9_class.vim | 145 | ||||
-rw-r--r-- | src/testing.c | 2 | ||||
-rw-r--r-- | src/typval.c | 46 | ||||
-rw-r--r-- | src/userfunc.c | 71 | ||||
-rw-r--r-- | src/version.c | 2 | ||||
-rw-r--r-- | src/vim.h | 2 | ||||
-rw-r--r-- | src/vim9.h | 12 | ||||
-rw-r--r-- | src/vim9class.c | 493 | ||||
-rw-r--r-- | src/vim9compile.c | 77 | ||||
-rw-r--r-- | src/vim9execute.c | 52 | ||||
-rw-r--r-- | src/vim9expr.c | 2 | ||||
-rw-r--r-- | src/vim9instr.c | 22 | ||||
-rw-r--r-- | src/vim9script.c | 11 | ||||
-rw-r--r-- | src/vim9type.c | 19 | ||||
-rw-r--r-- | src/viminfo.c | 2 |
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) |