From ab2e608239d0518241454bd7cb6abe3f680764c1 Mon Sep 17 00:00:00 2001 From: Daniel Kolesa Date: Thu, 30 Jun 2016 14:04:03 +0100 Subject: [PATCH] eolian: add support for static and terminated arrays Adds two new type types, STATIC_ARRAY and TERMINATED_ARRAY. Static arrays are only allowed as struct members right now - they translate to regular C static arrays (allowing them elsewhere wouldn't be good, as C isn't very good at working with the size information). Terminated arrays are basically sequences of data terminated at the end. The base type of static arrays can be any type that is not marked ref (explicit ref may get allowed later). The base type of terminated arrays has the same restriction plus that it has to be either implicitly reference type (i.e. translating to pointer in C), integer type or a character. In case of ref types, the terminator is NULL. In case of integer types, the terminator is a zero. In case of character types, the terminator is also a zero (null terminator like C strings). @feature --- src/bin/eolian/types_generator.c | 21 ++++- src/bindings/luajit/eolian.lua | 23 ++++-- src/lib/eolian/Eolian.h | 12 +++ src/lib/eolian/database_type.c | 3 +- src/lib/eolian/database_type_api.c | 7 ++ src/lib/eolian/database_validate.c | 2 + src/lib/eolian/eo_lexer.c | 5 +- src/lib/eolian/eo_lexer.h | 2 + src/lib/eolian/eo_parser.c | 124 +++++++++++++++++++++++------ src/lib/eolian/eolian_database.h | 1 + src/tests/eolian/data/struct.eo | 2 + src/tests/eolian/data/struct_ref.c | 2 + src/tests/eolian/eolian_parsing.c | 15 ++++ 13 files changed, 180 insertions(+), 39 deletions(-) diff --git a/src/bin/eolian/types_generator.c b/src/bin/eolian/types_generator.c index 59954054e1..241da0a60f 100644 --- a/src/bin/eolian/types_generator.c +++ b/src/bin/eolian/types_generator.c @@ -70,10 +70,23 @@ _type_generate(const Eolian_Typedecl *tp, Eina_Bool full, Eina_Bool use_legacy) EINA_ITERATOR_FOREACH(members, member) { const Eolian_Type *type = eolian_typedecl_struct_field_type_get(member); - Eina_Stringshare *c_type = eolian_type_c_type_get(type); - eina_strbuf_append_printf(buf, " %s%s%s;", - c_type, strchr(c_type, '*')?"":" ", - eolian_typedecl_struct_field_name_get(member)); + Eina_Stringshare *c_type = NULL; + if (eolian_type_type_get(type) == EOLIAN_TYPE_STATIC_ARRAY) + { + c_type = eolian_type_c_type_get(eolian_type_base_type_get(type)); + eina_strbuf_append_printf(buf, " %s%s%s[%zu];", + c_type, strchr(c_type, '*')?"":" ", + eolian_typedecl_struct_field_name_get(member), + eolian_type_array_size_get(type)); + } + else + { + c_type = eolian_type_c_type_get(type); + eina_strbuf_append_printf(buf, " %s%s%s;", + c_type, strchr(c_type, '*')?"":" ", + eolian_typedecl_struct_field_name_get(member)); + } + eina_stringshare_del(c_type); const Eolian_Documentation *fdoc = eolian_typedecl_struct_field_documentation_get(member); if (fdoc) diff --git a/src/bindings/luajit/eolian.lua b/src/bindings/luajit/eolian.lua index 4f8a1c9012..9cd7050bc5 100644 --- a/src/bindings/luajit/eolian.lua +++ b/src/bindings/luajit/eolian.lua @@ -76,6 +76,8 @@ ffi.cdef [[ EOLIAN_TYPE_COMPLEX, EOLIAN_TYPE_POINTER, EOLIAN_TYPE_CLASS, + EOLIAN_TYPE_STATIC_ARRAY, + EOLIAN_TYPE_TERMINATED_ARRAY, EOLIAN_TYPE_UNDEFINED } Eolian_Type_Type; @@ -300,6 +302,7 @@ ffi.cdef [[ const Eolian_Type *eolian_typedecl_aliased_base_get(const Eolian_Typedecl *tp); const Eolian_Class *eolian_type_class_get(const Eolian_Type *tp); + size_t eolian_type_array_size_get(const Eolian_Type *tp); Eina_Bool eolian_type_is_own(const Eolian_Type *tp); Eina_Bool eolian_type_is_const(const Eolian_Type *tp); Eina_Bool eolian_type_is_ref(const Eolian_Type *tp); @@ -448,13 +451,15 @@ M.declaration_type = { } M.type_type = { - UNKNOWN = 0, - VOID = 1, - REGULAR = 2, - COMPLEX = 3, - POINTER = 4, - CLASS = 5, - UNDEFINED = 6 + UNKNOWN = 0, + VOID = 1, + REGULAR = 2, + COMPLEX = 3, + POINTER = 4, + CLASS = 5, + STATIC_ARRAY = 6, + TERMINATED_ARRAY = 7, + UNDEFINED = 8 } M.typedecl_type = { @@ -650,6 +655,10 @@ M.Type = ffi.metatype("Eolian_Type", { return v end, + array_size_get = function(self) + return tonumber(eolian.eolian_type_array_size_get(self)) + end, + is_own = function(self) return eolian.eolian_type_is_own(self) ~= 0 end, diff --git a/src/lib/eolian/Eolian.h b/src/lib/eolian/Eolian.h index 8039337a2f..59eb0f6032 100644 --- a/src/lib/eolian/Eolian.h +++ b/src/lib/eolian/Eolian.h @@ -220,6 +220,8 @@ typedef enum EOLIAN_TYPE_COMPLEX, EOLIAN_TYPE_POINTER, EOLIAN_TYPE_CLASS, + EOLIAN_TYPE_STATIC_ARRAY, + EOLIAN_TYPE_TERMINATED_ARRAY, EOLIAN_TYPE_UNDEFINED } Eolian_Type_Type; @@ -1726,6 +1728,16 @@ EAPI const Eolian_Type *eolian_type_aliased_base_get(const Eolian_Type *tp); */ EAPI const Eolian_Class *eolian_type_class_get(const Eolian_Type *tp); +/* + * @brief Get the size of an EOLIAN_TYPE_STATIC_ARRAY. + * + * @param[in] tp the type. + * @return the size or 0. + * + * @ingroup Eolian + */ +EAPI size_t eolian_type_array_size_get(const Eolian_Type *tp); + /* * @brief Get whether the given type is owned. * diff --git a/src/lib/eolian/database_type.c b/src/lib/eolian/database_type.c index ee1f11477d..d0e64233b1 100644 --- a/src/lib/eolian/database_type.c +++ b/src/lib/eolian/database_type.c @@ -101,9 +101,10 @@ database_type_to_str(const Eolian_Type *tp, Eina_Strbuf *buf, const char *name) eina_strbuf_append(buf, "__undefined_type"); else { + /* handles arrays and pointers as they all serialize to pointers */ Eolian_Type *btp = tp->base_type; database_type_to_str(tp->base_type, buf, NULL); - if (btp->type != EOLIAN_TYPE_POINTER || btp->is_const) + if (eina_strbuf_string_get(buf)[eina_strbuf_length_get(buf) - 1] != '*') eina_strbuf_append_char(buf, ' '); eina_strbuf_append_char(buf, '*'); if (tp->is_const) eina_strbuf_append(buf, " const"); diff --git a/src/lib/eolian/database_type_api.c b/src/lib/eolian/database_type_api.c index 6afe870fc4..4411f8ae5b 100644 --- a/src/lib/eolian/database_type_api.c +++ b/src/lib/eolian/database_type_api.c @@ -313,6 +313,13 @@ eolian_type_class_get(const Eolian_Type *tp) return eolian_class_get_by_name(tp->full_name); } +EAPI size_t +eolian_type_array_size_get(const Eolian_Type *tp) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(tp, 0); + return tp->static_size; +} + EAPI Eina_Bool eolian_type_is_own(const Eolian_Type *tp) { diff --git a/src/lib/eolian/database_validate.c b/src/lib/eolian/database_validate.c index b9657ee9af..aab20a594c 100644 --- a/src/lib/eolian/database_validate.c +++ b/src/lib/eolian/database_validate.c @@ -242,6 +242,8 @@ _validate_type(const Validator *vs, const Eolian_Type *tp) return _validate_typedecl(vs, tpp); } case EOLIAN_TYPE_POINTER: + case EOLIAN_TYPE_STATIC_ARRAY: + case EOLIAN_TYPE_TERMINATED_ARRAY: return _validate_type(vs, tp->base_type); case EOLIAN_TYPE_CLASS: { diff --git a/src/lib/eolian/eo_lexer.c b/src/lib/eolian/eo_lexer.c index 1a0d9bd15f..a3a4d4d7f6 100644 --- a/src/lib/eolian/eo_lexer.c +++ b/src/lib/eolian/eo_lexer.c @@ -72,6 +72,8 @@ static const char * const ctypes[] = "void", + NULL, NULL, /* array types */ + "Eina_Accessor", "Eina_Array", "Eina_Iterator", "Eina_Hash", "Eina_List", "Eina_Promise", "Eina_Value", "const char *", "Eina_Stringshare *", @@ -943,7 +945,6 @@ lex(Eo_Lexer *ls, Eo_Token *tok) { int dcol = ls->column; next_char(ls); - if (!ls->expr_mode) return '.'; if (!isdigit(ls->current)) return '.'; eina_strbuf_reset(ls->buff); eina_strbuf_append_char(ls->buff, '.'); @@ -959,7 +960,7 @@ lex(Eo_Lexer *ls, Eo_Token *tok) next_char(ls); continue; } - else if (ls->expr_mode && isdigit(ls->current)) + else if (isdigit(ls->current)) { int col = ls->column; eina_strbuf_reset(ls->buff); diff --git a/src/lib/eolian/eo_lexer.h b/src/lib/eolian/eo_lexer.h index 5105288e38..cc36d77fe8 100644 --- a/src/lib/eolian/eo_lexer.h +++ b/src/lib/eolian/eo_lexer.h @@ -50,6 +50,8 @@ enum Tokens \ KW(void), \ \ + KW(static_array), KW(terminated_array), \ + \ KW(accessor), KW(array), KW(iterator), KW(hash), KW(list), \ KW(promise), \ KW(generic_value), KW(string), KW(stringshare), \ diff --git a/src/lib/eolian/eo_parser.c b/src/lib/eolian/eo_parser.c index 8f92002d01..ead4183e19 100644 --- a/src/lib/eolian/eo_parser.c +++ b/src/lib/eolian/eo_parser.c @@ -187,7 +187,7 @@ static Eina_Strbuf * parse_name(Eo_Lexer *ls, Eina_Strbuf *buf) { check(ls, TOK_VALUE); - if (eo_lexer_get_c_type(ls->t.kw)) + if (eo_lexer_is_type_keyword(ls->t.kw)) eo_lexer_syntax_error(ls, "invalid name"); eina_strbuf_reset(buf); for (;;) @@ -198,7 +198,7 @@ parse_name(Eo_Lexer *ls, Eina_Strbuf *buf) eo_lexer_get(ls); eina_strbuf_append(buf, "."); check(ls, TOK_VALUE); - if (eo_lexer_get_c_type(ls->t.kw)) + if (eo_lexer_is_type_keyword(ls->t.kw)) eo_lexer_syntax_error(ls, "invalid name"); } return buf; @@ -460,14 +460,15 @@ parse_expr(Eo_Lexer *ls) return parse_expr_bin(ls, 1); } -static Eolian_Type *parse_type_void(Eo_Lexer *ls, Eina_Bool allow_ref); +static Eolian_Type *parse_type_void(Eo_Lexer *ls, Eina_Bool allow_ref, + Eina_Bool allow_sarray); static Eolian_Type * -parse_type(Eo_Lexer *ls, Eina_Bool allow_ref) +parse_type(Eo_Lexer *ls, Eina_Bool allow_ref, Eina_Bool allow_sarray) { Eolian_Type *ret; eo_lexer_context_push(ls); - ret = parse_type_void(ls, allow_ref); + ret = parse_type_void(ls, allow_ref, allow_sarray); if (ret->type == EOLIAN_TYPE_VOID) { eo_lexer_context_restore(ls); @@ -516,7 +517,7 @@ parse_struct(Eo_Lexer *ls, const char *name, Eina_Bool is_extern, def->field_list = eina_list_append(def->field_list, fdef); eo_lexer_get(ls); check_next(ls, ':'); - tp = parse_type(ls, EINA_TRUE); + tp = parse_type(ls, EINA_TRUE, EINA_TRUE); FILL_BASE(fdef->base, ls, fline, fcol); fdef->type = tp; fdef->name = eina_stringshare_ref(fname); @@ -693,6 +694,18 @@ _parse_dep(Eo_Lexer *ls, const char *fname, const char *name) } } +static const Eina_Bool _ownable_types[] = { + EINA_FALSE, /* unknown */ + EINA_FALSE, /* void */ + EINA_FALSE, /* regular */ + EINA_TRUE, /* complex */ + EINA_TRUE, /* pointer */ + EINA_TRUE, /* class */ + EINA_TRUE, /* static array */ + EINA_TRUE, /* terminated array */ + EINA_FALSE /* undefined */ +}; + static Eina_Bool _type_is_ownable(Eolian_Type *tp) { @@ -706,13 +719,25 @@ _type_is_ownable(Eolian_Type *tp) return EINA_FALSE; return (ct[strlen(ct) - 1] == '*'); } - return (tp->type == EOLIAN_TYPE_POINTER || - tp->type == EOLIAN_TYPE_COMPLEX || - tp->type == EOLIAN_TYPE_CLASS); + return _ownable_types[tp->type]; +} + +static Eina_Bool +_type_is_terminatable(Eolian_Type *tp) +{ + if (_type_is_ownable(tp)) + return EINA_TRUE; + if (tp->type == EOLIAN_TYPE_REGULAR) + { + int kwid = eo_lexer_keyword_str_to_id(tp->name); + /* don't include bool, it only has 2 values so it's useless */ + return (kwid >= KW_byte && kwid < KW_bool); + } + return EINA_FALSE; } static Eolian_Type * -parse_type_void(Eo_Lexer *ls, Eina_Bool allow_ref) +parse_type_void(Eo_Lexer *ls, Eina_Bool allow_ref, Eina_Bool allow_sarray) { Eolian_Type *def; Eina_Strbuf *buf; @@ -726,7 +751,7 @@ parse_type_void(Eo_Lexer *ls, Eina_Bool allow_ref) pline = ls->line_number; pcol = ls->column; check_next(ls, '('); - def = parse_type_void(ls, allow_ref); + def = parse_type_void(ls, allow_ref, EINA_FALSE); FILL_BASE(def->base, ls, line, col); def->is_const = EINA_TRUE; check_match(ls, ')', '(', pline, pcol); @@ -739,7 +764,7 @@ parse_type_void(Eo_Lexer *ls, Eina_Bool allow_ref) pline = ls->line_number; pcol = ls->column; check_next(ls, '('); - def = parse_type_void(ls, EINA_FALSE); + def = parse_type_void(ls, EINA_FALSE, EINA_FALSE); FILL_BASE(def->base, ls, line, col); def->is_ref = EINA_TRUE; check_match(ls, ')', '(', pline, pcol); @@ -753,7 +778,7 @@ parse_type_void(Eo_Lexer *ls, Eina_Bool allow_ref) pcolumn = ls->column; check_next(ls, '('); eo_lexer_context_push(ls); - def = parse_type_void(ls, allow_ref); + def = parse_type_void(ls, allow_ref, EINA_FALSE); if (!_type_is_ownable(def)) { eo_lexer_context_restore(ls); @@ -773,7 +798,7 @@ parse_type_void(Eo_Lexer *ls, Eina_Bool allow_ref) pcolumn = ls->column; check_next(ls, '('); eo_lexer_context_push(ls); - def = parse_type_void(ls, allow_ref); + def = parse_type_void(ls, allow_ref, EINA_FALSE); if (!_type_is_ownable(def)) { eo_lexer_context_restore(ls); @@ -803,12 +828,61 @@ parse_type_void(Eo_Lexer *ls, Eina_Bool allow_ref) def->type = EOLIAN_TYPE_UNDEFINED; eo_lexer_get(ls); } + else if (ls->t.kw == KW_static_array) + { + if (!allow_sarray) + eo_lexer_syntax_error(ls, "static arrays not allowed in this context"); + def->type = EOLIAN_TYPE_STATIC_ARRAY; + eo_lexer_get(ls); + check_next(ls, '<'); + def->base_type = parse_type(ls, EINA_FALSE, EINA_FALSE); + pop_type(ls); + check_next(ls, ','); + check(ls, TOK_NUMBER); + eo_lexer_context_push(ls); + if (ls->t.kw == NUM_FLOAT || ls->t.kw == NUM_DOUBLE) + { + eo_lexer_context_restore(ls); + eo_lexer_syntax_error(ls, "integer expected"); + } + eo_lexer_context_pop(ls); + switch (ls->t.kw) + { + case NUM_INT : def->static_size = ls->t.value.i; break; + case NUM_UINT : def->static_size = ls->t.value.u; break; + case NUM_LONG : def->static_size = ls->t.value.l; break; + case NUM_ULONG : def->static_size = ls->t.value.ul; break; + case NUM_LLONG : def->static_size = ls->t.value.ll; break; + case NUM_ULLONG: def->static_size = ls->t.value.ull; break; + default: + eo_lexer_syntax_error(ls, "wrong type, internal error"); + break; + } + eo_lexer_get(ls); + check_next(ls, '>'); + } + else if (ls->t.kw == KW_terminated_array) + { + def->type = EOLIAN_TYPE_TERMINATED_ARRAY; + eo_lexer_get(ls); + check_next(ls, '<'); + eo_lexer_context_push(ls); + def->base_type = parse_type(ls, EINA_FALSE, EINA_FALSE); + if (!_type_is_terminatable(def->base_type)) + { + eo_lexer_context_restore(ls); + eo_lexer_syntax_error(ls, "terminatable type expected"); + } + eo_lexer_context_pop(ls); + pop_type(ls); + check_next(ls, '>'); + } else { int tpid = ls->t.kw; def->type = EOLIAN_TYPE_REGULAR; check(ls, TOK_VALUE); - if (eo_lexer_get_c_type(ls->t.kw)) + if (eo_lexer_is_type_keyword(ls->t.kw)) { _fill_name(eina_stringshare_ref(ls->t.value.s), &def->full_name, &def->name, &def->namespaces); @@ -818,17 +892,17 @@ parse_type_void(Eo_Lexer *ls, Eina_Bool allow_ref) int bline = ls->line_number, bcol = ls->column; def->type = EOLIAN_TYPE_COMPLEX; check_next(ls, '<'); - def->base_type = parse_type(ls, EINA_FALSE); + def->base_type = parse_type(ls, EINA_FALSE, EINA_FALSE); pop_type(ls); if (tpid == KW_hash) { check_next(ls, ','); - def->base_type->next_type = parse_type(ls, EINA_FALSE); + def->base_type->next_type = parse_type(ls, EINA_FALSE, EINA_FALSE); pop_type(ls); } else if(tpid == KW_promise && test_next(ls, ',')) { - def->base_type->next_type = parse_type(ls, EINA_FALSE); + def->base_type->next_type = parse_type(ls, EINA_FALSE, EINA_FALSE); pop_type(ls); } check_match(ls, '>', '<', bline, bcol); @@ -920,7 +994,7 @@ parse_typedef(Eo_Lexer *ls) } eo_lexer_context_pop(ls); check_next(ls, ':'); - def->base_type = parse_type(ls, EINA_FALSE); + def->base_type = parse_type(ls, EINA_FALSE, EINA_FALSE); pop_type(ls); check_next(ls, ';'); FILL_DOC(ls, def, doc); @@ -959,7 +1033,7 @@ parse_variable(Eo_Lexer *ls, Eina_Bool global) } eo_lexer_context_pop(ls); check_next(ls, ':'); - def->base_type = parse_type(ls, EINA_FALSE); + def->base_type = parse_type(ls, EINA_FALSE, EINA_FALSE); pop_type(ls); if ((ls->t.token == '=') && !has_extern) { @@ -988,9 +1062,9 @@ parse_return(Eo_Lexer *ls, Eo_Ret_Def *ret, Eina_Bool allow_void) eo_lexer_get(ls); check_next(ls, ':'); if (allow_void) - ret->type = parse_type_void(ls, EINA_TRUE); + ret->type = parse_type_void(ls, EINA_TRUE, EINA_FALSE); else - ret->type = parse_type(ls, EINA_TRUE); + ret->type = parse_type(ls, EINA_TRUE, EINA_FALSE); ret->doc = NULL; ret->default_ret_val = NULL; ret->warn_unused = EINA_FALSE; @@ -1049,9 +1123,9 @@ parse_param(Eo_Lexer *ls, Eina_List **params, Eina_Bool allow_inout, eo_lexer_get(ls); check_next(ls, ':'); if (par->param_dir == EOLIAN_OUT_PARAM || par->param_dir == EOLIAN_INOUT_PARAM) - par->type = parse_type_void(ls, EINA_TRUE); + par->type = parse_type_void(ls, EINA_TRUE, EINA_FALSE); else - par->type = parse_type(ls, EINA_TRUE); + par->type = parse_type(ls, EINA_TRUE, EINA_FALSE); pop_type(ls); if ((is_vals || (par->param_dir == EOLIAN_OUT_PARAM)) && (ls->t.token == '(')) { @@ -1649,7 +1723,7 @@ end: if (ls->t.token == ':') { eo_lexer_get(ls); - ev->type = parse_type(ls, EINA_TRUE); + ev->type = parse_type(ls, EINA_TRUE, EINA_FALSE); pop_type(ls); } check(ls, ';'); diff --git a/src/lib/eolian/eolian_database.h b/src/lib/eolian/eolian_database.h index 874862bf00..9becd5af76 100644 --- a/src/lib/eolian/eolian_database.h +++ b/src/lib/eolian/eolian_database.h @@ -173,6 +173,7 @@ struct _Eolian_Type Eina_Stringshare *full_name; Eina_List *namespaces; Eina_Stringshare *freefunc; + size_t static_size; Eina_Bool is_const :1; Eina_Bool is_own :1; Eina_Bool is_ref :1; diff --git a/src/tests/eolian/data/struct.eo b/src/tests/eolian/data/struct.eo index 077e499591..81429b9234 100644 --- a/src/tests/eolian/data/struct.eo +++ b/src/tests/eolian/data/struct.eo @@ -1,6 +1,8 @@ struct Named { field: ref(int); something: string; + arr: static_array; + tarr: terminated_array; } struct Another { diff --git a/src/tests/eolian/data/struct_ref.c b/src/tests/eolian/data/struct_ref.c index 1fbec777bc..dd9c64e375 100644 --- a/src/tests/eolian/data/struct_ref.c +++ b/src/tests/eolian/data/struct_ref.c @@ -15,6 +15,8 @@ typedef struct _Named { int *field; const char *something; + int arr[16]; + const char **tarr; } Named; typedef struct _Another diff --git a/src/tests/eolian/eolian_parsing.c b/src/tests/eolian/eolian_parsing.c index 607849307b..2a880dac02 100644 --- a/src/tests/eolian/eolian_parsing.c +++ b/src/tests/eolian/eolian_parsing.c @@ -692,6 +692,21 @@ START_TEST(eolian_struct) fail_if(!(type_name = eolian_type_c_type_get(ftype))); fail_if(strcmp(type_name, "const char *")); eina_stringshare_del(type_name); + fail_if(!(field = eolian_typedecl_struct_field_get(tdl, "arr"))); + fail_if(!(ftype = eolian_typedecl_struct_field_type_get(field))); + fail_if(eolian_type_is_ref(ftype)); + fail_if(eolian_type_array_size_get(ftype) != 16); + fail_if(eolian_type_type_get(ftype) != EOLIAN_TYPE_STATIC_ARRAY); + fail_if(!(type_name = eolian_type_c_type_get(ftype))); + fail_if(strcmp(type_name, "int *")); + eina_stringshare_del(type_name); + fail_if(!(field = eolian_typedecl_struct_field_get(tdl, "tarr"))); + fail_if(!(ftype = eolian_typedecl_struct_field_type_get(field))); + fail_if(eolian_type_is_ref(ftype)); + fail_if(!(type_name = eolian_type_c_type_get(ftype))); + fail_if(eolian_type_type_get(ftype) != EOLIAN_TYPE_TERMINATED_ARRAY); + fail_if(strcmp(type_name, "const char **")); + eina_stringshare_del(type_name); /* referencing */ fail_if(!(tdl = eolian_typedecl_struct_get_by_name("Another")));