mirror of
https://github.com/vim/vim.git
synced 2025-09-24 03:44:06 -04:00
patch 9.1.0219: Vim9: No enum support
Problem: No enum support Solution: Implement enums for Vim9 script (Yegappan Lakshmanan) closes: #14224 Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
committed by
Christian Brabandt
parent
8ede7a0694
commit
3164cf8f12
519
src/vim9class.c
519
src/vim9class.c
@@ -168,7 +168,8 @@ struct oc_newmember_S
|
||||
/*
|
||||
* Add a member to an object or a class.
|
||||
* Returns OK when successful, "init_expr" will be consumed then.
|
||||
* Returns FAIL otherwise, caller might need to free "init_expr".
|
||||
* Returns OK on success and FAIL on memory allocation failure (caller might
|
||||
* need to free "init_expr").
|
||||
*/
|
||||
static int
|
||||
add_member(
|
||||
@@ -323,13 +324,15 @@ validate_extends_class(
|
||||
}
|
||||
|
||||
if (tv.v_type != VAR_CLASS || tv.vval.v_class == NULL
|
||||
|| (is_class
|
||||
&& (tv.vval.v_class->class_flags & CLASS_INTERFACE) != 0)
|
||||
|| (!is_class
|
||||
&& (tv.vval.v_class->class_flags & CLASS_INTERFACE) == 0))
|
||||
// a interface cannot extend a class and a class cannot extend an
|
||||
// interface.
|
||||
|| (is_class && IS_INTERFACE(tv.vval.v_class))
|
||||
|| (!is_class && !IS_INTERFACE(tv.vval.v_class))
|
||||
|| (is_class && IS_ENUM(tv.vval.v_class)))
|
||||
{
|
||||
// a class cannot extend an interface
|
||||
// an interface cannot extend a class
|
||||
// a class cannot extend an enum.
|
||||
semsg(_(e_cannot_extend_str), extends_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
class_T *extends_cl = tv.vval.v_class;
|
||||
@@ -793,7 +796,7 @@ validate_implements_classes(
|
||||
|
||||
if (tv.v_type != VAR_CLASS
|
||||
|| tv.vval.v_class == NULL
|
||||
|| (tv.vval.v_class->class_flags & CLASS_INTERFACE) == 0)
|
||||
|| !IS_INTERFACE(tv.vval.v_class))
|
||||
{
|
||||
semsg(_(e_not_valid_interface_str), impl);
|
||||
success = FALSE;
|
||||
@@ -1234,14 +1237,14 @@ add_lookup_tables(class_T *cl, class_T *extends_cl, garray_T *objmethods_gap)
|
||||
* Add class members to a new class. Allocate a typval for each class member
|
||||
* and initialize it.
|
||||
*/
|
||||
static void
|
||||
static int
|
||||
add_class_members(class_T *cl, exarg_T *eap, garray_T *type_list_gap)
|
||||
{
|
||||
// Allocate a typval for each class member and initialize it.
|
||||
cl->class_members_tv = ALLOC_CLEAR_MULT(typval_T,
|
||||
cl->class_class_member_count);
|
||||
if (cl->class_members_tv == NULL)
|
||||
return;
|
||||
return FAIL;
|
||||
|
||||
for (int i = 0; i < cl->class_class_member_count; ++i)
|
||||
{
|
||||
@@ -1250,19 +1253,19 @@ add_class_members(class_T *cl, exarg_T *eap, garray_T *type_list_gap)
|
||||
if (m->ocm_init != NULL)
|
||||
{
|
||||
typval_T *etv = eval_expr(m->ocm_init, eap);
|
||||
if (etv != NULL)
|
||||
{
|
||||
if (m->ocm_type->tt_type == VAR_ANY
|
||||
&& !(m->ocm_flags & OCMFLAG_HAS_TYPE)
|
||||
&& etv->v_type != VAR_SPECIAL)
|
||||
// If the member variable type is not yet set, then use
|
||||
// the initialization expression type.
|
||||
m->ocm_type = typval2type(etv, get_copyID(),
|
||||
type_list_gap,
|
||||
TVTT_DO_MEMBER|TVTT_MORE_SPECIFIC);
|
||||
*tv = *etv;
|
||||
vim_free(etv);
|
||||
}
|
||||
if (etv == NULL)
|
||||
return FAIL;
|
||||
|
||||
if (m->ocm_type->tt_type == VAR_ANY
|
||||
&& !(m->ocm_flags & OCMFLAG_HAS_TYPE)
|
||||
&& etv->v_type != VAR_SPECIAL)
|
||||
// If the member variable type is not yet set, then use
|
||||
// the initialization expression type.
|
||||
m->ocm_type = typval2type(etv, get_copyID(),
|
||||
type_list_gap,
|
||||
TVTT_DO_MEMBER|TVTT_MORE_SPECIFIC);
|
||||
*tv = *etv;
|
||||
vim_free(etv);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1273,6 +1276,8 @@ add_class_members(class_T *cl, exarg_T *eap, garray_T *type_list_gap)
|
||||
if (m->ocm_flags & OCMFLAG_CONST)
|
||||
item_lock(tv, DICT_MAXNEST, TRUE, TRUE);
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1284,13 +1289,21 @@ add_default_constructor(
|
||||
garray_T *classfunctions_gap,
|
||||
garray_T *type_list_gap)
|
||||
{
|
||||
garray_T fga;
|
||||
garray_T fga;
|
||||
int is_enum = IS_ENUM(cl);
|
||||
|
||||
ga_init2(&fga, 1, 1000);
|
||||
ga_concat(&fga, (char_u *)"new(");
|
||||
for (int i = 0; i < cl->class_obj_member_count; ++i)
|
||||
{
|
||||
if (i > 0)
|
||||
if (i < 2 && is_enum)
|
||||
// The first two object variables in an enum are the enum value
|
||||
// name and ordinal. Don't initialize these object variables in
|
||||
// the default constructor as they are already initialized right
|
||||
// after creating the object.
|
||||
continue;
|
||||
|
||||
if (i > (is_enum ? 2 : 0))
|
||||
ga_concat(&fga, (char_u *)", ");
|
||||
ga_concat(&fga, (char_u *)"this.");
|
||||
ocmember_T *m = cl->class_obj_members + i;
|
||||
@@ -1336,6 +1349,7 @@ add_default_constructor(
|
||||
* Add the class methods and object methods to the new class "cl".
|
||||
* When extending a class "extends_cl", add the instance methods from the
|
||||
* parent class also.
|
||||
* Returns OK on success and FAIL on memory allocation failure.
|
||||
*/
|
||||
static int
|
||||
add_classfuncs_objmethods(
|
||||
@@ -1373,7 +1387,7 @@ add_classfuncs_objmethods(
|
||||
|
||||
if (gap->ga_len != 0)
|
||||
mch_memmove(*fup, gap->ga_data, sizeof(ufunc_T *) * gap->ga_len);
|
||||
vim_free(gap->ga_data);
|
||||
VIM_CLEAR(gap->ga_data);
|
||||
if (loop == 1)
|
||||
cl->class_class_function_count_child = gap->ga_len;
|
||||
else
|
||||
@@ -1422,6 +1436,8 @@ add_classfuncs_objmethods(
|
||||
if (loop == 2)
|
||||
fp->uf_flags |= FC_OBJECT;
|
||||
}
|
||||
|
||||
ga_clear(gap);
|
||||
}
|
||||
|
||||
return OK;
|
||||
@@ -1471,6 +1487,246 @@ find_class_name_end(char_u *arg)
|
||||
return end;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns TRUE if the enum value "varname" is already defined.
|
||||
*/
|
||||
static int
|
||||
is_duplicate_enum(
|
||||
garray_T *enum_gap,
|
||||
char_u *varname,
|
||||
char_u *varname_end)
|
||||
{
|
||||
char_u *name = vim_strnsave(varname, varname_end - varname);
|
||||
int dup = FALSE;
|
||||
|
||||
for (int i = 0; i < enum_gap->ga_len; ++i)
|
||||
{
|
||||
ocmember_T *m = ((ocmember_T *)enum_gap->ga_data) + i;
|
||||
if (STRCMP(name, m->ocm_name) == 0)
|
||||
{
|
||||
semsg(_(e_duplicate_enum_str), name);
|
||||
dup = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
vim_free(name);
|
||||
return dup;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the enum values in "line" separated by comma and add them to "gap".
|
||||
* If the last enum value is found, then "enum_end" is set to TRUE.
|
||||
*/
|
||||
static int
|
||||
enum_parse_values(
|
||||
exarg_T *eap,
|
||||
class_T *en,
|
||||
char_u *line,
|
||||
garray_T *gap,
|
||||
int *num_enum_values,
|
||||
int *enum_end)
|
||||
{
|
||||
evalarg_T evalarg;
|
||||
char_u *p = line;
|
||||
char initexpr_buf[1024];
|
||||
char_u last_char = NUL;
|
||||
int rc = OK;
|
||||
|
||||
fill_evalarg_from_eap(&evalarg, eap, FALSE);
|
||||
|
||||
int did_emsg_before = did_emsg;
|
||||
while (*p != NUL)
|
||||
{
|
||||
// ignore comment
|
||||
if (*p == '#')
|
||||
break;
|
||||
|
||||
if (!eval_isnamec1(*p))
|
||||
{
|
||||
semsg(_(e_invalid_enum_value_declaration_str), p);
|
||||
break;
|
||||
}
|
||||
|
||||
char_u *eni_name_start = p;
|
||||
char_u *eni_name_end = to_name_end(p, FALSE);
|
||||
|
||||
if (is_duplicate_enum(gap, eni_name_start, eni_name_end))
|
||||
break;
|
||||
|
||||
p = skipwhite(eni_name_end);
|
||||
|
||||
char_u *init_expr = NULL;
|
||||
if (*p == '(')
|
||||
{
|
||||
if (VIM_ISWHITE(p[-1]))
|
||||
{
|
||||
semsg(_(e_no_white_space_allowed_before_str_str), "(", line);
|
||||
break;
|
||||
}
|
||||
|
||||
char_u *expr_start, *expr_end;
|
||||
|
||||
p = eni_name_start;
|
||||
(void)skip_expr_concatenate(&p, &expr_start, &expr_end, &evalarg);
|
||||
|
||||
while (*expr_start && *expr_start != '(')
|
||||
expr_start++;
|
||||
|
||||
if (expr_end > expr_start)
|
||||
init_expr = vim_strnsave(expr_start, expr_end - expr_start);
|
||||
}
|
||||
|
||||
if (init_expr == NULL)
|
||||
vim_snprintf(initexpr_buf, sizeof(initexpr_buf), "%s.new()",
|
||||
en->class_name);
|
||||
else
|
||||
{
|
||||
vim_snprintf(initexpr_buf, sizeof(initexpr_buf), "%s.new%s",
|
||||
en->class_name, init_expr);
|
||||
vim_free(init_expr);
|
||||
}
|
||||
if (add_member(gap, eni_name_start, eni_name_end, FALSE,
|
||||
TRUE, TRUE, TRUE, &en->class_object_type,
|
||||
vim_strsave((char_u *)initexpr_buf)) == FAIL)
|
||||
break;
|
||||
|
||||
++*num_enum_values;
|
||||
|
||||
if (*p != '#')
|
||||
last_char = *p;
|
||||
|
||||
if (*p != NUL && *p != ',')
|
||||
break;
|
||||
|
||||
if (*p == ',')
|
||||
{
|
||||
if (!IS_WHITE_OR_NUL(p[1]))
|
||||
{
|
||||
semsg(_(e_white_space_required_after_str_str), ",", line);
|
||||
break;
|
||||
}
|
||||
if (VIM_ISWHITE(p[-1]))
|
||||
{
|
||||
semsg(_(e_no_white_space_allowed_before_str_str), ",", line);
|
||||
break;
|
||||
}
|
||||
p = skipwhite(p + 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (*p != NUL && *p != '#')
|
||||
{
|
||||
if (did_emsg == did_emsg_before)
|
||||
semsg(_(e_missing_comma_before_argument_str), p);
|
||||
rc = FAIL;
|
||||
}
|
||||
|
||||
if (last_char != ',')
|
||||
// last enum value should not be terminated by ","
|
||||
*enum_end = TRUE;
|
||||
|
||||
// Free the memory pointed by expr_start.
|
||||
clear_evalarg(&evalarg, NULL);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add the "values" class variable (List of enum value objects) to the enum
|
||||
* class "en"
|
||||
*/
|
||||
static int
|
||||
enum_add_values_member(
|
||||
class_T *en,
|
||||
garray_T *gap,
|
||||
int num_enum_values,
|
||||
garray_T *type_list_gap)
|
||||
{
|
||||
garray_T fga;
|
||||
int rc = FAIL;
|
||||
|
||||
ga_init2(&fga, 1, 1000);
|
||||
ga_concat(&fga, (char_u *)"[");
|
||||
for (int i = 0; i < num_enum_values; ++i)
|
||||
{
|
||||
ocmember_T *m = ((ocmember_T *)gap->ga_data) + i;
|
||||
|
||||
if (i > 0)
|
||||
ga_concat(&fga, (char_u *)", ");
|
||||
ga_concat(&fga, en->class_name);
|
||||
ga_concat(&fga, (char_u *)".");
|
||||
ga_concat(&fga, (char_u *)m->ocm_name);
|
||||
}
|
||||
ga_concat(&fga, (char_u *)"]");
|
||||
ga_append(&fga, NUL);
|
||||
|
||||
char_u *varname = (char_u *)"values";
|
||||
|
||||
type_T *type = get_type_ptr(type_list_gap);
|
||||
if (type == NULL)
|
||||
goto done;
|
||||
|
||||
type->tt_type = VAR_LIST;
|
||||
type->tt_member = get_type_ptr(type_list_gap);
|
||||
if (type->tt_member != NULL)
|
||||
{
|
||||
type->tt_member->tt_type = VAR_OBJECT;
|
||||
type->tt_member->tt_class = en;
|
||||
}
|
||||
|
||||
rc = add_member(gap, varname, varname + 6, FALSE, FALSE, TRUE, TRUE, type,
|
||||
vim_strsave((char_u *)fga.ga_data));
|
||||
|
||||
done:
|
||||
vim_free(fga.ga_data);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear the constructor method names in a enum class, so that an enum class
|
||||
* cannot be instantiated.
|
||||
*/
|
||||
static void
|
||||
enum_clear_constructors(class_T *en)
|
||||
{
|
||||
for (int i = 0; i < en->class_class_function_count; ++i)
|
||||
{
|
||||
ufunc_T *fp = en->class_class_functions[i];
|
||||
|
||||
if (fp->uf_flags & FC_NEW)
|
||||
*fp->uf_name = NUL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the name and ordinal object variable in the enum value "enval" in
|
||||
* the enum "en". These values are set during the enum value object creation.
|
||||
*/
|
||||
void
|
||||
enum_set_internal_obj_vars(class_T *en, object_T *enval)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < en->class_class_member_count; ++i)
|
||||
{
|
||||
typval_T *en_tv = en->class_members_tv + i;
|
||||
if (en_tv != NULL && en_tv->v_type == VAR_UNKNOWN)
|
||||
break;
|
||||
}
|
||||
|
||||
// First object variable is the name
|
||||
ocmember_T *value_ocm = en->class_class_members + i;
|
||||
typval_T *name_tv = (typval_T *)(enval + 1);
|
||||
name_tv->v_type = VAR_STRING;
|
||||
name_tv->vval.v_string = vim_strsave(value_ocm->ocm_name);
|
||||
|
||||
// Second object variable is the ordinal
|
||||
typval_T *ord_tv = (typval_T *)(name_tv + 1);
|
||||
ord_tv->v_type = VAR_NUMBER;
|
||||
ord_tv->vval.v_number = i;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle ":class" and ":abstract class" up to ":endclass".
|
||||
@@ -1479,10 +1735,12 @@ find_class_name_end(char_u *arg)
|
||||
void
|
||||
ex_class(exarg_T *eap)
|
||||
{
|
||||
int is_class = eap->cmdidx == CMD_class; // FALSE for :interface
|
||||
int is_class = eap->cmdidx == CMD_class;
|
||||
int is_abstract = eap->cmdidx == CMD_abstract;
|
||||
int is_enum = eap->cmdidx == CMD_enum;
|
||||
int is_interface;
|
||||
long start_lnum = SOURCING_LNUM;
|
||||
char_u *arg = eap->arg;
|
||||
int is_abstract = eap->cmdidx == CMD_abstract;
|
||||
|
||||
if (is_abstract)
|
||||
{
|
||||
@@ -1495,12 +1753,16 @@ ex_class(exarg_T *eap)
|
||||
is_class = TRUE;
|
||||
}
|
||||
|
||||
is_interface = !is_class && !is_enum;
|
||||
|
||||
if (!current_script_is_vim9()
|
||||
|| (cmdmod.cmod_flags & CMOD_LEGACY)
|
||||
|| !getline_equal(eap->ea_getline, eap->cookie, getsourceline))
|
||||
{
|
||||
if (is_class)
|
||||
emsg(_(e_class_can_only_be_defined_in_vim9_script));
|
||||
else if (is_enum)
|
||||
emsg(_(e_enum_can_only_be_defined_in_vim9_script));
|
||||
else
|
||||
emsg(_(e_interface_can_only_be_defined_in_vim9_script));
|
||||
return;
|
||||
@@ -1510,6 +1772,8 @@ ex_class(exarg_T *eap)
|
||||
{
|
||||
if (is_class)
|
||||
semsg(_(e_class_name_must_start_with_uppercase_letter_str), arg);
|
||||
else if (is_enum)
|
||||
semsg(_(e_enum_name_must_start_with_uppercase_letter_str), arg);
|
||||
else
|
||||
semsg(_(e_interface_name_must_start_with_uppercase_letter_str),
|
||||
arg);
|
||||
@@ -1523,11 +1787,6 @@ ex_class(exarg_T *eap)
|
||||
}
|
||||
char_u *name_start = arg;
|
||||
|
||||
// "export class" gets used when creating the class, don't use "is_export"
|
||||
// for the items inside the class.
|
||||
int class_export = is_export;
|
||||
is_export = FALSE;
|
||||
|
||||
// TODO:
|
||||
// generics: <Tkey, Tentry>
|
||||
|
||||
@@ -1545,6 +1804,11 @@ ex_class(exarg_T *eap)
|
||||
// specifies SomeInterface
|
||||
if (STRNCMP(arg, "extends", 7) == 0 && IS_WHITE_OR_NUL(arg[7]))
|
||||
{
|
||||
if (is_enum)
|
||||
{
|
||||
emsg(_(e_enum_cannot_extend_class));
|
||||
goto early_ret;
|
||||
}
|
||||
if (extends != NULL)
|
||||
{
|
||||
emsg(_(e_duplicate_extends));
|
||||
@@ -1567,7 +1831,7 @@ ex_class(exarg_T *eap)
|
||||
else if (STRNCMP(arg, "implements", 10) == 0
|
||||
&& IS_WHITE_OR_NUL(arg[10]))
|
||||
{
|
||||
if (!is_class)
|
||||
if (is_interface)
|
||||
{
|
||||
emsg(_(e_interface_cannot_use_implements));
|
||||
goto early_ret;
|
||||
@@ -1652,11 +1916,15 @@ early_ret:
|
||||
class_T *cl = NULL;
|
||||
class_T *extends_cl = NULL; // class from "extends" argument
|
||||
class_T **intf_classes = NULL;
|
||||
int num_enum_values = 0;
|
||||
|
||||
cl = ALLOC_CLEAR_ONE(class_T);
|
||||
if (cl == NULL)
|
||||
goto cleanup;
|
||||
if (!is_class)
|
||||
|
||||
if (is_enum)
|
||||
cl->class_flags = CLASS_ENUM;
|
||||
else if (is_interface)
|
||||
cl->class_flags = CLASS_INTERFACE;
|
||||
else if (is_abstract)
|
||||
cl->class_flags = CLASS_ABSTRACT;
|
||||
@@ -1666,22 +1934,48 @@ early_ret:
|
||||
if (cl->class_name == NULL)
|
||||
goto cleanup;
|
||||
|
||||
cl->class_type.tt_type = VAR_CLASS;
|
||||
cl->class_type.tt_class = cl;
|
||||
cl->class_object_type.tt_type = VAR_OBJECT;
|
||||
cl->class_object_type.tt_class = cl;
|
||||
|
||||
// Add the class to the script-local variables.
|
||||
// TODO: handle other context, e.g. in a function
|
||||
// TODO: does uf_hash need to be cleared?
|
||||
typval_T tv;
|
||||
tv.v_type = VAR_CLASS;
|
||||
tv.vval.v_class = cl;
|
||||
is_export = class_export;
|
||||
SOURCING_LNUM = start_lnum;
|
||||
int rc = set_var_const(cl->class_name, current_sctx.sc_sid,
|
||||
NULL, &tv, FALSE, 0, 0);
|
||||
if (rc == FAIL)
|
||||
goto cleanup;
|
||||
|
||||
if (is_enum)
|
||||
{
|
||||
// All the enum classes have the name and ordinal object variables.
|
||||
char_u *varname = (char_u *)"name";
|
||||
if (add_member(&objmembers, varname, varname + 4, FALSE, FALSE, TRUE,
|
||||
TRUE, &t_string, NULL) == FAIL)
|
||||
goto cleanup;
|
||||
|
||||
varname = (char_u *)"ordinal";
|
||||
if (add_member(&objmembers, varname, varname + 7, FALSE, FALSE, TRUE,
|
||||
TRUE, &t_number, NULL) == FAIL)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// "export class" gets used when creating the class, don't use "is_export"
|
||||
// for the items inside the class.
|
||||
is_export = FALSE;
|
||||
|
||||
// When parsing an enum definition, this denotes whether all the enumerated
|
||||
// values are parsed or not.
|
||||
int enum_end = FALSE;
|
||||
|
||||
/*
|
||||
* Go over the body of the class/interface until "endclass" or
|
||||
* "endinterface" is found.
|
||||
* "endinterface" or "endenum" is found.
|
||||
*/
|
||||
char_u *theline = NULL;
|
||||
int success = FALSE;
|
||||
@@ -1704,10 +1998,32 @@ early_ret:
|
||||
}
|
||||
|
||||
char_u *p = line;
|
||||
char *end_name = is_class ? "endclass" : "endinterface";
|
||||
if (checkforcmd(&p, end_name, is_class ? 4 : 5))
|
||||
|
||||
char *end_name;
|
||||
int shortlen;
|
||||
int fullen;
|
||||
if (is_class)
|
||||
{
|
||||
if (STRNCMP(line, end_name, is_class ? 8 : 12) != 0)
|
||||
end_name = "endclass";
|
||||
shortlen = 4;
|
||||
fullen = 8;
|
||||
}
|
||||
else if (is_enum)
|
||||
{
|
||||
end_name = "endenum";
|
||||
shortlen = 4;
|
||||
fullen = 7;
|
||||
}
|
||||
else
|
||||
{
|
||||
end_name = "endinterface";
|
||||
shortlen = 5;
|
||||
fullen = 12;
|
||||
}
|
||||
|
||||
if (checkforcmd(&p, end_name, shortlen))
|
||||
{
|
||||
if (STRNCMP(line, end_name, fullen) != 0)
|
||||
semsg(_(e_command_cannot_be_shortened_str), line);
|
||||
else if (*p == '|' || !ends_excmd2(line, p))
|
||||
semsg(_(e_trailing_characters_str), p);
|
||||
@@ -1715,13 +2031,34 @@ early_ret:
|
||||
success = TRUE;
|
||||
break;
|
||||
}
|
||||
char *wrong_name = is_class ? "endinterface" : "endclass";
|
||||
if (checkforcmd(&p, wrong_name, is_class ? 5 : 4))
|
||||
|
||||
int wrong_endname = FALSE;
|
||||
if (is_class)
|
||||
wrong_endname = checkforcmd(&p, "endinterface", 5)
|
||||
|| checkforcmd(&p, "endenum", 4);
|
||||
else if (is_enum)
|
||||
wrong_endname = checkforcmd(&p, "endclass", 4)
|
||||
|| checkforcmd(&p, "endinterface", 5);
|
||||
else
|
||||
wrong_endname = checkforcmd(&p, "endclass", 4)
|
||||
|| checkforcmd(&p, "endenum", 4);
|
||||
if (wrong_endname)
|
||||
{
|
||||
semsg(_(e_invalid_command_str_expected_str), line, end_name);
|
||||
break;
|
||||
}
|
||||
|
||||
if (is_enum && !enum_end)
|
||||
{
|
||||
// In an enum, all the enumerated values are at the beginning
|
||||
// separated by comma. The class and object variables/methods
|
||||
// follow the values.
|
||||
if (enum_parse_values(eap, cl, line, &classmembers,
|
||||
&num_enum_values, &enum_end) == FAIL)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
int has_public = FALSE;
|
||||
if (checkforcmd(&p, "public", 3))
|
||||
{
|
||||
@@ -1730,7 +2067,7 @@ early_ret:
|
||||
semsg(_(e_command_cannot_be_shortened_str), line);
|
||||
break;
|
||||
}
|
||||
if (!is_class)
|
||||
if (is_interface)
|
||||
{
|
||||
emsg(_(e_public_variable_not_supported_in_interface));
|
||||
break;
|
||||
@@ -1756,7 +2093,14 @@ early_ret:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!is_class)
|
||||
if (is_enum)
|
||||
{
|
||||
// "abstract" not supported in an enum
|
||||
emsg(_(e_abstract_cannot_be_used_in_enum));
|
||||
break;
|
||||
}
|
||||
|
||||
if (is_interface)
|
||||
{
|
||||
// "abstract" not supported in an interface
|
||||
emsg(_(e_abstract_cannot_be_used_in_interface));
|
||||
@@ -1789,7 +2133,7 @@ early_ret:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!is_class)
|
||||
if (is_interface)
|
||||
{
|
||||
emsg(_(e_static_member_not_supported_in_interface));
|
||||
break;
|
||||
@@ -1812,7 +2156,7 @@ early_ret:
|
||||
has_var = TRUE;
|
||||
else if (checkforcmd(&p, "final", 5))
|
||||
{
|
||||
if (!is_class)
|
||||
if (is_interface)
|
||||
{
|
||||
emsg(_(e_final_variable_not_supported_in_interface));
|
||||
break;
|
||||
@@ -1821,7 +2165,7 @@ early_ret:
|
||||
}
|
||||
else if (checkforcmd(&p, "const", 5))
|
||||
{
|
||||
if (!is_class)
|
||||
if (is_interface)
|
||||
{
|
||||
emsg(_(e_const_variable_not_supported_in_interface));
|
||||
break;
|
||||
@@ -1867,7 +2211,7 @@ early_ret:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!is_class && *varname == '_')
|
||||
if (is_interface && *varname == '_')
|
||||
{
|
||||
// private variables are not supported in an interface
|
||||
semsg(_(e_protected_variable_not_supported_in_interface),
|
||||
@@ -1877,7 +2221,7 @@ early_ret:
|
||||
|
||||
if (parse_member(eap, line, varname, has_public,
|
||||
&varname_end, &has_type, &type_list, &type,
|
||||
is_class ? &init_expr: NULL) == FAIL)
|
||||
!is_interface ? &init_expr: NULL) == FAIL)
|
||||
break;
|
||||
|
||||
if (is_reserved_varname(varname, varname_end)
|
||||
@@ -1930,7 +2274,7 @@ early_ret:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!is_class && *p == '_')
|
||||
if (is_interface && *p == '_')
|
||||
{
|
||||
// private methods are not supported in an interface
|
||||
semsg(_(e_protected_method_not_supported_in_interface), p);
|
||||
@@ -1953,10 +2297,10 @@ early_ret:
|
||||
|
||||
ga_init2(&lines_to_free, sizeof(char_u *), 50);
|
||||
int class_flags;
|
||||
if (is_class)
|
||||
class_flags = abstract_method ? CF_ABSTRACT_METHOD : CF_CLASS;
|
||||
else
|
||||
if (is_interface)
|
||||
class_flags = CF_INTERFACE;
|
||||
else
|
||||
class_flags = abstract_method ? CF_ABSTRACT_METHOD : CF_CLASS;
|
||||
ufunc_T *uf = define_function(&ea, NULL, &lines_to_free,
|
||||
class_flags, objmembers.ga_data, objmembers.ga_len);
|
||||
ga_clear_strings(&lines_to_free);
|
||||
@@ -2011,15 +2355,25 @@ early_ret:
|
||||
{
|
||||
if (is_class)
|
||||
semsg(_(e_not_valid_command_in_class_str), line);
|
||||
else if (is_enum)
|
||||
semsg(_(e_not_valid_command_in_enum_str), line);
|
||||
else
|
||||
semsg(_(e_not_valid_command_in_interface_str), line);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (theline == NULL && !success && is_enum)
|
||||
emsg(_(e_missing_endenum));
|
||||
|
||||
vim_free(theline);
|
||||
|
||||
if (success && is_enum)
|
||||
// Add the enum "values" class variable.
|
||||
enum_add_values_member(cl, &classmembers, num_enum_values, &type_list);
|
||||
|
||||
/*
|
||||
* Check a few things before defining the class.
|
||||
* Check a few things
|
||||
*/
|
||||
|
||||
// Check the "extends" class is valid.
|
||||
@@ -2067,7 +2421,8 @@ early_ret:
|
||||
|
||||
if (success)
|
||||
{
|
||||
// "endclass" encountered without failures: Create the class.
|
||||
// "endclass" or "endinterface" or "endenum" encountered without any
|
||||
// failures
|
||||
|
||||
if (extends_cl != NULL)
|
||||
{
|
||||
@@ -2114,10 +2469,6 @@ early_ret:
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Allocate a typval for each class member and initialize it.
|
||||
if (is_class && cl->class_class_member_count > 0)
|
||||
add_class_members(cl, eap, &type_list);
|
||||
|
||||
int have_new = FALSE;
|
||||
ufunc_T *class_func = NULL;
|
||||
for (int i = 0; i < classfunctions.ga_len; ++i)
|
||||
@@ -2133,7 +2484,7 @@ early_ret:
|
||||
if (have_new)
|
||||
// The return type of new() is an object of class "cl"
|
||||
class_func->uf_ret_type->tt_class = cl;
|
||||
else if (is_class && !is_abstract && !have_new)
|
||||
else if ((is_class || is_enum) && !is_abstract && !have_new)
|
||||
// No new() method was defined, add the default constructor.
|
||||
add_default_constructor(cl, &classfunctions, &type_list);
|
||||
|
||||
@@ -2144,13 +2495,21 @@ early_ret:
|
||||
|
||||
update_builtin_method_index(cl);
|
||||
|
||||
cl->class_type.tt_type = VAR_CLASS;
|
||||
cl->class_type.tt_class = cl;
|
||||
cl->class_object_type.tt_type = VAR_OBJECT;
|
||||
cl->class_object_type.tt_class = cl;
|
||||
class_created(cl);
|
||||
|
||||
// Allocate a typval for each class member and initialize it.
|
||||
if ((is_class || is_enum) && cl->class_class_member_count > 0)
|
||||
if (add_class_members(cl, eap, &type_list) == FAIL)
|
||||
goto cleanup;
|
||||
|
||||
cl->class_type_list = type_list;
|
||||
|
||||
class_created(cl);
|
||||
if (is_enum)
|
||||
{
|
||||
// clear the constructor method names, so that an enum class cannot
|
||||
// be instantiated
|
||||
enum_clear_constructors(cl);
|
||||
}
|
||||
|
||||
// TODO:
|
||||
// - Fill hashtab with object members and methods ?
|
||||
@@ -2264,15 +2623,6 @@ oc_member_type_by_idx(
|
||||
return m[member_idx].ocm_type;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle ":enum" up to ":endenum".
|
||||
*/
|
||||
void
|
||||
ex_enum(exarg_T *eap UNUSED)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
/*
|
||||
* Type aliases (:type)
|
||||
*/
|
||||
@@ -3334,8 +3684,14 @@ member_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len)
|
||||
semsg(_(e_object_variable_str_accessible_only_using_object_str),
|
||||
varname, cl->class_name);
|
||||
else
|
||||
semsg(_(e_class_variable_str_not_found_in_class_str),
|
||||
varname, cl->class_name);
|
||||
{
|
||||
if (IS_ENUM(cl))
|
||||
semsg(_(e_enum_value_str_not_found_in_enum_str),
|
||||
varname, cl->class_name);
|
||||
else
|
||||
semsg(_(e_class_variable_str_not_found_in_class_str),
|
||||
varname, cl->class_name);
|
||||
}
|
||||
}
|
||||
vim_free(varname);
|
||||
}
|
||||
@@ -3480,8 +3836,17 @@ object_string(
|
||||
garray_T ga;
|
||||
ga_init2(&ga, 1, 50);
|
||||
|
||||
ga_concat(&ga, (char_u *)"object of ");
|
||||
class_T *cl = obj == NULL ? NULL : obj->obj_class;
|
||||
if (cl != NULL && IS_ENUM(cl))
|
||||
{
|
||||
ga_concat(&ga, cl->class_name);
|
||||
char_u *name = ((typval_T *)(obj + 1))->vval.v_string;
|
||||
ga_concat(&ga, (char_u *)".");
|
||||
ga_concat(&ga, name);
|
||||
return ga.ga_data;
|
||||
}
|
||||
|
||||
ga_concat(&ga, (char_u *)"object of ");
|
||||
ga_concat(&ga, cl == NULL ? (char_u *)"[unknown]"
|
||||
: cl->class_name);
|
||||
if (cl != NULL)
|
||||
|
Reference in New Issue
Block a user