forked from aniani/vim
patch 9.0.1031: Vim9 class is not implemented yet
Problem: Vim9 class is not implemented yet. Solution: Add very basic class support.
This commit is contained in:
parent
038e6d20e6
commit
00b28d6c23
@ -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.
|
||||
|
24
src/errors.h
24
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
|
||||
|
47
src/eval.c
47
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:
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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 : */
|
||||
|
@ -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);
|
||||
|
@ -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 : */
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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 \
|
||||
|
145
src/testdir/test_vim9_class.vim
Normal file
145
src/testdir/test_vim9_class.vim
Normal file
@ -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
|
@ -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
|
||||
|
46
src/typval.c
46
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;
|
||||
|
||||
|
@ -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 defined in this class.
|
||||
* Returns a pointer to the function or NULL if no function defined.
|
||||
*/
|
||||
ufunc_T *
|
||||
define_function(exarg_T *eap, char_u *name_arg, garray_T *lines_to_free)
|
||||
define_function(
|
||||
exarg_T *eap,
|
||||
char_u *name_arg,
|
||||
garray_T *lines_to_free,
|
||||
class_T *class_arg)
|
||||
{
|
||||
int j;
|
||||
int c;
|
||||
@ -4488,8 +4540,9 @@ define_function(exarg_T *eap, char_u *name_arg, garray_T *lines_to_free)
|
||||
p = eap->arg;
|
||||
}
|
||||
|
||||
name = save_function_name(&p, &is_global, eap->skip,
|
||||
TFN_NO_AUTOLOAD | TFN_NEW_FUNC, &fudi);
|
||||
int tfn_flags = TFN_NO_AUTOLOAD | TFN_NEW_FUNC
|
||||
| (class_arg == 0 ? 0 : TFN_INT);
|
||||
name = save_function_name(&p, &is_global, eap->skip, tfn_flags, &fudi);
|
||||
paren = (vim_strchr(p, '(') != NULL);
|
||||
if (name == NULL && (fudi.fd_dict == NULL || !paren) && !eap->skip)
|
||||
{
|
||||
@ -4690,7 +4743,7 @@ define_function(exarg_T *eap, char_u *name_arg, garray_T *lines_to_free)
|
||||
if (get_function_args(&p, ')', &newargs,
|
||||
eap->cmdidx == CMD_def ? &argtypes : NULL, FALSE,
|
||||
NULL, &varargs, &default_args, eap->skip,
|
||||
eap, lines_to_free) == FAIL)
|
||||
eap, class_arg, &newlines, lines_to_free) == FAIL)
|
||||
goto errret_2;
|
||||
whitep = p;
|
||||
|
||||
@ -5145,7 +5198,7 @@ ex_function(exarg_T *eap)
|
||||
garray_T lines_to_free;
|
||||
|
||||
ga_init2(&lines_to_free, sizeof(char_u *), 50);
|
||||
(void)define_function(eap, NULL, &lines_to_free);
|
||||
(void)define_function(eap, NULL, &lines_to_free, NULL);
|
||||
ga_clear_strings(&lines_to_free);
|
||||
}
|
||||
|
||||
|
@ -695,6 +695,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
1031,
|
||||
/**/
|
||||
1030,
|
||||
/**/
|
||||
|
@ -2120,6 +2120,8 @@ typedef int sock_T;
|
||||
#define VAR_TYPE_CHANNEL 9
|
||||
#define VAR_TYPE_BLOB 10
|
||||
#define VAR_TYPE_INSTR 11
|
||||
#define VAR_TYPE_CLASS 12
|
||||
#define VAR_TYPE_OBJECT 13
|
||||
|
||||
#define DICT_MAXNEST 100 // maximum nesting of lists and dicts
|
||||
|
||||
|
12
src/vim9.h
12
src/vim9.h
@ -32,6 +32,7 @@ typedef enum {
|
||||
|
||||
ISN_SOURCE, // source autoload script, isn_arg.number is the script ID
|
||||
ISN_INSTR, // instructions compiled from expression
|
||||
ISN_CONSTRUCT, // construct an object, using contstruct_T
|
||||
|
||||
// get and set variables
|
||||
ISN_LOAD, // push local variable isn_arg.number
|
||||
@ -110,6 +111,7 @@ typedef enum {
|
||||
ISN_PCALL_END, // cleanup after ISN_PCALL with cpf_top set
|
||||
ISN_RETURN, // return, result is on top of stack
|
||||
ISN_RETURN_VOID, // Push void, then return
|
||||
ISN_RETURN_OBJECT, // Push constructed object, then return
|
||||
ISN_FUNCREF, // push a function ref to dfunc isn_arg.funcref
|
||||
ISN_NEWFUNC, // create a global function from a lambda function
|
||||
ISN_DEF, // list functions
|
||||
@ -463,6 +465,12 @@ typedef struct {
|
||||
long ewin_time; // time argument (msec)
|
||||
} echowin_T;
|
||||
|
||||
// arguments to ISN_CONSTRUCT
|
||||
typedef struct {
|
||||
int construct_size; // size of object in bytes
|
||||
class_T *construct_class; // class the object is created from
|
||||
} construct_T;
|
||||
|
||||
/*
|
||||
* Instruction
|
||||
*/
|
||||
@ -514,6 +522,7 @@ struct isn_S {
|
||||
debug_T debug;
|
||||
deferins_T defer;
|
||||
echowin_T echowin;
|
||||
construct_T construct;
|
||||
} isn_arg;
|
||||
};
|
||||
|
||||
@ -757,7 +766,8 @@ typedef struct {
|
||||
|
||||
int lhs_has_type; // type was specified
|
||||
type_T *lhs_type;
|
||||
type_T *lhs_member_type;
|
||||
int lhs_member_idx; // object member index
|
||||
type_T *lhs_member_type; // list/dict/object member type
|
||||
|
||||
int lhs_append; // used by ISN_REDIREND
|
||||
} lhs_T;
|
||||
|
493
src/vim9class.c
493
src/vim9class.c
@ -27,9 +27,16 @@
|
||||
void
|
||||
ex_class(exarg_T *eap)
|
||||
{
|
||||
int is_abstract = eap->cmdidx == CMD_abstract;
|
||||
if (!current_script_is_vim9()
|
||||
|| (cmdmod.cmod_flags & CMOD_LEGACY)
|
||||
|| !getline_equal(eap->getline, eap->cookie, getsourceline))
|
||||
{
|
||||
emsg(_(e_class_can_only_be_defined_in_vim9_script));
|
||||
return;
|
||||
}
|
||||
|
||||
char_u *arg = eap->arg;
|
||||
int is_abstract = eap->cmdidx == CMD_abstract;
|
||||
if (is_abstract)
|
||||
{
|
||||
if (STRNCMP(arg, "class", 5) != 0 || !VIM_ISWHITE(arg[5]))
|
||||
@ -45,38 +52,286 @@ ex_class(exarg_T *eap)
|
||||
semsg(_(e_class_name_must_start_with_uppercase_letter_str), arg);
|
||||
return;
|
||||
}
|
||||
char_u *name_end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
|
||||
if (!IS_WHITE_OR_NUL(*name_end))
|
||||
{
|
||||
semsg(_(e_white_space_required_after_class_name_str), arg);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO:
|
||||
// generics: <Tkey, Tentry>
|
||||
// generics: <Tkey, Tentry>
|
||||
// extends SomeClass
|
||||
// implements SomeInterface
|
||||
// specifies SomeInterface
|
||||
// check nothing follows
|
||||
|
||||
// TODO: handle "is_export" if it is set
|
||||
|
||||
// TODO: handle until "endclass" is found:
|
||||
// object and class members (public, read access, private):
|
||||
// public this.varname
|
||||
// public static varname
|
||||
// this.varname
|
||||
// static varname
|
||||
// this._varname
|
||||
// static _varname
|
||||
//
|
||||
// constructors:
|
||||
// def new()
|
||||
// enddef
|
||||
// def newOther()
|
||||
// enddef
|
||||
//
|
||||
// methods (object, class, generics):
|
||||
// def someMethod()
|
||||
// enddef
|
||||
// static def someMethod()
|
||||
// enddef
|
||||
// def <Tval> someMethod()
|
||||
// enddef
|
||||
// static def <Tval> someMethod()
|
||||
// enddef
|
||||
garray_T type_list; // list of pointers to allocated types
|
||||
ga_init2(&type_list, sizeof(type_T *), 10);
|
||||
|
||||
// Growarray with object members declared in the class.
|
||||
garray_T objmembers;
|
||||
ga_init2(&objmembers, sizeof(objmember_T), 10);
|
||||
|
||||
// Growarray with object methods declared in the class.
|
||||
garray_T objmethods;
|
||||
ga_init2(&objmethods, sizeof(ufunc_T), 10);
|
||||
|
||||
/*
|
||||
* Go over the body of the class until "endclass" is found.
|
||||
*/
|
||||
char_u *theline = NULL;
|
||||
int success = FALSE;
|
||||
for (;;)
|
||||
{
|
||||
vim_free(theline);
|
||||
theline = eap->getline(':', eap->cookie, 0, GETLINE_CONCAT_ALL);
|
||||
if (theline == NULL)
|
||||
break;
|
||||
char_u *line = skipwhite(theline);
|
||||
|
||||
// TODO:
|
||||
// class members (public, read access, private):
|
||||
// static varname
|
||||
// public static varname
|
||||
// static _varname
|
||||
//
|
||||
// constructors:
|
||||
// def new()
|
||||
// enddef
|
||||
// def newOther()
|
||||
// enddef
|
||||
//
|
||||
// methods (object, class, generics):
|
||||
// def someMethod()
|
||||
// enddef
|
||||
// static def someMethod()
|
||||
// enddef
|
||||
// def <Tval> someMethod()
|
||||
// enddef
|
||||
// static def <Tval> someMethod()
|
||||
// enddef
|
||||
|
||||
char_u *p = line;
|
||||
if (checkforcmd(&p, "endclass", 4))
|
||||
{
|
||||
if (STRNCMP(line, "endclass", 8) != 0)
|
||||
semsg(_(e_command_cannot_be_shortened_str), line);
|
||||
else if (*p == '|' || !ends_excmd2(line, p))
|
||||
semsg(_(e_trailing_characters_str), p);
|
||||
|
||||
success = TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
// "this.varname"
|
||||
// "this._varname"
|
||||
// TODO:
|
||||
// "public this.varname"
|
||||
if (STRNCMP(line, "this", 4) == 0)
|
||||
{
|
||||
if (line[4] != '.' || !eval_isnamec1(line[5]))
|
||||
{
|
||||
semsg(_(e_invalid_object_member_declaration_str), line);
|
||||
break;
|
||||
}
|
||||
char_u *varname = line + 5;
|
||||
char_u *varname_end = to_name_end(varname, FALSE);
|
||||
|
||||
char_u *colon = skipwhite(varname_end);
|
||||
// TODO: accept initialization and figure out type from it
|
||||
if (*colon != ':')
|
||||
{
|
||||
emsg(_(e_type_or_initialization_required));
|
||||
break;
|
||||
}
|
||||
if (VIM_ISWHITE(*varname_end))
|
||||
{
|
||||
semsg(_(e_no_white_space_allowed_before_colon_str), varname);
|
||||
break;
|
||||
}
|
||||
if (!VIM_ISWHITE(colon[1]))
|
||||
{
|
||||
semsg(_(e_white_space_required_after_str_str), ":", varname);
|
||||
break;
|
||||
}
|
||||
|
||||
char_u *type_arg = skipwhite(colon + 1);
|
||||
type_T *type = parse_type(&type_arg, &type_list, TRUE);
|
||||
if (type == NULL)
|
||||
break;
|
||||
|
||||
if (ga_grow(&objmembers, 1) == FAIL)
|
||||
break;
|
||||
objmember_T *m = ((objmember_T *)objmembers.ga_data)
|
||||
+ objmembers.ga_len;
|
||||
m->om_name = vim_strnsave(varname, varname_end - varname);
|
||||
m->om_type = type;
|
||||
++objmembers.ga_len;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
semsg(_(e_not_valid_command_in_class_str), line);
|
||||
break;
|
||||
}
|
||||
}
|
||||
vim_free(theline);
|
||||
|
||||
if (success)
|
||||
{
|
||||
class_T *cl = ALLOC_CLEAR_ONE(class_T);
|
||||
if (cl == NULL)
|
||||
goto cleanup;
|
||||
cl->class_refcount = 1;
|
||||
cl->class_name = vim_strnsave(arg, name_end - arg);
|
||||
|
||||
// Members are used by the new() function, add them here.
|
||||
cl->class_obj_member_count = objmembers.ga_len;
|
||||
cl->class_obj_members = ALLOC_MULT(objmember_T, objmembers.ga_len);
|
||||
if (cl->class_name == NULL
|
||||
|| cl->class_obj_members == NULL)
|
||||
{
|
||||
vim_free(cl->class_name);
|
||||
vim_free(cl->class_obj_members);
|
||||
vim_free(cl);
|
||||
goto cleanup;
|
||||
}
|
||||
mch_memmove(cl->class_obj_members, objmembers.ga_data,
|
||||
sizeof(objmember_T) * objmembers.ga_len);
|
||||
vim_free(objmembers.ga_data);
|
||||
|
||||
int have_new = FALSE;
|
||||
for (int i = 0; i < objmethods.ga_len; ++i)
|
||||
if (STRCMP((((ufunc_T *)objmethods.ga_data) + i)->uf_name,
|
||||
"new") == 0)
|
||||
{
|
||||
have_new = TRUE;
|
||||
break;
|
||||
}
|
||||
if (!have_new)
|
||||
{
|
||||
// No new() method was defined, add the default constructor.
|
||||
garray_T fga;
|
||||
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)
|
||||
ga_concat(&fga, (char_u *)", ");
|
||||
ga_concat(&fga, (char_u *)"this.");
|
||||
objmember_T *m = cl->class_obj_members + i;
|
||||
ga_concat(&fga, (char_u *)m->om_name);
|
||||
}
|
||||
ga_concat(&fga, (char_u *)")\nenddef\n");
|
||||
ga_append(&fga, NUL);
|
||||
|
||||
exarg_T fea;
|
||||
CLEAR_FIELD(fea);
|
||||
fea.cmdidx = CMD_def;
|
||||
fea.cmd = fea.arg = fga.ga_data;
|
||||
|
||||
garray_T lines_to_free;
|
||||
ga_init2(&lines_to_free, sizeof(char_u *), 50);
|
||||
|
||||
ufunc_T *nf = define_function(&fea, NULL, &lines_to_free, cl);
|
||||
|
||||
ga_clear_strings(&lines_to_free);
|
||||
vim_free(fga.ga_data);
|
||||
|
||||
if (nf != NULL && ga_grow(&objmethods, 1) == OK)
|
||||
{
|
||||
((ufunc_T **)objmethods.ga_data)[objmethods.ga_len] = nf;
|
||||
++objmethods.ga_len;
|
||||
|
||||
nf->uf_flags |= FC_NEW;
|
||||
nf->uf_class = cl;
|
||||
nf->uf_ret_type = get_type_ptr(&type_list);
|
||||
if (nf->uf_ret_type != NULL)
|
||||
{
|
||||
nf->uf_ret_type->tt_type = VAR_OBJECT;
|
||||
nf->uf_ret_type->tt_member = (type_T *)cl;
|
||||
nf->uf_ret_type->tt_argcount = 0;
|
||||
nf->uf_ret_type->tt_args = NULL;
|
||||
}
|
||||
cl->class_new_func = nf;
|
||||
}
|
||||
}
|
||||
|
||||
cl->class_obj_method_count = objmethods.ga_len;
|
||||
cl->class_obj_methods = ALLOC_MULT(ufunc_T *, objmethods.ga_len);
|
||||
if (cl->class_obj_methods == NULL)
|
||||
{
|
||||
vim_free(cl->class_name);
|
||||
vim_free(cl->class_obj_members);
|
||||
vim_free(cl->class_obj_methods);
|
||||
vim_free(cl);
|
||||
goto cleanup;
|
||||
}
|
||||
mch_memmove(cl->class_obj_methods, objmethods.ga_data,
|
||||
sizeof(ufunc_T *) * objmethods.ga_len);
|
||||
vim_free(objmethods.ga_data);
|
||||
|
||||
cl->class_type.tt_type = VAR_CLASS;
|
||||
cl->class_type.tt_member = (type_T *)cl;
|
||||
cl->class_type_list = type_list;
|
||||
|
||||
// TODO:
|
||||
// - Add the methods to the class
|
||||
// - array with ufunc_T pointers
|
||||
// - Fill hashtab with object members and methods
|
||||
// - Generate the default new() method, if needed.
|
||||
// Later:
|
||||
// - class members
|
||||
// - class methods
|
||||
|
||||
// Add the class to the script-local variables.
|
||||
typval_T tv;
|
||||
tv.v_type = VAR_CLASS;
|
||||
tv.vval.v_class = cl;
|
||||
set_var_const(cl->class_name, current_sctx.sc_sid,
|
||||
NULL, &tv, FALSE, ASSIGN_DECL, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
for (int i = 0; i < objmembers.ga_len; ++i)
|
||||
{
|
||||
objmember_T *m = ((objmember_T *)objmembers.ga_data) + i;
|
||||
vim_free(m->om_name);
|
||||
}
|
||||
ga_clear(&objmembers);
|
||||
|
||||
ga_clear(&objmethods);
|
||||
clear_type_list(&type_list);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find member "name" in class "cl" and return its type.
|
||||
* When not found t_any is returned.
|
||||
*/
|
||||
type_T *
|
||||
class_member_type(
|
||||
class_T *cl,
|
||||
char_u *name,
|
||||
char_u *name_end,
|
||||
int *member_idx)
|
||||
{
|
||||
*member_idx = -1; // not found (yet)
|
||||
size_t len = name_end - name;
|
||||
|
||||
for (int i = 0; i < cl->class_obj_member_count; ++i)
|
||||
{
|
||||
objmember_T *m = cl->class_obj_members + i;
|
||||
if (STRNCMP(m->om_name, name, len) == 0 && m->om_name[len] == NUL)
|
||||
{
|
||||
*member_idx = i;
|
||||
return m->om_type;
|
||||
}
|
||||
}
|
||||
return &t_any;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -106,5 +361,191 @@ ex_type(exarg_T *eap UNUSED)
|
||||
// TODO
|
||||
}
|
||||
|
||||
/*
|
||||
* Evaluate what comes after a class:
|
||||
* - class member: SomeClass.varname
|
||||
* - class method: SomeClass.SomeMethod()
|
||||
* - class constructor: SomeClass.new()
|
||||
* - object member: someObject.varname
|
||||
* - object method: someObject.SomeMethod()
|
||||
*
|
||||
* "*arg" points to the '.'.
|
||||
* "*arg" is advanced to after the member name or method call.
|
||||
*
|
||||
* Returns FAIL or OK.
|
||||
*/
|
||||
int
|
||||
class_object_index(
|
||||
char_u **arg,
|
||||
typval_T *rettv,
|
||||
evalarg_T *evalarg,
|
||||
int verbose UNUSED) // give error messages
|
||||
{
|
||||
// int evaluate = evalarg != NULL
|
||||
// && (evalarg->eval_flags & EVAL_EVALUATE);
|
||||
|
||||
if (VIM_ISWHITE((*arg)[1]))
|
||||
{
|
||||
semsg(_(e_no_white_space_allowed_after_str_str), ".", *arg);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
++*arg;
|
||||
char_u *name = *arg;
|
||||
char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
|
||||
if (name_end == name)
|
||||
return FAIL;
|
||||
size_t len = name_end - name;
|
||||
|
||||
class_T *cl = rettv->v_type == VAR_CLASS ? rettv->vval.v_class
|
||||
: rettv->vval.v_object->obj_class;
|
||||
if (*name_end == '(')
|
||||
{
|
||||
for (int i = 0; i < cl->class_obj_method_count; ++i)
|
||||
{
|
||||
ufunc_T *fp = cl->class_obj_methods[i];
|
||||
if (STRNCMP(name, fp->uf_name, len) == 0 && fp->uf_name[len] == NUL)
|
||||
{
|
||||
typval_T argvars[MAX_FUNC_ARGS + 1];
|
||||
int argcount = 0;
|
||||
|
||||
char_u *argp = name_end;
|
||||
int ret = get_func_arguments(&argp, evalarg, 0,
|
||||
argvars, &argcount);
|
||||
if (ret == FAIL)
|
||||
return FAIL;
|
||||
|
||||
funcexe_T funcexe;
|
||||
CLEAR_FIELD(funcexe);
|
||||
funcexe.fe_evaluate = TRUE;
|
||||
|
||||
// Call the user function. Result goes into rettv;
|
||||
// TODO: pass the object
|
||||
rettv->v_type = VAR_UNKNOWN;
|
||||
int error = call_user_func_check(fp, argcount, argvars,
|
||||
rettv, &funcexe, NULL);
|
||||
|
||||
// Clear the arguments.
|
||||
for (int idx = 0; idx < argcount; ++idx)
|
||||
clear_tv(&argvars[idx]);
|
||||
|
||||
if (error != FCERR_NONE)
|
||||
{
|
||||
user_func_error(error, printable_func_name(fp),
|
||||
funcexe.fe_found_var);
|
||||
return FAIL;
|
||||
}
|
||||
*arg = argp;
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
|
||||
semsg(_(e_method_not_found_on_class_str_str), cl->class_name, name);
|
||||
}
|
||||
|
||||
else if (rettv->v_type == VAR_OBJECT)
|
||||
{
|
||||
for (int i = 0; i < cl->class_obj_member_count; ++i)
|
||||
{
|
||||
objmember_T *m = &cl->class_obj_members[i];
|
||||
if (STRNCMP(name, m->om_name, len) == 0 && m->om_name[len] == NUL)
|
||||
{
|
||||
// The object only contains a pointer to the class, the member
|
||||
// values array follows right after that.
|
||||
object_T *obj = rettv->vval.v_object;
|
||||
typval_T *tv = (typval_T *)(obj + 1) + i;
|
||||
copy_tv(tv, rettv);
|
||||
object_unref(obj);
|
||||
|
||||
*arg = name_end;
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
|
||||
semsg(_(e_member_not_found_on_object_str_str), cl->class_name, name);
|
||||
}
|
||||
|
||||
// TODO: class member
|
||||
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make a copy of an object.
|
||||
*/
|
||||
void
|
||||
copy_object(typval_T *from, typval_T *to)
|
||||
{
|
||||
*to = *from;
|
||||
if (to->vval.v_object != NULL)
|
||||
++to->vval.v_object->obj_refcount;
|
||||
}
|
||||
|
||||
/*
|
||||
* Free an object.
|
||||
*/
|
||||
static void
|
||||
object_clear(object_T *obj)
|
||||
{
|
||||
class_T *cl = obj->obj_class;
|
||||
|
||||
// the member values are just after the object structure
|
||||
typval_T *tv = (typval_T *)(obj + 1);
|
||||
for (int i = 0; i < cl->class_obj_member_count; ++i)
|
||||
clear_tv(tv + i);
|
||||
|
||||
vim_free(obj);
|
||||
}
|
||||
|
||||
/*
|
||||
* Unreference an object.
|
||||
*/
|
||||
void
|
||||
object_unref(object_T *obj)
|
||||
{
|
||||
if (obj != NULL && --obj->obj_refcount <= 0)
|
||||
object_clear(obj);
|
||||
}
|
||||
|
||||
/*
|
||||
* Make a copy of a class.
|
||||
*/
|
||||
void
|
||||
copy_class(typval_T *from, typval_T *to)
|
||||
{
|
||||
*to = *from;
|
||||
if (to->vval.v_class != NULL)
|
||||
++to->vval.v_class->class_refcount;
|
||||
}
|
||||
|
||||
/*
|
||||
* Unreference a class. Free it when the reference count goes down to zero.
|
||||
*/
|
||||
void
|
||||
class_unref(typval_T *tv)
|
||||
{
|
||||
class_T *cl = tv->vval.v_class;
|
||||
if (cl != NULL && --cl->class_refcount <= 0)
|
||||
{
|
||||
vim_free(cl->class_name);
|
||||
|
||||
for (int i = 0; i < cl->class_obj_member_count; ++i)
|
||||
{
|
||||
objmember_T *m = &cl->class_obj_members[i];
|
||||
vim_free(m->om_name);
|
||||
}
|
||||
vim_free(cl->class_obj_members);
|
||||
|
||||
vim_free(cl->class_obj_methods);
|
||||
|
||||
if (cl->class_new_func != NULL)
|
||||
func_ptr_unref(cl->class_new_func);
|
||||
|
||||
clear_type_list(&cl->class_type_list);
|
||||
|
||||
vim_free(cl);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif // FEAT_EVAL
|
||||
|
@ -43,6 +43,20 @@ lookup_local(char_u *name, size_t len, lvar_T *lvar, cctx_T *cctx)
|
||||
if (len == 0)
|
||||
return FAIL;
|
||||
|
||||
if (len == 4 && STRNCMP(name, "this", 4) == 0
|
||||
&& cctx->ctx_ufunc != NULL
|
||||
&& (cctx->ctx_ufunc->uf_flags & FC_OBJECT))
|
||||
{
|
||||
if (lvar != NULL)
|
||||
{
|
||||
CLEAR_POINTER(lvar);
|
||||
lvar->lv_name = (char_u *)"this";
|
||||
if (cctx->ctx_ufunc->uf_class != NULL)
|
||||
lvar->lv_type = &cctx->ctx_ufunc->uf_class->class_type;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
// Find local in current function scope.
|
||||
for (idx = 0; idx < cctx->ctx_locals.ga_len; ++idx)
|
||||
{
|
||||
@ -296,7 +310,11 @@ variable_exists(char_u *name, size_t len, cctx_T *cctx)
|
||||
{
|
||||
return (cctx != NULL
|
||||
&& (lookup_local(name, len, NULL, cctx) == OK
|
||||
|| arg_exists(name, len, NULL, NULL, NULL, cctx) == OK))
|
||||
|| arg_exists(name, len, NULL, NULL, NULL, cctx) == OK
|
||||
|| (len == 4
|
||||
&& cctx->ctx_ufunc != NULL
|
||||
&& (cctx->ctx_ufunc->uf_flags & FC_OBJECT)
|
||||
&& STRNCMP(name, "this", 4) == 0)))
|
||||
|| script_var_exists(name, len, cctx, NULL) == OK
|
||||
|| find_imported(name, len, FALSE) != NULL;
|
||||
}
|
||||
@ -957,7 +975,7 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx, garray_T *lines_to_free)
|
||||
goto theend;
|
||||
}
|
||||
|
||||
ufunc = define_function(eap, lambda_name, lines_to_free);
|
||||
ufunc = define_function(eap, lambda_name, lines_to_free, NULL);
|
||||
if (ufunc == NULL)
|
||||
{
|
||||
r = eap->skip ? OK : FAIL;
|
||||
@ -1450,6 +1468,7 @@ compile_lhs(
|
||||
lhs->lhs_dest = dest_local;
|
||||
lhs->lhs_vimvaridx = -1;
|
||||
lhs->lhs_scriptvar_idx = -1;
|
||||
lhs->lhs_member_idx = -1;
|
||||
|
||||
// "dest_end" is the end of the destination, including "[expr]" or
|
||||
// ".name".
|
||||
@ -1509,7 +1528,7 @@ compile_lhs(
|
||||
else
|
||||
{
|
||||
// No specific kind of variable recognized, just a name.
|
||||
if (check_reserved_name(lhs->lhs_name) == FAIL)
|
||||
if (check_reserved_name(lhs->lhs_name, cctx) == FAIL)
|
||||
return FAIL;
|
||||
|
||||
if (lookup_local(var_start, lhs->lhs_varlen,
|
||||
@ -1757,8 +1776,16 @@ compile_lhs(
|
||||
lhs->lhs_type = &t_any;
|
||||
}
|
||||
|
||||
if (lhs->lhs_type->tt_member == NULL)
|
||||
if (lhs->lhs_type == NULL || lhs->lhs_type->tt_member == NULL)
|
||||
lhs->lhs_member_type = &t_any;
|
||||
else if (lhs->lhs_type->tt_type == VAR_CLASS
|
||||
|| lhs->lhs_type->tt_type == VAR_OBJECT)
|
||||
{
|
||||
// for an object or class member get the type of the member
|
||||
class_T *cl = (class_T *)lhs->lhs_type->tt_member;
|
||||
lhs->lhs_member_type = class_member_type(cl, after + 1,
|
||||
lhs->lhs_end, &lhs->lhs_member_idx);
|
||||
}
|
||||
else
|
||||
lhs->lhs_member_type = lhs->lhs_type->tt_member;
|
||||
}
|
||||
@ -1880,6 +1907,11 @@ compile_assign_index(
|
||||
r = FAIL;
|
||||
}
|
||||
}
|
||||
else if (lhs->lhs_member_idx >= 0)
|
||||
{
|
||||
// object member index
|
||||
r = generate_PUSHNR(cctx, lhs->lhs_member_idx);
|
||||
}
|
||||
else // if (*p == '.')
|
||||
{
|
||||
char_u *key_end = to_name_end(p + 1, TRUE);
|
||||
@ -1996,7 +2028,7 @@ compile_assign_unlet(
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
if (lhs->lhs_type == &t_any)
|
||||
if (lhs->lhs_type == NULL || lhs->lhs_type == &t_any)
|
||||
{
|
||||
// Index on variable of unknown type: check at runtime.
|
||||
dest_type = VAR_ANY;
|
||||
@ -2042,8 +2074,12 @@ compile_assign_unlet(
|
||||
if (compile_load_lhs(lhs, var_start, rhs_type, cctx) == FAIL)
|
||||
return FAIL;
|
||||
|
||||
if (dest_type == VAR_LIST || dest_type == VAR_DICT
|
||||
|| dest_type == VAR_BLOB || dest_type == VAR_ANY)
|
||||
if (dest_type == VAR_LIST
|
||||
|| dest_type == VAR_DICT
|
||||
|| dest_type == VAR_BLOB
|
||||
|| dest_type == VAR_CLASS
|
||||
|| dest_type == VAR_OBJECT
|
||||
|| dest_type == VAR_ANY)
|
||||
{
|
||||
if (is_assign)
|
||||
{
|
||||
@ -2466,6 +2502,8 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
|
||||
case VAR_PARTIAL:
|
||||
case VAR_VOID:
|
||||
case VAR_INSTR:
|
||||
case VAR_CLASS:
|
||||
case VAR_OBJECT:
|
||||
case VAR_SPECIAL: // cannot happen
|
||||
// This is skipped for local variables, they are always
|
||||
// initialized to zero. But in a "for" or "while" loop
|
||||
@ -2897,6 +2935,22 @@ compile_def_function(
|
||||
if (check_args_shadowing(ufunc, &cctx) == FAIL)
|
||||
goto erret;
|
||||
|
||||
// For an object method and constructor "this" is the first local variable.
|
||||
if (ufunc->uf_flags & FC_OBJECT)
|
||||
{
|
||||
dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
|
||||
+ ufunc->uf_dfunc_idx;
|
||||
if (GA_GROW_FAILS(&dfunc->df_var_names, 1))
|
||||
goto erret;
|
||||
((char_u **)dfunc->df_var_names.ga_data)[0] =
|
||||
vim_strsave((char_u *)"this");
|
||||
++dfunc->df_var_names.ga_len;
|
||||
|
||||
// In the constructor allocate memory for the object.
|
||||
if ((ufunc->uf_flags & FC_NEW) == FC_NEW)
|
||||
generate_CONSTRUCT(&cctx, ufunc->uf_class);
|
||||
}
|
||||
|
||||
if (ufunc->uf_def_args.ga_len > 0)
|
||||
{
|
||||
int count = ufunc->uf_def_args.ga_len;
|
||||
@ -3500,14 +3554,19 @@ nextline:
|
||||
{
|
||||
if (ufunc->uf_ret_type->tt_type == VAR_UNKNOWN)
|
||||
ufunc->uf_ret_type = &t_void;
|
||||
else if (ufunc->uf_ret_type->tt_type != VAR_VOID)
|
||||
else if (ufunc->uf_ret_type->tt_type != VAR_VOID
|
||||
&& (ufunc->uf_flags & FC_NEW) != FC_NEW)
|
||||
{
|
||||
emsg(_(e_missing_return_statement));
|
||||
goto erret;
|
||||
}
|
||||
|
||||
// Return void if there is no return at the end.
|
||||
generate_instr(&cctx, ISN_RETURN_VOID);
|
||||
// For a constructor return the object.
|
||||
if ((ufunc->uf_flags & FC_NEW) == FC_NEW)
|
||||
generate_instr(&cctx, ISN_RETURN_OBJECT);
|
||||
else
|
||||
generate_instr(&cctx, ISN_RETURN_VOID);
|
||||
}
|
||||
|
||||
// When compiled with ":silent!" and there was an error don't consider the
|
||||
|
@ -2029,6 +2029,7 @@ handle_debug(isn_T *iptr, ectx_T *ectx)
|
||||
for (ni = iptr + 1; ni->isn_type != ISN_FINISH; ++ni)
|
||||
if (ni->isn_type == ISN_DEBUG
|
||||
|| ni->isn_type == ISN_RETURN
|
||||
|| ni->isn_type == ISN_RETURN_OBJECT
|
||||
|| ni->isn_type == ISN_RETURN_VOID)
|
||||
{
|
||||
end_lnum = ni->isn_lnum + (ni->isn_type == ISN_DEBUG ? 0 : 1);
|
||||
@ -2082,7 +2083,7 @@ execute_storeindex(isn_T *iptr, ectx_T *ectx)
|
||||
// Stack contains:
|
||||
// -3 value to be stored
|
||||
// -2 index
|
||||
// -1 dict or list
|
||||
// -1 dict, list, blob or object
|
||||
tv = STACK_TV_BOT(-3);
|
||||
SOURCING_LNUM = iptr->isn_lnum;
|
||||
if (dest_type == VAR_ANY)
|
||||
@ -2203,6 +2204,13 @@ execute_storeindex(isn_T *iptr, ectx_T *ectx)
|
||||
return FAIL;
|
||||
blob_set_append(blob, lidx, nr);
|
||||
}
|
||||
else if (dest_type == VAR_CLASS || dest_type == VAR_OBJECT)
|
||||
{
|
||||
long idx = (long)tv_idx->vval.v_number;
|
||||
object_T *obj = tv_dest->vval.v_object;
|
||||
typval_T *otv = (typval_T *)(obj + 1);
|
||||
otv[idx] = *tv;
|
||||
}
|
||||
else
|
||||
{
|
||||
status = FAIL;
|
||||
@ -3001,6 +3009,18 @@ exec_instructions(ectx_T *ectx)
|
||||
iptr = &ectx->ec_instr[ectx->ec_iidx++];
|
||||
switch (iptr->isn_type)
|
||||
{
|
||||
// Constructor, new() method.
|
||||
case ISN_CONSTRUCT:
|
||||
// "this" is always the local variable at index zero
|
||||
tv = STACK_TV_VAR(0);
|
||||
tv->v_type = VAR_OBJECT;
|
||||
tv->vval.v_object = alloc_clear(
|
||||
iptr->isn_arg.construct.construct_size);
|
||||
tv->vval.v_object->obj_class =
|
||||
iptr->isn_arg.construct.construct_class;
|
||||
tv->vval.v_object->obj_refcount = 1;
|
||||
break;
|
||||
|
||||
// execute Ex command line
|
||||
case ISN_EXEC:
|
||||
if (exec_command(iptr) == FAIL)
|
||||
@ -4092,15 +4112,25 @@ exec_instructions(ectx_T *ectx)
|
||||
goto on_error;
|
||||
break;
|
||||
|
||||
// return from a :def function call without a value
|
||||
// Return from a :def function call without a value.
|
||||
// Return from a constructor.
|
||||
case ISN_RETURN_VOID:
|
||||
case ISN_RETURN_OBJECT:
|
||||
if (GA_GROW_FAILS(&ectx->ec_stack, 1))
|
||||
goto theend;
|
||||
tv = STACK_TV_BOT(0);
|
||||
++ectx->ec_stack.ga_len;
|
||||
tv->v_type = VAR_VOID;
|
||||
tv->vval.v_number = 0;
|
||||
tv->v_lock = 0;
|
||||
if (iptr->isn_type == ISN_RETURN_VOID)
|
||||
{
|
||||
tv->v_type = VAR_VOID;
|
||||
tv->vval.v_number = 0;
|
||||
tv->v_lock = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
*tv = *STACK_TV_VAR(0);
|
||||
++tv->vval.v_object->obj_refcount;
|
||||
}
|
||||
// FALLTHROUGH
|
||||
|
||||
// return from a :def function call with what is on the stack
|
||||
@ -4193,7 +4223,7 @@ exec_instructions(ectx_T *ectx)
|
||||
CLEAR_FIELD(ea);
|
||||
ea.cmd = ea.arg = iptr->isn_arg.string;
|
||||
ga_init2(&lines_to_free, sizeof(char_u *), 50);
|
||||
define_function(&ea, NULL, &lines_to_free);
|
||||
define_function(&ea, NULL, &lines_to_free, NULL);
|
||||
ga_clear_strings(&lines_to_free);
|
||||
}
|
||||
break;
|
||||
@ -6018,6 +6048,11 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
|
||||
|
||||
switch (iptr->isn_type)
|
||||
{
|
||||
case ISN_CONSTRUCT:
|
||||
smsg("%s%4d NEW %s size %d", pfx, current,
|
||||
iptr->isn_arg.construct.construct_class->class_name,
|
||||
(int)iptr->isn_arg.construct.construct_size);
|
||||
break;
|
||||
case ISN_EXEC:
|
||||
smsg("%s%4d EXEC %s", pfx, current, iptr->isn_arg.string);
|
||||
break;
|
||||
@ -6447,6 +6482,9 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
|
||||
case ISN_RETURN_VOID:
|
||||
smsg("%s%4d RETURN void", pfx, current);
|
||||
break;
|
||||
case ISN_RETURN_OBJECT:
|
||||
smsg("%s%4d RETURN object", pfx, current);
|
||||
break;
|
||||
case ISN_FUNCREF:
|
||||
{
|
||||
funcref_T *funcref = &iptr->isn_arg.funcref;
|
||||
@ -6979,6 +7017,8 @@ tv2bool(typval_T *tv)
|
||||
case VAR_ANY:
|
||||
case VAR_VOID:
|
||||
case VAR_INSTR:
|
||||
case VAR_CLASS:
|
||||
case VAR_OBJECT:
|
||||
break;
|
||||
}
|
||||
return FALSE;
|
||||
|
@ -235,6 +235,8 @@ compile_member(int is_slice, int *keeping_dict, cctx_T *cctx)
|
||||
case VAR_JOB:
|
||||
case VAR_CHANNEL:
|
||||
case VAR_INSTR:
|
||||
case VAR_CLASS:
|
||||
case VAR_OBJECT:
|
||||
case VAR_UNKNOWN:
|
||||
case VAR_ANY:
|
||||
case VAR_VOID:
|
||||
|
@ -113,6 +113,24 @@ generate_instr_debug(cctx_T *cctx)
|
||||
return isn;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate an ISN_CONSTRUCT instruction.
|
||||
* The object will have "size" members.
|
||||
*/
|
||||
int
|
||||
generate_CONSTRUCT(cctx_T *cctx, class_T *cl)
|
||||
{
|
||||
isn_T *isn;
|
||||
|
||||
RETURN_OK_IF_SKIP(cctx);
|
||||
if ((isn = generate_instr(cctx, ISN_CONSTRUCT)) == NULL)
|
||||
return FAIL;
|
||||
isn->isn_arg.construct.construct_size = sizeof(object_T)
|
||||
+ cl->class_obj_member_count * sizeof(typval_T);
|
||||
isn->isn_arg.construct.construct_class = cl;
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* If type at "offset" isn't already VAR_STRING then generate ISN_2STRING.
|
||||
* But only for simple types.
|
||||
@ -163,6 +181,8 @@ may_generate_2STRING(int offset, int tolerant, cctx_T *cctx)
|
||||
case VAR_JOB:
|
||||
case VAR_CHANNEL:
|
||||
case VAR_INSTR:
|
||||
case VAR_CLASS:
|
||||
case VAR_OBJECT:
|
||||
to_string_error(type->tt_type);
|
||||
return FAIL;
|
||||
}
|
||||
@ -2403,6 +2423,7 @@ delete_instr(isn_T *isn)
|
||||
case ISN_COMPARESPECIAL:
|
||||
case ISN_COMPARESTRING:
|
||||
case ISN_CONCAT:
|
||||
case ISN_CONSTRUCT:
|
||||
case ISN_COND2BOOL:
|
||||
case ISN_DEBUG:
|
||||
case ISN_DEFER:
|
||||
@ -2457,6 +2478,7 @@ delete_instr(isn_T *isn)
|
||||
case ISN_REDIRSTART:
|
||||
case ISN_RETURN:
|
||||
case ISN_RETURN_VOID:
|
||||
case ISN_RETURN_OBJECT:
|
||||
case ISN_SHUFFLE:
|
||||
case ISN_SLICE:
|
||||
case ISN_SOURCE:
|
||||
|
@ -838,7 +838,7 @@ vim9_declare_scriptvar(exarg_T *eap, char_u *arg)
|
||||
// parse type, check for reserved name
|
||||
p = skipwhite(p + 1);
|
||||
type = parse_type(&p, &si->sn_type_list, TRUE);
|
||||
if (type == NULL || check_reserved_name(name) == FAIL)
|
||||
if (type == NULL || check_reserved_name(name, NULL) == FAIL)
|
||||
{
|
||||
vim_free(name);
|
||||
return p;
|
||||
@ -1126,12 +1126,17 @@ static char *reserved[] = {
|
||||
};
|
||||
|
||||
int
|
||||
check_reserved_name(char_u *name)
|
||||
check_reserved_name(char_u *name, cctx_T *cctx)
|
||||
{
|
||||
int idx;
|
||||
|
||||
for (idx = 0; reserved[idx] != NULL; ++idx)
|
||||
if (STRCMP(reserved[idx], name) == 0)
|
||||
if (STRCMP(reserved[idx], name) == 0
|
||||
// "this" can be used in an object method
|
||||
&& !(STRCMP("this", name) == 0
|
||||
&& cctx != NULL
|
||||
&& cctx->ctx_ufunc != NULL
|
||||
&& (cctx->ctx_ufunc->uf_flags & FC_OBJECT)))
|
||||
{
|
||||
semsg(_(e_cannot_use_reserved_name), name);
|
||||
return FAIL;
|
||||
|
@ -29,7 +29,7 @@
|
||||
* Allocate memory for a type_T and add the pointer to type_gap, so that it can
|
||||
* be easily freed later.
|
||||
*/
|
||||
static type_T *
|
||||
type_T *
|
||||
get_type_ptr(garray_T *type_gap)
|
||||
{
|
||||
type_T *type;
|
||||
@ -94,7 +94,12 @@ alloc_type(type_T *type)
|
||||
*ret = *type;
|
||||
|
||||
if (ret->tt_member != NULL)
|
||||
ret->tt_member = alloc_type(ret->tt_member);
|
||||
{
|
||||
// tt_member points to the class_T for VAR_CLASS and VAR_OBJECT
|
||||
if (type->tt_type != VAR_CLASS && type->tt_type != VAR_OBJECT)
|
||||
ret->tt_member = alloc_type(ret->tt_member);
|
||||
}
|
||||
|
||||
if (type->tt_args != NULL)
|
||||
{
|
||||
int i;
|
||||
@ -124,7 +129,11 @@ free_type(type_T *type)
|
||||
free_type(type->tt_args[i]);
|
||||
vim_free(type->tt_args);
|
||||
}
|
||||
free_type(type->tt_member);
|
||||
|
||||
// for an object and class tt_member is a pointer to the class
|
||||
if (type->tt_type != VAR_OBJECT && type->tt_type != VAR_CLASS)
|
||||
free_type(type->tt_member);
|
||||
|
||||
vim_free(type);
|
||||
}
|
||||
|
||||
@ -1203,6 +1212,8 @@ equal_type(type_T *type1, type_T *type2, int flags)
|
||||
case VAR_JOB:
|
||||
case VAR_CHANNEL:
|
||||
case VAR_INSTR:
|
||||
case VAR_CLASS:
|
||||
case VAR_OBJECT:
|
||||
break; // not composite is always OK
|
||||
case VAR_LIST:
|
||||
case VAR_DICT:
|
||||
@ -1451,6 +1462,8 @@ vartype_name(vartype_T type)
|
||||
case VAR_LIST: return "list";
|
||||
case VAR_DICT: return "dict";
|
||||
case VAR_INSTR: return "instr";
|
||||
case VAR_CLASS: return "class";
|
||||
case VAR_OBJECT: return "object";
|
||||
|
||||
case VAR_FUNC:
|
||||
case VAR_PARTIAL: return "func";
|
||||
|
@ -1370,6 +1370,8 @@ write_viminfo_varlist(FILE *fp)
|
||||
case VAR_JOB:
|
||||
case VAR_CHANNEL:
|
||||
case VAR_INSTR:
|
||||
case VAR_CLASS:
|
||||
case VAR_OBJECT:
|
||||
continue;
|
||||
}
|
||||
fprintf(fp, "!%s\t%s\t", this_var->di_key, s);
|
||||
|
Loading…
x
Reference in New Issue
Block a user