forked from enlightenment/efl
eolian: add support for composite section into regular classes
Each regular class can now have a section called 'composite', which can contain interfaces and interfaces only. This defines a list of interfaces that are allowed to be unimplemented on the class, as it is assumed the class will be composited with some other class implementing those interfaces. Only regular classes can have this, as only regular classes can be instantiated. It will also be necessary to check whether the classes in the section appear somewhere within the inheritance tree. For now, this testing is not being done. Example of usage: composite { Some.Magic.Interface; Another.Magic.Interface; } directly in the class body.
This commit is contained in:
parent
dab4d8d8a4
commit
7feeda0410
|
@ -105,6 +105,8 @@ tests/eolian/data/function_types.eot \
|
|||
tests/eolian/data/import_types.eot \
|
||||
tests/eolian/data/class_requires.eo \
|
||||
tests/eolian/data/mixins_require.eo \
|
||||
tests/eolian/data/iface.eo \
|
||||
tests/eolian/data/unimpl.eo \
|
||||
tests/eolian/data_aux/aux_a.eo \
|
||||
tests/eolian/data_aux/aux_b.eo \
|
||||
tests/eolian/data_aux/aux_c.eo
|
||||
|
|
|
@ -31,6 +31,7 @@ database_class_del(Eolian_Class *cl)
|
|||
EINA_LIST_FREE(cl->parts, pt) database_part_del(pt);
|
||||
eina_list_free(cl->requires);
|
||||
eina_list_free(cl->callables);
|
||||
eina_list_free(cl->composite);
|
||||
|
||||
if (cl->legacy_prefix) eina_stringshare_del(cl->legacy_prefix);
|
||||
if (cl->eo_prefix) eina_stringshare_del(cl->eo_prefix);
|
||||
|
|
|
@ -715,7 +715,14 @@ _db_fill_callables(Eolian_Class *cl, Eolian_Class *icl, Eina_Hash *fs, Eina_Bool
|
|||
EINA_LIST_FOREACH(icl->callables, l, impl)
|
||||
{
|
||||
Impl_Status ost = (Impl_Status)eina_hash_find(fs, &impl->foo_id);
|
||||
Eina_Bool extd = _extend_impl(fs, impl, !allow_impl);
|
||||
Eina_Bool extd = (ost != IMPL_STATUS_FULL);
|
||||
if (icl->type == EOLIAN_CLASS_REGULAR)
|
||||
/* stuff coming from full classes is assumed to be already checked
|
||||
* so we are sure that everything is implemented or composite'd
|
||||
*/
|
||||
eina_hash_set(fs, &impl->foo_id, (void *)IMPL_STATUS_FULL);
|
||||
else
|
||||
extd = _extend_impl(fs, impl, !allow_impl);
|
||||
if (extd)
|
||||
{
|
||||
/* we had an unimplementation in the list, replace
|
||||
|
@ -739,7 +746,7 @@ _db_fill_callables(Eolian_Class *cl, Eolian_Class *icl, Eina_Hash *fs, Eina_Bool
|
|||
}
|
||||
|
||||
static Eina_Bool
|
||||
_db_check_implemented(Validate_State *vals, Eolian_Class *cl, Eina_Hash *fs)
|
||||
_db_check_implemented(Validate_State *vals, Eolian_Class *cl, Eina_Hash *fs, Eina_Hash *cs)
|
||||
{
|
||||
if (cl->type != EOLIAN_CLASS_REGULAR)
|
||||
return EINA_TRUE;
|
||||
|
@ -753,8 +760,14 @@ _db_check_implemented(Validate_State *vals, Eolian_Class *cl, Eina_Hash *fs)
|
|||
Eolian_Implement *impl;
|
||||
EINA_LIST_FOREACH(cl->callables, l, impl)
|
||||
{
|
||||
Impl_Status st = (Impl_Status)eina_hash_find(fs, &impl->foo_id);
|
||||
const Eolian_Function *fid = impl->foo_id;
|
||||
Impl_Status st = (Impl_Status)eina_hash_find(fs, &fid);
|
||||
/* found an interface this func was originally defined in in the
|
||||
* composite list; in that case, ignore it and assume it will come
|
||||
* from a composite object later
|
||||
*/
|
||||
if (eina_hash_find(cs, &fid->klass))
|
||||
continue;
|
||||
switch (st)
|
||||
{
|
||||
case IMPL_STATUS_NONE:
|
||||
|
@ -880,8 +893,9 @@ end:
|
|||
|
||||
static Eina_Bool
|
||||
_db_swap_inherit(Eolian_Class *cl, Eina_Bool succ, Eina_Stringshare *in_cl,
|
||||
Eolian_Class **out_cl)
|
||||
Eolian_Class **out_cl, Eina_Bool iface_only)
|
||||
{
|
||||
char buf[PATH_MAX];
|
||||
if (!succ)
|
||||
{
|
||||
eina_stringshare_del(in_cl);
|
||||
|
@ -891,10 +905,23 @@ _db_swap_inherit(Eolian_Class *cl, Eina_Bool succ, Eina_Stringshare *in_cl,
|
|||
if (!icl)
|
||||
{
|
||||
succ = EINA_FALSE;
|
||||
char buf[PATH_MAX];
|
||||
snprintf(buf, sizeof(buf), "unknown inherit '%s' (incorrect case?)", in_cl);
|
||||
_obj_error(&cl->base, buf);
|
||||
}
|
||||
else if (iface_only && (icl->type != EOLIAN_CLASS_INTERFACE))
|
||||
{
|
||||
succ = EINA_FALSE;
|
||||
snprintf(buf, sizeof(buf), "non-interface class '%s' in composite list", icl->base.name);
|
||||
_obj_error(&cl->base, buf);
|
||||
}
|
||||
else if (iface_only && !_get_impl_class(cl, icl->base.name))
|
||||
{
|
||||
/* TODO: optimize check using a lookup hash later */
|
||||
succ = EINA_FALSE;
|
||||
snprintf(buf, sizeof(buf), "interface '%s' not found within the inheritance tree of '%s'",
|
||||
icl->base.name, cl->base.name);
|
||||
_obj_error(&cl->base, buf);
|
||||
}
|
||||
else
|
||||
*out_cl = icl;
|
||||
eina_stringshare_del(in_cl);
|
||||
|
@ -919,7 +946,7 @@ _db_fill_inherits(Validate_State *vals, Eolian_Class *cl, Eina_Hash *fhash)
|
|||
|
||||
if (cl->parent_name)
|
||||
{
|
||||
succ = _db_swap_inherit(cl, succ, cl->parent_name, &cl->parent);
|
||||
succ = _db_swap_inherit(cl, succ, cl->parent_name, &cl->parent, EINA_FALSE);
|
||||
if (succ)
|
||||
{
|
||||
/* fill if not found, but do not return right away because
|
||||
|
@ -933,7 +960,7 @@ _db_fill_inherits(Validate_State *vals, Eolian_Class *cl, Eina_Hash *fhash)
|
|||
EINA_LIST_FREE(il, inn)
|
||||
{
|
||||
Eolian_Class *out_cl = NULL;
|
||||
succ = _db_swap_inherit(cl, succ, inn, &out_cl);
|
||||
succ = _db_swap_inherit(cl, succ, inn, &out_cl, EINA_FALSE);
|
||||
if (!succ)
|
||||
continue;
|
||||
cl->extends = eina_list_append(cl->extends, out_cl);
|
||||
|
@ -945,7 +972,7 @@ _db_fill_inherits(Validate_State *vals, Eolian_Class *cl, Eina_Hash *fhash)
|
|||
EINA_LIST_FREE(rl, inn)
|
||||
{
|
||||
Eolian_Class *out_cl = NULL;
|
||||
succ = _db_swap_inherit(cl, succ, inn, &out_cl);
|
||||
succ = _db_swap_inherit(cl, succ, inn, &out_cl, EINA_FALSE);
|
||||
if (succ && !(out_cl->type == EOLIAN_CLASS_REGULAR || out_cl->type == EOLIAN_CLASS_ABSTRACT))
|
||||
{
|
||||
char buf[PATH_MAX];
|
||||
|
@ -964,11 +991,29 @@ _db_fill_inherits(Validate_State *vals, Eolian_Class *cl, Eina_Hash *fhash)
|
|||
}
|
||||
}
|
||||
|
||||
/* a set of interfaces for quick checks */
|
||||
Eina_Hash *ch = eina_hash_pointer_new(NULL);
|
||||
|
||||
il = cl->composite;
|
||||
cl->composite = NULL;
|
||||
EINA_LIST_FREE(il, inn)
|
||||
{
|
||||
Eolian_Class *out_cl = NULL;
|
||||
succ = _db_swap_inherit(cl, succ, inn, &out_cl, EINA_TRUE);
|
||||
if (!succ)
|
||||
continue;
|
||||
cl->composite = eina_list_append(cl->composite, out_cl);
|
||||
eina_hash_set(ch, &out_cl, out_cl);
|
||||
}
|
||||
|
||||
/* failed on the way, no point in filling further
|
||||
* the failed stuff will get dropped so it's ok if it's inconsistent
|
||||
*/
|
||||
if (!succ)
|
||||
return EINA_FALSE;
|
||||
{
|
||||
eina_hash_free(ch);
|
||||
return EINA_FALSE;
|
||||
}
|
||||
|
||||
eina_hash_add(fhash, &cl->base.name, cl);
|
||||
|
||||
|
@ -977,10 +1022,18 @@ _db_fill_inherits(Validate_State *vals, Eolian_Class *cl, Eina_Hash *fhash)
|
|||
|
||||
/* make sure impls/ctors are filled first, but do it only once */
|
||||
if (!_db_fill_implements(cl, fh))
|
||||
return EINA_FALSE;
|
||||
{
|
||||
eina_hash_free(ch);
|
||||
eina_hash_free(fh);
|
||||
return EINA_FALSE;
|
||||
}
|
||||
|
||||
if (!_db_fill_ctors(cl))
|
||||
return EINA_FALSE;
|
||||
{
|
||||
eina_hash_free(ch);
|
||||
eina_hash_free(fh);
|
||||
return EINA_FALSE;
|
||||
}
|
||||
|
||||
/* fill callables list with stuff from inheritance tree, the current
|
||||
* class stuff is already filled in _db_fill_implements, this is needed
|
||||
|
@ -994,10 +1047,11 @@ _db_fill_inherits(Validate_State *vals, Eolian_Class *cl, Eina_Hash *fhash)
|
|||
_db_fill_callables(cl, icl, fh, EINA_FALSE);
|
||||
|
||||
/* verify that all methods are implemented on the class */
|
||||
if (!_db_check_implemented(vals, cl, fh))
|
||||
if (!_db_check_implemented(vals, cl, fh, ch))
|
||||
vals->warned = EINA_TRUE;
|
||||
|
||||
eina_hash_free(fh);
|
||||
eina_hash_free(ch);
|
||||
|
||||
return EINA_TRUE;
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ enum Tokens
|
|||
* they just fill in the "kw" field of the token */
|
||||
#define KEYWORDS KW(class), KW(const), KW(enum), KW(return), KW(struct), \
|
||||
\
|
||||
KW(abstract), KW(constructor), KW(constructors), KW(data), \
|
||||
KW(abstract), KW(composite), KW(constructor), KW(constructors), KW(data), \
|
||||
KW(destructor), KW(eo), KW(eo_prefix), KW(event_prefix), KW(events), \
|
||||
KW(extends), KW(free), KW(get), KW(implements), KW(import), KW(interface), \
|
||||
KW(keys), KW(legacy), KW(legacy_prefix), KW(methods), KW(mixin), KW(params), \
|
||||
|
|
|
@ -1815,6 +1815,43 @@ parse_parts(Eo_Lexer *ls)
|
|||
check_match(ls, '}', '{', line, col);
|
||||
}
|
||||
|
||||
static void
|
||||
parse_composite(Eo_Lexer *ls)
|
||||
{
|
||||
int line, col;
|
||||
if (ls->klass->type != EOLIAN_CLASS_REGULAR)
|
||||
eo_lexer_syntax_error(ls, "composite section only allowed in regular classes");
|
||||
eo_lexer_get(ls);
|
||||
line = ls->line_number, col = ls->column;
|
||||
check_next(ls, '{');
|
||||
while (ls->t.token != '}')
|
||||
{
|
||||
Eina_Strbuf *buf = eina_strbuf_new();
|
||||
eo_lexer_dtor_push(ls, EINA_FREE_CB(eina_strbuf_free), buf);
|
||||
eo_lexer_context_push(ls);
|
||||
parse_name(ls, buf);
|
||||
const char *nm = eina_strbuf_string_get(buf);
|
||||
char *fnm = database_class_to_filename(nm);
|
||||
if (!eina_hash_find(ls->state->filenames_eo, fnm))
|
||||
{
|
||||
free(fnm);
|
||||
char ebuf[PATH_MAX];
|
||||
eo_lexer_context_restore(ls);
|
||||
snprintf(ebuf, sizeof(ebuf), "unknown interface '%s'", nm);
|
||||
eo_lexer_syntax_error(ls, ebuf);
|
||||
return;
|
||||
}
|
||||
/* do not introduce a dependency */
|
||||
database_defer(ls->state, fnm, EINA_FALSE);
|
||||
free(fnm);
|
||||
ls->klass->composite = eina_list_append(ls->klass->composite,
|
||||
eina_stringshare_add(nm));
|
||||
eo_lexer_dtor_pop(ls);
|
||||
check_next(ls, ';');
|
||||
}
|
||||
check_match(ls, '}', '{', line, col);
|
||||
}
|
||||
|
||||
static void
|
||||
parse_implements(Eo_Lexer *ls, Eina_Bool iface)
|
||||
{
|
||||
|
@ -1886,6 +1923,7 @@ parse_class_body(Eo_Lexer *ls, Eolian_Class_Type type)
|
|||
has_data = EINA_FALSE,
|
||||
has_methods = EINA_FALSE,
|
||||
has_parts = EINA_FALSE,
|
||||
has_composite = EINA_FALSE,
|
||||
has_implements = EINA_FALSE,
|
||||
has_constructors = EINA_FALSE,
|
||||
has_events = EINA_FALSE;
|
||||
|
@ -1941,6 +1979,10 @@ parse_class_body(Eo_Lexer *ls, Eolian_Class_Type type)
|
|||
CASE_LOCK(ls, parts, "parts definition")
|
||||
parse_parts(ls);
|
||||
break;
|
||||
case KW_composite:
|
||||
CASE_LOCK(ls, composite, "composite definition")
|
||||
parse_composite(ls);
|
||||
break;
|
||||
case KW_implements:
|
||||
CASE_LOCK(ls, implements, "implements definition")
|
||||
parse_implements(ls, type == EOLIAN_CLASS_INTERFACE);
|
||||
|
|
|
@ -191,6 +191,7 @@ struct _Eolian_Class
|
|||
Eina_List *constructors; /* Eolian_Constructor */
|
||||
Eina_List *events; /* Eolian_Event */
|
||||
Eina_List *parts; /* Eolian_Part */
|
||||
Eina_List *composite; /* Eolian_Class */
|
||||
Eina_List *requires; /* a list of required other classes only used internally */
|
||||
Eina_List *callables; /* internal for now */
|
||||
Eina_Bool class_ctor_enable:1;
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
interface Iface {
|
||||
methods {
|
||||
foo {}
|
||||
bar {}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
class Unimpl implements Iface {
|
||||
composite {
|
||||
Iface;
|
||||
}
|
||||
implements {
|
||||
Iface.foo;
|
||||
}
|
||||
}
|
|
@ -1633,6 +1633,21 @@ EFL_START_TEST(eolian_class_requires_classes)
|
|||
}
|
||||
EFL_END_TEST
|
||||
|
||||
EFL_START_TEST(eolian_class_unimpl)
|
||||
{
|
||||
Eolian_State *eos = eolian_state_new();
|
||||
|
||||
fail_if(!eolian_state_directory_add(eos, TESTS_SRC_DIR"/data"));
|
||||
|
||||
setenv("EOLIAN_CLASS_UNIMPLEMENTED_WARN", "1", 1);
|
||||
const Eolian_Unit *unit = eolian_state_file_parse(eos, TESTS_SRC_DIR"/data/unimpl.eo");
|
||||
unsetenv("EOLIAN_CLASS_UNIMPLEMENTED_WARN");
|
||||
fail_if(!unit);
|
||||
|
||||
eolian_state_free(eos);
|
||||
}
|
||||
EFL_END_TEST
|
||||
|
||||
void eolian_parsing_test(TCase *tc)
|
||||
{
|
||||
tcase_add_test(tc, eolian_simple_parsing);
|
||||
|
@ -1658,4 +1673,5 @@ void eolian_parsing_test(TCase *tc)
|
|||
tcase_add_test(tc, eolian_parts);
|
||||
tcase_add_test(tc, eolian_mixins_require);
|
||||
tcase_add_test(tc, eolian_class_requires_classes);
|
||||
tcase_add_test(tc, eolian_class_unimpl);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue