eolian_aux: add initial eolian_aux APIs

eolian_aux is a set of auxiliary APIs for eolian that build on top
of the existing eolian APIs but do not belong in the main library.
This commit is contained in:
Daniel Kolesa 2018-11-04 18:56:54 +01:00 committed by Christopher Michael
parent 422960b0e6
commit 416c71332d
11 changed files with 658 additions and 4 deletions

View File

@ -4,7 +4,7 @@
lib_LTLIBRARIES += lib/eolian/libeolian.la
installed_eolianmainheadersdir = $(includedir)/eolian-@VMAJ@
dist_installed_eolianmainheaders_DATA = lib/eolian/Eolian.h
dist_installed_eolianmainheaders_DATA = lib/eolian/Eolian.h lib/eolian/Eolian_Aux.h
lib_eolian_libeolian_la_SOURCES = \
lib/eolian/eo_lexer.c \
@ -36,7 +36,8 @@ lib_eolian_libeolian_la_SOURCES = \
lib/eolian/database_var.c \
lib/eolian/database_var_api.c \
lib/eolian/database_validate.c \
lib/eolian/database_check.c
lib/eolian/database_check.c \
lib/eolian/eolian_aux.c
lib_eolian_libeolian_la_CPPFLAGS = -I$(top_builddir)/src/lib/efl @EOLIAN_CFLAGS@ \
-DPACKAGE_DATA_DIR=\"$(datadir)/eolian\"
@ -109,6 +110,7 @@ tests/eolian/eolian_suite
tests_eolian_eolian_suite_SOURCES = \
tests/eolian/eolian_parsing.c \
tests/eolian/eolian_aux.c \
tests/eolian/eolian_static.c \
tests/eolian/eolian_generation.c \
tests/eolian/eolian_generated_future.c \

180
src/lib/eolian/Eolian_Aux.h Normal file
View File

@ -0,0 +1,180 @@
#ifndef EOLIAN_AUX_H
#define EOLIAN_AUX_H
#include <Eolian.h>
#ifdef EAPI
# undef EAPI
#endif
#ifdef _WIN32
# ifdef EFL_BUILD
# ifdef DLL_EXPORT
# define EAPI __declspec(dllexport)
# else
# define EAPI
# endif
# else
# define EAPI __declspec(dllimport)
# endif
#else
# ifdef __GNUC__
# if __GNUC__ >= 4
# define EAPI __attribute__ ((visibility("default")))
# else
# define EAPI
# endif
# else
# define EAPI
# endif
#endif
#ifdef __cplusplus
extern "C" {
#endif
/**
* @page eolian_aux_main Eolian Auxiliary Library (BETA)
*
* @date 2018 (created)
*
* @section toc Table of Contents
*
* @li @ref eolian_aux_main_intro
*
* @section eolian_main_intro Introduction
*
* This is the Eolian auxiliary library, providing API to support generators
* and other utilities which do not fit into the primary Eolian library. All
* these APIs are built on top of the core Eolian APIs.
*
* Recommended reading:
*
* @li @ref Eolian
*
* @addtogroup Eolian
* @{
*/
#ifdef EFL_BETA_API_SUPPORT
/**
* @brief Build reverse children hierarchy of an Eolian state.
*
* The point of this is basically to figure out reverse inheritance. The
* keys of the returned hash will be class pointers and the values will
* be lists. If classes A and B both inherit from C, they will be in C's
* list after lookup.
*
* You are responsible for freeing this hash using standard eina_hash_free().
*
* @param[in] state the Eolian state.
* @return a hash containing the hierarchy.
*
* @ingroup Eolian
*/
EAPI Eina_Hash *eolian_aux_state_class_children_find(const Eolian_State *state);
/**
* @brief Get all APIs that are usable on the class.
*
* The @p funcs parameter will contain the functions, while @p events will
* contain events. The initial items in the lists will be the funcs and
* events defined by the class itself, followed by those defined in the
* classes it inherits from.
*
* Funcs will contain implement pointers, events will contain event pointers.
*
* The @p ownfuncs param defines how many of the list's items are the classes'
* own API. Same happens with @p ownevs for events.
*
* You are responsible for freeing the returned lists but not their contents.
* If you want to skip either of the lists, pass NULL.
*
* @param[in] klass the class.
* @param[out] funcs the functions list reference, NULL if not used.
* @param[out] events the event list reference, NULL if not used.
* @param[out] ownfuncs the number of own functions, NULL if not used.
* @param[out] ownevs the number of own events, NULL if not used.
* @return the total number of items written into either list.
*
* @ingroup Eolian
*/
EAPI size_t eolian_aux_class_callables_get(const Eolian_Class *klass, Eina_List **funcs, Eina_List **events, size_t *ownfuncs, size_t *ownevs);
/**
* @brief Get all implementations of a function in a state.
*
* Given a function and a childen hierarchy from
* eolian_aux_state_class_children_find(), this will func
* all implementations of that function in the system. This
* is useful for various utilities. You need to free the list
* but not the contents.
*
* @param[in] func the function.
* @param[in] class_children the children hierarchy.
* @return a list of implementations.
*
* @ingroup Eolian
*/
EAPI Eina_List *eolian_aux_function_all_implements_get(const Eolian_Function *func, Eina_Hash *class_children);
/**
* @brief Get previous implementation in the inheritance hierarchy.
*
* This performs a depth-first search in the hierarchy, starting with the
* specific given implementation. Once it is found, this parent implementation
* is returned.
*
* @param[in] impl the implementation.
* @return the parent implementation.
*
* @ingroup Eolian
*/
EAPI const Eolian_Implement *eolian_aux_implement_parent_get(const Eolian_Implement *impl);
/**
* @brief Get documentation for an implementaiton.
*
* This first checks if the implementation has documentation for the given
* type. If so, it is returned; if not, parent implementations as specified
* in eolian_aux_implement_parent_get() are searched and the first one to
* have the documentation is used.
*
* @param[in] impl the implementation.
* @param[in] ftype the function type (method, property, getter, setter).
* @return the documentation or NULL.
*
* @ingroup Eolian
*/
EAPI const Eolian_Documentation *eolian_aux_implement_documentation_get(const Eolian_Implement *impl, Eolian_Function_Type ftype);
/**
* @brief Get documentation fallback for an implementation.
*
* This is used when the implement does not have a "common" documentation
* block. If the given implement is a getter and not a setter, this returns
* the getter's documentation. If it's a setter but not a getter, it returns
* the setter documentation. Otherwise, it returns NULL.
*
* @param[in] impl the implementation.
* @return the documentation or NULL.
*
* @ingroup Eolian
*/
EAPI const Eolian_Documentation *eolian_aux_implement_documentation_fallback_get(const Eolian_Implement *impl);
#endif
/**
* @}
*/
#ifdef __cplusplus
} // extern "C" {
#endif
#undef EAPI
#define EAPI
#endif

294
src/lib/eolian/eolian_aux.c Normal file
View File

@ -0,0 +1,294 @@
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "Eolian_Aux.h"
static void
_hashlist_free(void *ptr)
{
eina_list_free((Eina_List *)ptr);
}
EAPI Eina_Hash *
eolian_aux_state_class_children_find(const Eolian_State *state)
{
if (!state)
return NULL;
Eina_Hash *h = eina_hash_pointer_new(_hashlist_free);
Eina_Iterator *itr = eolian_state_classes_get(state);
if (!itr)
return h;
const Eolian_Class *cl;
EINA_ITERATOR_FOREACH(itr, cl)
{
const Eolian_Class *icl = eolian_class_parent_get(cl);
if (icl)
eina_hash_set(h, &icl, eina_list_append(eina_hash_find(h, &icl), cl));
Eina_Iterator *iitr = eolian_class_extensions_get(cl);
EINA_ITERATOR_FOREACH(iitr, icl)
{
eina_hash_set(h, &icl,
eina_list_append(eina_hash_find(h, &icl), cl));
}
eina_iterator_free(iitr);
}
eina_iterator_free(itr);
return h;
}
static size_t
_callables_find_body(const Eolian_Class *pcl,
Eina_List **funcs, Eina_List **events,
Eina_Hash *written)
{
Eina_Iterator *iitr;
const Eolian_Implement *imp;
const Eolian_Event *ev;
size_t total = 0;
if (!funcs)
goto justevs;
iitr = eolian_class_implements_get(pcl);
EINA_ITERATOR_FOREACH(iitr, imp)
{
const Eolian_Function *ifn =
eolian_implement_function_get(imp, NULL);
if (eina_hash_find(written, &ifn))
continue;
*funcs = eina_list_append(*funcs, imp);
++total;
}
eina_iterator_free(iitr);
if (!events)
return total;
justevs:
iitr = eolian_class_events_get(pcl);
EINA_ITERATOR_FOREACH(iitr, ev)
{
/* events do not override */
*events = eina_list_append(*events, ev);
++total;
}
eina_iterator_free(iitr);
return total;
}
static size_t
_callables_find(const Eolian_Class *cl, Eina_List **funcs,
Eina_List **events, Eina_Hash *written)
{
size_t total = 0;
if (!funcs && !events)
return total;
const Eolian_Class *pcl = eolian_class_parent_get(cl);
if (pcl)
total += _callables_find_body(pcl, funcs, events, written);
Eina_Iterator *itr = eolian_class_extensions_get(cl);
EINA_ITERATOR_FOREACH(itr, pcl)
total += _callables_find_body(pcl, funcs, events, written);
return total;
}
EAPI size_t
eolian_aux_class_callables_get(const Eolian_Class *klass,
Eina_List **funcs, Eina_List **events,
size_t *ownfuncs, size_t *ownevs)
{
size_t of = 0, oe = 0, total = 0;
if (!klass || (!funcs && !events))
{
if (ownfuncs) *ownfuncs = of;
if (ownevs) *ownevs = oe;
return total;
}
Eina_Hash *written = eina_hash_pointer_new(NULL);
if (funcs)
{
const Eolian_Implement *imp;
Eina_Iterator *itr = eolian_class_implements_get(klass);
EINA_ITERATOR_FOREACH(itr, imp)
{
const Eolian_Function *ifn =
eolian_implement_function_get(imp, NULL);
eina_hash_set(written, &ifn, ifn);
*funcs = eina_list_append(*funcs, imp);
++of;
}
eina_iterator_free(itr);
}
if (events)
{
const Eolian_Event *ev;
Eina_Iterator *itr = eolian_class_events_get(klass);
EINA_ITERATOR_FOREACH(itr, ev)
{
/* no need to mark in written, events do not override */
*events = eina_list_append(*events, ev);
++oe;
}
}
if (ownfuncs) *ownfuncs = of;
if (ownevs) *ownevs = oe;
total = of + oe;
total += _callables_find(klass, funcs, events, written);
eina_hash_free(written);
return total;
}
static void
_all_impls_find(Eina_List **l, const Eolian_Class *cl,
const Eolian_Function *func, Eina_Hash *got,
Eina_Hash *children)
{
if (eina_hash_find(got, &cl))
return;
eina_hash_add(got, &cl, cl);
Eina_Iterator *itr = eolian_class_implements_get(cl);
const Eolian_Implement *imp;
EINA_ITERATOR_FOREACH(itr, imp)
{
const Eolian_Function *ofn = eolian_implement_function_get(imp, NULL);
if (ofn == func)
{
*l = eina_list_append(*l, imp);
break;
}
}
eina_iterator_free(itr);
Eina_List *tl;
const Eolian_Class *icl;
EINA_LIST_FOREACH(eina_hash_find(children, &cl), tl, icl)
_all_impls_find(l, icl, func, got, children);
}
EAPI Eina_List *
eolian_aux_function_all_implements_get(const Eolian_Function *func,
Eina_Hash *class_children)
{
if (!class_children)
return NULL;
const Eolian_Class *cl = eolian_implement_class_get(
eolian_function_implement_get(func));
Eina_List *ret = NULL;
Eina_Hash *got = eina_hash_pointer_new(NULL);
_all_impls_find(&ret, cl, func, got, class_children);
eina_hash_free(got);
return ret;
}
static const Eolian_Implement * _parent_impl_find(
const char *fulln, const Eolian_Class *cl);
static const Eolian_Implement *
_parent_impl_find_body(const Eolian_Class *icl, const char *fulln)
{
Eina_Iterator *iitr = eolian_class_implements_get(icl);
const Eolian_Implement *iimpl;
EINA_ITERATOR_FOREACH(iitr, iimpl)
{
if (eolian_implement_name_get(iimpl) == fulln)
{
eina_iterator_free(iitr);
return iimpl;
}
}
eina_iterator_free(iitr);
return _parent_impl_find(fulln, icl);
}
static const Eolian_Implement *
_parent_impl_find(const char *fulln, const Eolian_Class *cl)
{
const Eolian_Implement *iret = NULL;
const Eolian_Class *icl = eolian_class_parent_get(cl);
if (icl)
iret = _parent_impl_find_body(icl, fulln);
if (iret)
return iret;
Eina_Iterator *itr = eolian_class_extensions_get(cl);
EINA_ITERATOR_FOREACH(itr, icl)
{
iret = _parent_impl_find_body(icl, fulln);
if (iret)
{
eina_iterator_free(itr);
return iret;
}
}
eina_iterator_free(itr);
return NULL;
}
EAPI const Eolian_Implement *
eolian_aux_implement_parent_get(const Eolian_Implement *impl)
{
return _parent_impl_find(eolian_implement_name_get(impl),
eolian_implement_implementing_class_get(impl));
}
static const Eolian_Documentation *
_parent_documentation_find(const Eolian_Implement *impl,
Eolian_Function_Type ftype)
{
const Eolian_Implement *pimpl = eolian_aux_implement_parent_get(impl);
if (!pimpl)
return NULL;
const Eolian_Documentation *pdoc =
eolian_implement_documentation_get(pimpl, ftype);
if (!pdoc)
return _parent_documentation_find(pimpl, ftype);
return pdoc;
}
EAPI const Eolian_Documentation *
eolian_aux_implement_documentation_get(const Eolian_Implement *impl,
Eolian_Function_Type ftype)
{
const Eolian_Documentation *ret =
eolian_implement_documentation_get(impl, ftype);
if (ret)
return ret;
const Eolian_Class *icl = eolian_implement_implementing_class_get(impl);
if (eolian_implement_class_get(impl) == icl)
return NULL;
const Eolian_Implement *oimp = eolian_function_implement_get(
eolian_implement_function_get(impl, NULL));
if ((ftype == EOLIAN_PROP_GET) && !eolian_implement_is_prop_get(oimp))
return NULL;
if ((ftype == EOLIAN_PROP_SET) && !eolian_implement_is_prop_set(oimp))
return NULL;
return _parent_documentation_find(impl, ftype);
}
EAPI const Eolian_Documentation *
eolian_aux_implement_documentation_fallback_get(const Eolian_Implement *impl)
{
Eina_Bool ig = eolian_implement_is_prop_get(impl),
is = eolian_implement_is_prop_set(impl);
if (ig && !is)
return eolian_implement_documentation_get(impl, EOLIAN_PROP_GET);
else if (is && !ig)
return eolian_implement_documentation_get(impl, EOLIAN_PROP_SET);
return NULL;
}

View File

@ -31,7 +31,8 @@ eolian_src = [
'database_expr_api.c',
'database_var.c',
'database_var_api.c',
'database_validate.c'
'database_validate.c',
'eolian_aux.c'
]
eolian_lib = library('eolian', eolian_src,
@ -50,6 +51,11 @@ eolian = declare_dependency(
eolian_include_dir = join_paths(dir_data, 'eolian', 'include')
install_headers('Eolian.h',
eolian_header_src = [
'Eolian.h',
'Eolian_Aux.h'
]
install_headers(eolian_header_src,
install_dir : dir_package_include
)

View File

@ -0,0 +1,12 @@
class A(C) {
methods {
baz {}
}
events {
test2: void;
}
implements {
C.foo;
C.bar;
}
}

View File

@ -0,0 +1,2 @@
class B(C) {
}

View File

@ -0,0 +1,9 @@
class C {
methods {
foo {}
bar {}
}
events {
test: void;
}
}

View File

@ -0,0 +1,146 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <Eolian_Aux.h>
#include "eolian_suite.h"
EFL_START_TEST(eolian_aux_children)
{
Eolian_State *eos = eolian_state_new();
fail_if(!eolian_state_directory_add(eos, TESTS_SRC_DIR"/data_aux"));
fail_if(!eolian_state_file_parse(eos, TESTS_SRC_DIR"/data_aux/a.eo"));
fail_if(!eolian_state_file_parse(eos, TESTS_SRC_DIR"/data_aux/b.eo"));
Eina_Hash *chash = eolian_aux_state_class_children_find(eos);
fail_if(!chash);
const Eolian_Class *acl = eolian_state_class_by_name_get(eos, "A");
fail_if(!acl);
const Eolian_Class *bcl = eolian_state_class_by_name_get(eos, "B");
fail_if(!bcl);
const Eolian_Class *ccl = eolian_state_class_by_name_get(eos, "C");
fail_if(!ccl);
Eina_List *cl = eina_hash_find(chash, &ccl);
fail_if(!cl);
fail_if(eina_list_count(cl) != 2);
if (eina_list_nth(cl, 0) == bcl)
fail_if(eina_list_nth(cl, 1) != acl);
else
{
fail_if(eina_list_nth(cl, 0) != acl);
fail_if(eina_list_nth(cl, 1) != bcl);
}
eina_hash_free(chash);
eolian_state_free(eos);
}
EFL_END_TEST
EFL_START_TEST(eolian_aux_implements)
{
Eolian_State *eos = eolian_state_new();
fail_if(!eolian_state_directory_add(eos, TESTS_SRC_DIR"/data_aux"));
fail_if(!eolian_state_file_parse(eos, TESTS_SRC_DIR"/data_aux/a.eo"));
fail_if(!eolian_state_file_parse(eos, TESTS_SRC_DIR"/data_aux/b.eo"));
Eina_Hash *chash = eolian_aux_state_class_children_find(eos);
fail_if(!chash);
const Eolian_Class *ccl = eolian_state_class_by_name_get(eos, "C");
fail_if(!ccl);
const Eolian_Function *fn = eolian_class_function_by_name_get(ccl, "foo", EOLIAN_METHOD);
fail_if(!fn);
Eina_List *imps = eolian_aux_function_all_implements_get(fn, chash);
fail_if(!imps);
fail_if(eina_list_count(imps) != 2);
eina_list_free(imps);
eina_hash_free(chash);
eolian_state_free(eos);
}
EFL_END_TEST
EFL_START_TEST(eolian_aux_callables)
{
Eolian_State *eos = eolian_state_new();
fail_if(!eolian_state_directory_add(eos, TESTS_SRC_DIR"/data_aux"));
fail_if(!eolian_state_file_parse(eos, TESTS_SRC_DIR"/data_aux/a.eo"));
const Eolian_Class *acl = eolian_state_class_by_name_get(eos, "A");
fail_if(!acl);
Eina_List *funcs = NULL;
Eina_List *evs = NULL;
size_t ofuncs = 0;
size_t oevs = 0;
size_t clbls = eolian_aux_class_callables_get(acl, &funcs, &evs, &ofuncs, &oevs);
fail_if(clbls != 5);
fail_if(ofuncs != 3);
fail_if(oevs != 1);
fail_if(eina_list_count(funcs) != 3);
fail_if(eina_list_count(evs) != 2);
eina_list_free(funcs);
eina_list_free(evs);
eolian_state_free(eos);
}
EFL_END_TEST
EFL_START_TEST(eolian_aux_implparent)
{
Eolian_State *eos = eolian_state_new();
fail_if(!eolian_state_directory_add(eos, TESTS_SRC_DIR"/data_aux"));
fail_if(!eolian_state_file_parse(eos, TESTS_SRC_DIR"/data_aux/a.eo"));
const Eolian_Class *acl = eolian_state_class_by_name_get(eos, "A");
fail_if(!acl);
const Eolian_Class *ccl = eolian_state_class_by_name_get(eos, "C");
fail_if(!ccl);
const Eolian_Function *fn = eolian_class_function_by_name_get(ccl, "foo", EOLIAN_METHOD);
fail_if(!fn);
const Eolian_Implement *impl = NULL;
Eina_Iterator *itr = eolian_class_implements_get(acl);
EINA_ITERATOR_FOREACH(itr, impl)
{
if (eolian_implement_function_get(impl, NULL) == fn)
break;
}
eina_iterator_free(itr);
fail_if(!impl);
fail_if(eolian_implement_function_get(impl, NULL) != fn);
fail_if(eolian_implement_class_get(impl) != ccl);
fail_if(eolian_implement_implementing_class_get(impl) != acl);
const Eolian_Implement *pimpl = eolian_aux_implement_parent_get(impl);
fail_if(eolian_implement_class_get(pimpl) != ccl);
fail_if(eolian_implement_implementing_class_get(pimpl) != ccl);
eolian_state_free(eos);
}
EFL_END_TEST
void eolian_aux_test(TCase *tc)
{
tcase_add_test(tc, eolian_aux_children);
tcase_add_test(tc, eolian_aux_implements);
tcase_add_test(tc, eolian_aux_callables);
tcase_add_test(tc, eolian_aux_implparent);
}

View File

@ -14,6 +14,7 @@ static const Efl_Test_Case etc[] = {
{ "Eolian Parsing", eolian_parsing_test},
{ "Eolian Static Analysis", eolian_static_test},
{ "Eolian Generation", eolian_generation_test},
{ "Eolian Aux", eolian_aux_test},
{ NULL, NULL }
};

View File

@ -4,6 +4,7 @@
#include <check.h>
#include "../efl_check.h"
void eolian_parsing_test(TCase *tc);
void eolian_aux_test(TCase *tc);
void eolian_static_test(TCase *tc);
void eolian_generation_test(TCase *tc);

View File

@ -4,6 +4,7 @@ priv_eo_files = [
eolian_test_src = [
'eolian_parsing.c',
'eolian_aux.c',
'eolian_generation.c',
'eolian_generated_future.c',
'eolian_suite.c',