From a541aecf766a3ac4c0e49316b589fa860a743298 Mon Sep 17 00:00:00 2001 From: Daniel Zaoui Date: Thu, 1 May 2014 15:07:26 +0300 Subject: [PATCH] Eolian/Generator: add support for implementation source file. By using -gi option, the generator appends the functions that are present into the given eo file and missing into the developer file (given via -o option as an in/out file). @feature --- src/Makefile_Eolian.am | 2 + src/bin/eolian/impl_generator.c | 281 ++++++++++++++++++++++++++++++++ src/bin/eolian/impl_generator.h | 22 +++ src/bin/eolian/main.c | 79 +++++++-- src/tests/eolian/data/base.eo | 3 + src/tests/eolian/data/object.eo | 68 ++++++++ 6 files changed, 445 insertions(+), 10 deletions(-) create mode 100644 src/bin/eolian/impl_generator.c create mode 100644 src/bin/eolian/impl_generator.h create mode 100644 src/tests/eolian/data/object.eo diff --git a/src/Makefile_Eolian.am b/src/Makefile_Eolian.am index db8ebc9b77..e31111176f 100644 --- a/src/Makefile_Eolian.am +++ b/src/Makefile_Eolian.am @@ -30,6 +30,8 @@ bin_eolian_eolian_gen_SOURCES = \ bin/eolian/common_funcs.h \ bin/eolian/eo1_generator.c \ bin/eolian/eo1_generator.h \ + bin/eolian/impl_generator.c \ + bin/eolian/impl_generator.h \ bin/eolian/legacy_generator.c \ bin/eolian/legacy_generator.h \ bin/eolian/main.c diff --git a/src/bin/eolian/impl_generator.c b/src/bin/eolian/impl_generator.c new file mode 100644 index 0000000000..b2abf0ee35 --- /dev/null +++ b/src/bin/eolian/impl_generator.c @@ -0,0 +1,281 @@ +#include +#include + +#include "Eolian.h" +#include "impl_generator.h" +#include "common_funcs.h" + +static Eina_Bool +_params_generate(Eolian_Function foo, Eolian_Function_Type ftype, Eina_Bool var_as_ret, Eina_Strbuf *params) +{ + const Eina_List *itr; + Eolian_Function_Parameter param; + eina_strbuf_reset(params); + EINA_LIST_FOREACH(eolian_property_keys_list_get(foo), itr, param) + { + const char *pname; + const char *ptype; + eolian_parameter_information_get(param, NULL, &ptype, &pname, NULL); + Eina_Bool had_star = !!strchr(ptype, '*'); + Eina_Bool is_const = eolian_parameter_const_attribute_get(param, ftype == EOLIAN_PROP_GET); + if (eina_strbuf_length_get(params)) eina_strbuf_append(params, ", "); + eina_strbuf_append_printf(params, "%s%s%s%s", + is_const?"const ":"", ptype, + had_star?"":" ", + pname); + } + if (!var_as_ret) + { + Eina_Bool add_star = (ftype == EOLIAN_PROP_GET); + EINA_LIST_FOREACH(eolian_parameters_list_get(foo), itr, param) + { + const char *pname; + const char *ptype; + Eolian_Parameter_Dir pdir; + eolian_parameter_information_get(param, &pdir, &ptype, &pname, NULL); + Eina_Bool is_const = eolian_parameter_const_attribute_get(param, ftype == EOLIAN_PROP_GET); + Eina_Bool had_star = !!strchr(ptype, '*'); + if (ftype == EOLIAN_UNRESOLVED || ftype == EOLIAN_METHOD) add_star = (pdir == EOLIAN_OUT_PARAM); + if (eina_strbuf_length_get(params)) eina_strbuf_append(params, ", "); + eina_strbuf_append_printf(params, "%s%s%s%s%s", + is_const?"const ":"", + ptype, had_star?"":" ", add_star?"*":"", pname); + } + } + return EINA_TRUE; +} + +static Eina_Bool +_function_exists(const char* func_name, Eina_Strbuf *buffer) +{ + const char *ptr = eina_strbuf_string_get(buffer); + int func_len = strlen(func_name); + while ((ptr = strstr(ptr, func_name)) != NULL) + { + switch (*(ptr - 1)) + { + case '\n': case ' ': + { + switch (*(ptr + func_len)) + { + case ' ': case '(': + return EINA_TRUE; + } + } + default: + ptr++; /* so strstr doesn't fall again on func_name */ + } + } + return EINA_FALSE; +} + +/* Check if the type is used in the file, not if it is a typedef... */ +static Eina_Bool +_type_exists(const char* type_name, Eina_Strbuf *buffer) +{ + const char *ptr = eina_strbuf_string_get(buffer); + int type_len = strlen(type_name); + while ((ptr = strstr(ptr, type_name)) != NULL) + { + switch (*(ptr - 1)) + { + case '\n': case ' ': case ',': + { + switch (*(ptr + type_len)) + { + case '\n': case ' ': case ',': case ';': + return EINA_TRUE; + } + } + default: + ptr++; /* so strstr doesn't fall again on type_name */ + } + } + return EINA_FALSE; +} + +static Eina_Bool +_prototype_generate(Eolian_Function foo, Eolian_Function_Type ftype, Eina_Strbuf *data_type_buf, char *impl_name, Eina_Strbuf *buffer) +{ + Eina_Bool var_as_ret = EINA_FALSE, ret_const = EINA_FALSE; + Eina_Strbuf *params = NULL; + char func_name[100]; + + if (!impl_name && eolian_function_is_virtual_pure(foo, ftype)) return EINA_TRUE; + + sprintf(func_name, "_%s_%s%s", + impl_name?impl_name:lowclass, eolian_function_name_get(foo), + ftype == EOLIAN_PROP_GET?"_get": (ftype == EOLIAN_PROP_SET?"_set":"")); + + if (_function_exists(func_name, buffer)) return EINA_TRUE; + + printf("Generation of function %s\n", func_name); + const char *rettype = eolian_function_return_type_get(foo, ftype); + if (ftype == EOLIAN_PROP_GET && !rettype) + { + const Eina_List *l = eolian_parameters_list_get(foo); + if (eina_list_count(l) == 1) + { + Eolian_Function_Parameter param = eina_list_data_get(l); + eolian_parameter_information_get(param, NULL, &rettype, NULL, NULL); + var_as_ret = EINA_TRUE; + ret_const = eolian_parameter_const_attribute_get(param, EINA_TRUE); + } + } + + params = eina_strbuf_new(); + _params_generate(foo, ftype, var_as_ret, params); + if (eina_strbuf_length_get(params)) + eina_strbuf_prepend_printf(params, ", "); + + eina_strbuf_append_printf(buffer, + "EOLIAN static %s%s\n%s(%sEo *obj, %s *pd%s%s)\n{\n\n}\n\n", + ret_const?"const ":"", !rettype?"void":rettype, + func_name, + eolian_function_object_is_const(foo)?"const ":"", + !eina_strbuf_length_get(data_type_buf) ? "void" : eina_strbuf_string_get(data_type_buf), + !eina_strbuf_length_get(data_type_buf) ? " EINA_UNUSED" : "", + eina_strbuf_string_get(params) + ); + + eina_strbuf_free(params); + return EINA_TRUE; +} + +Eina_Bool +impl_source_generate(const char *class_name, Eina_Strbuf *buffer) +{ + Eina_Bool ret = EINA_FALSE; + Eina_Strbuf *data_type_buf = eina_strbuf_new(); + const Eina_List *itr_funcs; + Eolian_Function foo; + Eina_Strbuf *begin = eina_strbuf_new(); + + _class_func_names_fill(class_name, NULL); + + if (!_type_exists("EFL_BETA_API_SUPPORT", buffer)) + { + printf("Generation of EFL_BETA_API_SUPPORT\n"); + eina_strbuf_append_printf(begin, "#define EFL_BETA_API_SUPPORT\n"); + } + + if (!_type_exists("", buffer)) + { + printf("Generation of #include and \"%s.eo.h\"\n", lowclass); + eina_strbuf_append_printf(begin, "#include \n#include \"%s.eo.h\"\n\n", lowclass); + } + + /* Little calculation of the prefix of the data */ + const char *data_type = eolian_class_data_type_get(class_name); + if (data_type) + { + if (strcmp(data_type, "null")) + eina_strbuf_append_printf(data_type_buf, "%s", data_type); + } + else + eina_strbuf_append_printf(data_type_buf, "%s_Data", class_name); + + /* Definition of the structure */ + const char *data_type_str = eina_strbuf_string_get(data_type_buf); + if (!_type_exists(data_type_str, buffer) && 0 != eina_strbuf_length_get(data_type_buf)) + { + printf("Generation of type %s\n", data_type_str); + eina_strbuf_append_printf(begin, "typedef struct\n{\n\n} %s;\n\n", data_type_str); + } + + if (eina_strbuf_length_get(begin)) + eina_strbuf_prepend_printf(buffer, "%s", eina_strbuf_string_get(begin)); + eina_strbuf_free(begin); + + /* Properties */ + EINA_LIST_FOREACH(eolian_class_functions_list_get(class_name, EOLIAN_PROPERTY), itr_funcs, foo) + { + const Eolian_Function_Type ftype = eolian_function_type_get(foo); + if (ftype == EOLIAN_PROP_SET || ftype == EOLIAN_PROPERTY) + _prototype_generate(foo, EOLIAN_PROP_SET, data_type_buf, NULL, buffer); + + if (ftype == EOLIAN_PROP_GET || ftype == EOLIAN_PROPERTY) + _prototype_generate(foo, EOLIAN_PROP_GET, data_type_buf, NULL, buffer); + } + + /* Methods */ + EINA_LIST_FOREACH(eolian_class_functions_list_get(class_name, EOLIAN_METHOD), itr_funcs, foo) + { + _prototype_generate(foo, EOLIAN_METHOD, data_type_buf, NULL, buffer); + } + + /* Custom constructors */ + EINA_LIST_FOREACH(eolian_class_functions_list_get(class_name, EOLIAN_CTOR), itr_funcs, foo) + { + _prototype_generate(foo, EOLIAN_CTOR, data_type_buf, NULL, buffer); + } + + if (eolian_class_implements_list_get(class_name)) + { + Eolian_Implement impl_desc; + EINA_LIST_FOREACH(eolian_class_implements_list_get(class_name), itr_funcs, impl_desc) + { + const char *func_name; + const char *impl_class; + Eolian_Function_Type ftype; + + eolian_implement_information_get(impl_desc, &impl_class, &func_name, &ftype); + + _class_func_names_fill(impl_class, NULL); + + char implname[0xFF]; + char *tmp = implname; + sprintf(implname, "%s_%s", class_name, impl_class); + eina_str_tolower(&tmp); + + foo = eolian_class_function_find_by_name(impl_class, func_name, ftype); + if (!foo) + { + ERR ("Failed to generate implementation of %s:%s - missing form super class", impl_class, func_name); + goto end; + } + + switch (ftype) + { + case EOLIAN_PROP_SET: case EOLIAN_PROP_GET: + _prototype_generate(foo, ftype, data_type_buf, implname, buffer); + break; + default: + _prototype_generate(foo, eolian_function_type_get(foo), data_type_buf, implname, buffer); + break; + } + } + } + + _class_func_names_fill(class_name, NULL); + if (eolian_class_ctor_enable_get(class_name)) + { + char func_name[100]; + sprintf(func_name, "_%s_class_constructor", lowclass); + if (!_function_exists(func_name, buffer)) + { + printf("Generation of function %s\n", func_name); + eina_strbuf_append_printf(buffer, + "EOLIAN static void\n_%s_class_constructor(Eo_Class *klass)\n{\n\n}\n\n", + lowclass); + } + } + + if (eolian_class_dtor_enable_get(class_name)) + { + char func_name[100]; + sprintf(func_name, "_%s_class_destructor", lowclass); + if (!_function_exists(func_name, buffer)) + { + printf("Generation of function %s\n", func_name); + eina_strbuf_append_printf(buffer, "EOLIAN static void\n_%s_class_destructor(Eo_Class *klass)\n{\n\n}\n\n", + lowclass); + } + } + + ret = EINA_TRUE; +end: + eina_strbuf_free(data_type_buf); + return ret; +} + diff --git a/src/bin/eolian/impl_generator.h b/src/bin/eolian/impl_generator.h new file mode 100644 index 0000000000..b6793d785b --- /dev/null +++ b/src/bin/eolian/impl_generator.h @@ -0,0 +1,22 @@ +#ifndef IMPL_GENERATOR_H +#define IMPL_GENERATOR_H + +#include + +/* + * @brief Generate the implementation source code of a class + * + * This function generates all the source code of a class. + * + * @param[in] classname class name + * @param[inout] buf buffer to fill + * + * @return EINA_TRUE on success, EINA_FALSE on error. + * + */ +Eina_Bool +impl_source_generate(const char *classname, Eina_Strbuf *buf); + +#endif + + diff --git a/src/bin/eolian/main.c b/src/bin/eolian/main.c index 91e44ddf7e..576d18cf5f 100644 --- a/src/bin/eolian/main.c +++ b/src/bin/eolian/main.c @@ -5,6 +5,7 @@ #include "Eolian.h" #include "legacy_generator.h" #include "eo1_generator.h" +#include "impl_generator.h" #include "common_funcs.h" static char* @@ -118,6 +119,59 @@ end: return ret; } +static Eina_Bool +_generate_impl_c_file(char *filename, const char *classname) +{ + Eina_Bool ret = EINA_FALSE; + long file_size = 0; + + FILE* fd = fopen(filename, "rb"); + if (!fd) + { + ERR("Couldnt open file %s for reading", filename); + goto end; + } + + fseek(fd, 0, SEEK_END); + file_size = ftell(fd); + fseek(fd, 0, SEEK_SET); + char *content = malloc(file_size + 1); + fread(content, file_size, 1, fd); + content[file_size] = '\0'; + fclose(fd); + + if (!content) + { + ERR("Couldnt read file %s", filename); + goto end; + } + + Eina_Strbuf *buffer = eina_strbuf_manage_new(content); + + if (!impl_source_generate(classname, buffer)) + { + ERR("Failed to generate source for %s", classname); + goto end; + } + + fd = fopen(filename, "wb"); + if (!fd) + { + ERR("Couldnt open file %s for writing", filename); + goto end; + } + + const char *text = eina_strbuf_string_get(buffer); + if (text) fputs(text, fd); + + fclose(fd); + + ret = EINA_TRUE; +end: + eina_strbuf_free(buffer); + return ret; +} + // TODO join with header gen. static Eina_Bool _generate_legacy_header_file(char *filename, const char *classname) @@ -160,7 +214,8 @@ enum { NO_WAY_GEN, H_GEN, - C_GEN + C_GEN, + C_IMPL_GEN }; static int gen_opt = NO_WAY_GEN; static int eo_needed = 0; @@ -198,6 +253,7 @@ int main(int argc, char **argv) {"help", no_argument, 0, 'h'}, {"gh", no_argument, &gen_opt, H_GEN}, {"gc", no_argument, &gen_opt, C_GEN}, + {"gi", no_argument, &gen_opt, C_IMPL_GEN}, {"output", required_argument, 0, 'o'}, {"legacy", no_argument, (int *)&legacy_support, EINA_TRUE}, {"include", required_argument, 0, 'I'}, @@ -233,15 +289,14 @@ int main(int argc, char **argv) if (help) { - printf("Usage: %s [-h/--help] [-v/--verbose] [-I/--include input_dir] [--legacy] [--gh|--gc|--ah] [--output/-o outfile] file.eo ... \n", argv[0]); + printf("Usage: %s [-h/--help] [-v/--verbose] [-I/--include input_dir] [--legacy] [--gh|--gc|--gi] [--output/-o outfile] file.eo ... \n", argv[0]); printf(" --help/-h Print that help\n"); printf(" --include/-I Include 'input_dir' as directory to search .eo files into\n"); printf(" --output/-o Force output filename to 'outfile'\n"); printf(" --eo Set generator to eo mode. Must be specified\n"); - printf(" --gh Generate c header file [.h]\n"); - printf(" --gc Generate c source file [.c]\n"); - printf(" --ah Append eo class definitions to an existing c header file [.h]\n"); - printf(" --al Append legacy function definitions to an existing c header file [.h]\n"); + printf(" --gh Generate C header file [.h]\n"); + printf(" --gc Generate C source file [.c]\n"); + printf(" --gi Generate C implementation source file [.c]. The output will be a series of functions that have to be filled.\n"); printf(" --legacy Generate legacy\n"); ret = 0; goto end; @@ -284,10 +339,8 @@ int main(int argc, char **argv) { if (!output_filename) { - output_filename = malloc(strlen(eina_list_data_get(files4gen)) + 5); - strcpy(output_filename, eina_list_data_get(files4gen)); - if (C_GEN == gen_opt) strcat(output_filename, ".c"); - else strcat(output_filename, ".h"); + ERR("You must use -o argument for files generation."); + goto end; } switch (gen_opt) { @@ -306,6 +359,12 @@ int main(int argc, char **argv) ret = _generate_c_file(output_filename, classname, legacy_support)?0:1; break; } + case C_IMPL_GEN: + { + INF("Generating user source file %s\n", output_filename); + ret = _generate_impl_c_file(output_filename, classname) ? 0 : 1; + break; + } default: ERR("Bad generation option\n"); break; diff --git a/src/tests/eolian/data/base.eo b/src/tests/eolian/data/base.eo index e8132b8d4a..a252140ebb 100644 --- a/src/tests/eolian/data/base.eo +++ b/src/tests/eolian/data/base.eo @@ -5,4 +5,7 @@ class Base { destructor { } } + implements { + virtual::constructor; + } } diff --git a/src/tests/eolian/data/object.eo b/src/tests/eolian/data/object.eo new file mode 100644 index 0000000000..d250c8520d --- /dev/null +++ b/src/tests/eolian/data/object.eo @@ -0,0 +1,68 @@ +class Object (Base) { + constructors { + constructor_1 { + params { + @in int a; + @in char b; + } + } + constructor_2 { + } + } + properties { + a { + set { + return Eina_Bool(EINA_FALSE); + value: const; + } + get { + } + keys { + const char *part; + } + values { + @own Eina_List * value; + } + } + b { + set { + } + get { + /* set as virtual pure - no implementation expected */ + } + values { + @own Eina_List * value; + } + } + } + methods { + foo1 { + /*@ comment foo */ + params { + @in int a; /*@ a */ + @inout char *b; + @out double c; + } + return char *(NULL); /*@ comment for method return */ + } + foo2 { + /*@ comment foo */ + params { + @in int a; + @in const char *b; + } + const; + } + pure_foo3 { + /* set as virtual pure - no implementation expected */ + } + } + implements { + class::constructor; + class::destructor; + Base::constructor; + Base::destructor; + virtual::pure_foo3; + virtual::b::get; + } +}