edje_cc: support script inheritance
authorJeeyong Um <conr2d@gmail.com>
Fri, 27 Oct 2017 18:49:02 +0000 (11:49 -0700)
committerCedric Bail <cedric@osg.samsung.com>
Fri, 27 Oct 2017 20:34:01 +0000 (13:34 -0700)
Summary:
When inherit_script is set to 1, script of current group contains
variables and funtions from script of parent groups. If there is same
name variable or function, newly defined one will replace that of
parents.

Reviewers: cedric, jpeg

Subscribers: taxi2se

Differential Revision: https://phab.enlightenment.org/D5062

Signed-off-by: Cedric Bail <cedric@osg.samsung.com>
src/Makefile_Edje.am
src/bin/edje/edje_cc.h
src/bin/edje/edje_cc_handlers.c
src/bin/edje/edje_cc_script.c [new file with mode: 0644]

index 6b708a3..b4299f1 100644 (file)
@@ -176,7 +176,9 @@ bin/edje/edje_cc_parse.c \
 bin/edje/edje_cc_mem.c \
 bin/edje/edje_cc_handlers.c \
 bin/edje/edje_cc_sources.c \
+bin/edje/edje_cc_script.c \
 bin/edje/edje_multisense_convert.c
+
 bin_edje_edje_cc_CPPFLAGS = -I$(top_builddir)/src/lib/efl $(EDJE_COMMON_CPPFLAGS) @EDJE_LUA_CFLAGS@
 bin_edje_edje_cc_LDADD =  $(USE_EDJE_BIN_LIBS) @EDJE_LUA_LIBS@
 bin_edje_edje_cc_DEPENDENCIES = \
index 9e3c9a8..55c151b 100644 (file)
@@ -91,7 +91,10 @@ struct _Code
    char      *shared;
    char      *original;
    Eina_List *programs;
-   int         is_lua;
+   Eina_List *vars;
+   Eina_List *func;
+   Eina_Bool  parsed : 1;
+   Eina_Bool  is_lua : 1;
 };
 
 struct _Code_Program
@@ -160,8 +163,10 @@ struct _Edje_Part_Collection_Parser
    Eina_List *target_groups;
    Eina_List *links;
    Eina_Hash *link_hash;
+   Eina_List *base_codes;
    Eina_Bool default_mouse_events;
    Eina_Bool inherit_only;
+   Eina_Bool inherit_script : 1;
 };
 
 typedef enum
@@ -297,6 +302,8 @@ int get_param_index(char *str);
 void color_tree_root_free(void);
 void convert_color_code(char *str, int *r, int *g, int *b, int *a);
 
+void script_rewrite(Code *code);
+
 /* global vars */
 extern Eina_List             *ext_dirs;
 extern Eina_List             *img_dirs;
index 909b0e1..3f83283 100644 (file)
@@ -163,7 +163,7 @@ static Edje_Part_Description_Common *parent_desc = NULL;
 static Edje_Program *current_program = NULL;
 static Eina_List *current_program_lookups = NULL;
 Eina_Bool current_group_inherit = EINA_FALSE;
-Eina_Bool script_override = EINA_FALSE;
+Eina_Bool script_is_replaceable = EINA_FALSE;
 static Edje_Program *sequencing = NULL;
 static Eina_List *sequencing_lookups = NULL;
 static int *anonymous_delete = NULL;
@@ -268,6 +268,7 @@ static void st_collections_group_data_item(void);
 static void st_collections_group_orientation(void);
 static void st_collections_group_mouse_events(void);
 static void st_collections_group_use_custom_seat_names(void);
+static void st_collections_group_inherit_script(void);
 
 static void st_collections_group_limits_vertical(void);
 static void st_collections_group_limits_horizontal(void);
@@ -550,6 +551,7 @@ static void st_collections_group_mouse(void);
 static void st_collections_group_nomouse(void);
 static void st_collections_group_broadcast(void);
 static void st_collections_group_nobroadcast(void);
+static void st_collections_group_noinherit_script(void);
 
 static void st_images_vector(void);
 static void _handle_vector_image(void);
@@ -746,6 +748,7 @@ New_Statement_Handler statement_handlers[] =
      {"collections.group.broadcast_signal", st_collections_group_broadcast_signal},
      {"collections.group.orientation", st_collections_group_orientation},
      {"collections.group.mouse_events", st_collections_group_mouse_events},
+     {"collections.group.inherit_script", st_collections_group_inherit_script},
      {"collections.group.data.item", st_collections_group_data_item},
      {"collections.group.limits.horizontal", st_collections_group_limits_horizontal},
      {"collections.group.limits.vertical", st_collections_group_limits_vertical},
@@ -1143,6 +1146,8 @@ New_Statement_Handler statement_handlers_short[] =
        nobroadcast; -> broadcast_signal: 0;
        mouse; -> mouse_events: 1;
        nomouse; -> mouse_events: 0;
+       inherit_script; -> inherit_script: 1;
+       noinherit_script; -> inherit_script: 0;
        parts {
           part {
              mouse; -> mouse_events: 1;
@@ -1191,6 +1196,8 @@ New_Statement_Handler statement_handlers_short_single[] =
      {"collections.group.nomouse", st_collections_group_nomouse},
      {"collections.group.broadcast", st_collections_group_broadcast},
      {"collections.group.nobroadcast", st_collections_group_nobroadcast},
+     {"collections.group.inherit_script", st_collections_group_inherit_script},
+     {"collections.group.noinherit_script", st_collections_group_noinherit_script},
      {"collections.group.parts.part.description.inherit", st_collections_group_parts_part_description_inherit},
 };
 
@@ -4238,7 +4245,7 @@ ob_collections_group(void)
    current_desc = NULL;
 
    current_group_inherit = EINA_FALSE;
-   script_override = EINA_FALSE;
+   script_is_replaceable = EINA_FALSE;
 
    current_de = mem_alloc(SZ(Edje_Part_Collection_Directory_Entry));
    current_de->id = eina_list_count(edje_collections);
@@ -4257,6 +4264,7 @@ ob_collections_group(void)
 
    pcp = (Edje_Part_Collection_Parser *)pc;
    pcp->default_mouse_events = 1;
+   pcp->inherit_script = EINA_FALSE;
 
    pc->scene_size.width = 0;
    pc->scene_size.height = 0;
@@ -4835,6 +4843,7 @@ st_collections_group_inherit(void)
    pcp = (Edje_Part_Collection_Parser *)pc;
    pcp2 = (Edje_Part_Collection_Parser *)pc2;
    pcp->default_mouse_events = pcp2->default_mouse_events;
+   pcp->inherit_script = pcp2->inherit_script;
 
    /* as of 7 April 2014, target groups cannot be modified and are not freed.
     * this code will break if that ever changes.
@@ -4961,6 +4970,9 @@ st_collections_group_inherit(void)
    cd = eina_list_data_get(eina_list_last(codes));
 
    cd->is_lua = cd2->is_lua;
+   if (!cd2->is_lua)
+     pcp->base_codes = eina_list_append(pcp->base_codes, cd2);
+
    if (cd2->shared)
      {
         if (cd->shared)
@@ -4975,7 +4987,7 @@ st_collections_group_inherit(void)
         cd->shared = STRDUP(cd2->shared);
         cd->original = STRDUP(cd2->original);
 
-        script_override = EINA_TRUE;
+        script_is_replaceable = EINA_TRUE;
      }
 
    EINA_LIST_FOREACH(cd2->programs, l, cp2)
@@ -5271,6 +5283,76 @@ st_collections_group_nomouse(void)
 /**
     @page edcref
     @property
+        inherit_script
+    @parameters
+        [1 or 0]
+    @effect
+        Determine whether to inherit script block from parent group.
+        If it is set to 0, script from parent group will be replaced with
+        new script block.
+        Defaults to 0 if not set, to maintain compatibility.
+    @endproperty
+ */
+static void
+st_collections_group_inherit_script(void)
+{
+   Edje_Part_Collection_Parser *pcp;
+
+   pcp = eina_list_last_data_get(edje_collections);
+
+   if (get_arg_count() == 1)
+     pcp->inherit_script = parse_bool(0);
+   else
+     pcp->inherit_script = EINA_TRUE;
+}
+
+static void
+st_collections_group_noinherit_script(void)
+{
+   Edje_Part_Collection_Parser *pcp;
+
+   check_arg_count(0);
+
+   pcp = eina_list_last_data_get(edje_collections);
+   pcp->inherit_script = EINA_FALSE;
+}
+
+static void
+_script_flush(void)
+{
+   Edje_Part_Collection_Parser *pcp;
+   Code *code;
+
+   pcp = eina_list_last_data_get(edje_collections);
+   code = eina_list_last_data_get(codes);
+
+   if (!pcp->inherit_script || code->is_lua) return;
+
+   // If script is replaceable and overridable, code->shared will be inherited
+   // script. Free it to avoid duplication.
+   if (script_is_replaceable)
+     {
+        if (code->shared)
+          {
+             free(code->shared);
+             code->shared = NULL;
+          }
+        if (code->original)
+          {
+             free(code->original);
+             code->original = NULL;
+          }
+     }
+
+   script_rewrite(code);
+
+   eina_list_free(pcp->base_codes);
+}
+
+
+/**
+    @page edcref
+    @property
         program_source
     @parameters
         [source name]
@@ -5327,10 +5409,8 @@ st_collections_group_program_source(void)
 static void
 ob_collections_group_script(void)
 {
-   Edje_Part_Collection *pc;
    Code *cd;
 
-   pc = eina_list_last_data_get(edje_collections);
    cd = eina_list_data_get(eina_list_last(codes));
 
    if (!is_verbatim()) track_verbatim(1);
@@ -5345,14 +5425,11 @@ ob_collections_group_script(void)
             cd->l2 = get_verbatim_line2();
             if (cd->shared)
               {
-                  if (script_override)
+                  if (script_is_replaceable)
                     {
                        free(cd->shared);
                        free(cd->original);
-                       script_override = EINA_FALSE;
-
-                       WRN("%s:%i. Inherited script block in group \"%s\" is redefined. "
-                           "This can break inherited edje programs.", file_in, line - 1, pc->part);
+                       script_is_replaceable = EINA_FALSE;
                     }
                   else
                     {
@@ -15825,7 +15902,10 @@ edje_cc_handlers_pop_notify(const char *token)
    else if (current_program && (!strcmp(token, "link")))
      current_program = NULL;
    else if (current_de && (!strcmp(token, "group")))
-     _link_combine();
+     {
+        _link_combine();
+        _script_flush();
+     }
    else if (current_desc && (!strcmp(token, "description")))
      free_anchors();
 }
diff --git a/src/bin/edje/edje_cc_script.c b/src/bin/edje/edje_cc_script.c
new file mode 100644 (file)
index 0000000..60618f8
--- /dev/null
@@ -0,0 +1,465 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "edje_cc.h"
+
+#define MESSAGE_OVERRIDE
+
+typedef struct _Code_Symbol
+{
+   const char *name;
+   const char *tag;
+   Eina_List  *args;
+   char       *body;
+   Eina_Bool   is_public : 1;
+} Code_Symbol;
+
+typedef enum
+{
+   TOKEN_TYPE_INVALID = -1,
+   TOKEN_TYPE_EOF,
+   TOKEN_TYPE_COLON      = (1 << 0),
+   TOKEN_TYPE_SEMICOLON  = (1 << 1),
+   TOKEN_TYPE_COMMA      = (1 << 2),
+   TOKEN_TYPE_PARENS     = (1 << 3),
+   TOKEN_TYPE_BRACES     = (1 << 4),
+   TOKEN_TYPE_EQUAL_MARK = (1 << 5),
+   TOKEN_TYPE_PUBLIC     = (1 << 6),
+   TOKEN_TYPE_IDENTIFIER = (1 << 7)
+} Token_Type;
+
+typedef struct _Token
+{
+   char *str;
+   Token_Type type;
+} Token;
+
+static void   code_parse_internal(Code *code);
+static Token *next_token(char **begin, char *end);
+
+static void
+code_parse(Code *code)
+{
+   Edje_Part_Collection_Parser *pcp;
+   Code *base;
+   Eina_List *l;
+   int id;
+
+   if (code->is_lua) return;
+
+   id = eina_list_data_idx(codes, code);
+   pcp = eina_list_nth(edje_collections, id);
+
+   EINA_LIST_FOREACH(pcp->base_codes, l, base)
+     {
+        if (!base->parsed)
+          code_parse(base);
+     }
+
+   if (code->shared)
+     code_parse_internal(code);
+
+   code->parsed = EINA_TRUE;
+}
+
+static void
+code_parse_internal(Code *code)
+{
+   Code_Symbol *sym = NULL, *func;
+   Token *token, *tmp;
+   char *begin = code->shared;
+   char *end = begin + strlen(begin);
+   char *body;
+   Eina_Array *stack;
+   Eina_Bool is_args = EINA_FALSE;
+   Eina_Bool is_public = EINA_FALSE;
+   int depth = 0;
+
+   stack = eina_array_new(4);
+
+   while ((token = next_token(&begin, end)))
+     {
+        if (token->type == TOKEN_TYPE_EOF)
+          break;
+
+        // Variables in script cannot be initialized by assignment.
+        // Skip until value assignment expression ends.
+        if (token->type == TOKEN_TYPE_EQUAL_MARK)
+          {
+             while ((tmp = next_token(&begin, end)))
+               {
+                  if ((tmp->type == TOKEN_TYPE_COMMA) ||
+                      (tmp->type == TOKEN_TYPE_SEMICOLON))
+                    {
+                       token = tmp;
+                       break;
+                    }
+               }
+          }
+
+        switch (token->type)
+          {
+           case TOKEN_TYPE_COLON:
+             if (!sym)
+               sym = mem_alloc(SZ(Code_Symbol));
+             sym->tag = eina_array_pop(stack);
+             break;
+           case TOKEN_TYPE_SEMICOLON:
+             if (eina_array_count(stack))
+               {
+                  if (!sym)
+                    sym = mem_alloc(SZ(Code_Symbol));
+                  sym->name = eina_array_pop(stack);
+                  sym->is_public = is_public;
+                  code->vars = eina_list_append(code->vars, sym);
+                  sym = NULL;
+               }
+             is_public = EINA_FALSE;
+             break;
+           case TOKEN_TYPE_COMMA:
+             if (!sym)
+               sym = mem_alloc(SZ(Code_Symbol));
+             sym->name = eina_array_pop(stack);
+             if (is_args)
+               func->args = eina_list_append(func->args, sym);
+             else
+               {
+                  sym->is_public = is_public;
+                  code->vars = eina_list_append(code->vars, sym);
+               }
+             sym = NULL;
+             break;
+           case TOKEN_TYPE_PARENS:
+             is_args = !is_args;
+             if (is_args)
+               {
+                  if (!sym)
+                    func = mem_alloc(SZ(Code_Symbol));
+                  else
+                    {
+                       func = sym;
+                       sym = NULL;
+                    }
+                  func->name = eina_array_pop(stack);
+               }
+             else
+               {
+                  if (eina_array_count(stack))
+                    {
+                       if (!sym)
+                         sym = mem_alloc(SZ(Code_Symbol));
+                       sym->name = eina_array_pop(stack);
+                       func->args = eina_list_append(func->args, sym);
+                    }
+                  sym = func;
+               }
+             break;
+           case TOKEN_TYPE_BRACES:
+             depth = 1;
+             body = begin;
+             while ((tmp = next_token(&begin, end)))
+               {
+                  if (tmp->type == TOKEN_TYPE_BRACES)
+                    {
+                       switch (tmp->str[0])
+                         {
+                          case '{':
+                            depth++;
+                            break;
+                          case '}':
+                            depth--;
+                            break;
+                         }
+                    }
+                  if (!depth)
+                    break;
+               }
+             if ((begin - 1) > body)
+               {
+                  sym->body = mem_alloc(sizeof(char) * (begin - body - 1));
+                  strncpy(sym->body, body, (begin - body - 2));
+               }
+             sym->is_public = is_public;
+             code->func = eina_list_append(code->func, sym);
+             sym = NULL;
+             is_public = EINA_FALSE;
+             break;
+           case TOKEN_TYPE_PUBLIC:
+             is_public = EINA_TRUE;
+             break;
+           case TOKEN_TYPE_IDENTIFIER:
+             eina_array_push(stack, token->str);
+             token->str = NULL;
+             break;
+           default:
+             break;
+          }
+
+        if (token->str)
+          free(token->str);
+        free(token);
+     }
+
+   eina_array_free(stack);
+}
+
+static Token *
+next_token(char **begin, char *end)
+{
+   char buf[PATH_MAX] = { 0, };
+   char *src;
+   int index;
+   Token *token;
+   Eina_Bool parsed = EINA_FALSE;
+
+   if (!begin || (*begin >= end))
+     return NULL;
+
+   token = mem_alloc(SZ(Token));
+   token->type = TOKEN_TYPE_INVALID;
+
+   src = *begin - 1;
+   index = 0;
+
+   while (++src < end)
+     {
+        char ch = *src;
+
+        switch (ch)
+          {
+           case ' ':
+           case '\t':
+           case '\n':
+              if (index > 0)
+                parsed = EINA_TRUE;
+              break;
+           case ':':
+           case ';':
+           case ',':
+           case '(':
+           case ')':
+           case '{':
+           case '}':
+           case '=':
+              if (!index)
+                {
+                   buf[index++] = ch;
+                   src++;
+                }
+              goto exit;
+           default:
+              if (parsed)
+                goto exit;
+              buf[index++] = ch;
+              break;
+          }
+     }
+
+exit:
+   switch (buf[0])
+     {
+       case ':':
+         token->type = TOKEN_TYPE_COLON;
+         break;
+       case ';':
+         token->type = TOKEN_TYPE_SEMICOLON;
+         break;
+       case ',':
+         token->type = TOKEN_TYPE_COMMA;
+         break;
+       case '(':
+       case ')':
+         token->type = TOKEN_TYPE_PARENS;
+         break;
+       case '{':
+       case '}':
+         token->type = TOKEN_TYPE_BRACES;
+         break;
+       case '=':
+         token->type = TOKEN_TYPE_EQUAL_MARK;
+         break;
+       case '\0':
+         token->type = TOKEN_TYPE_EOF;
+         break;
+     }
+
+   if (token->type < 0)
+     {
+        if (!strcmp(buf, "public"))
+          token->type = TOKEN_TYPE_PUBLIC;
+        else
+          token->type = TOKEN_TYPE_IDENTIFIER;
+     }
+
+   *begin = src;
+   token->str = strdup(buf);
+   return token;
+}
+
+static void
+_push_symbol(Eina_List **total, Code_Symbol *sym, Edje_Part_Collection *pc)
+{
+   Eina_List *list, *l;
+   Code_Symbol *sym2;
+
+   list = *total;
+
+   EINA_LIST_FOREACH(list, l, sym2)
+     {
+        if (!strcmp(sym2->name, sym->name))
+          {
+             WRN("Symbols in group \"%s\" have same name \"%s\". Latter defined "
+                 "will shadow former one.", pc->part, sym->name);
+             list = eina_list_remove(list, sym2);
+             break;
+          }
+     }
+   list = eina_list_append(list, sym);
+   *total = list;
+}
+
+void
+script_rewrite(Code *code)
+{
+   Edje_Part_Collection *pc;
+   Edje_Part_Collection_Parser *pcp;
+   Code *base;
+   Eina_List *l, *ll;
+   int id, count;
+   Eina_Strbuf *buf;
+   Eina_List *vars = NULL;
+   Eina_List *func = NULL;
+#ifdef MESSAGE_OVERRIDE
+   Eina_List *message = NULL;
+#endif
+   Code_Symbol *sym, *arg;
+
+   code_parse(code);
+
+   id = eina_list_data_idx(codes, code);
+   pc = eina_list_nth(edje_collections, id);
+   pcp = (Edje_Part_Collection_Parser *)pc;
+
+   EINA_LIST_FOREACH(pcp->base_codes, l, base)
+     {
+        EINA_LIST_FOREACH(base->vars, ll, sym)
+          _push_symbol(&vars, sym, pc);
+
+        EINA_LIST_FOREACH(base->func, ll, sym)
+          {
+#ifndef MESSAGE_OVERRIDE
+             _push_symbol(&func, sym, pc);
+#else
+             if (strcmp(sym->name, "message"))
+               _push_symbol(&func, sym, pc);
+             else
+               message = eina_list_append(message, sym);
+#endif
+          }
+     }
+
+   EINA_LIST_FOREACH(code->vars, l, sym)
+     _push_symbol(&vars, sym, pc);
+   EINA_LIST_FOREACH(code->func, l, sym)
+     {
+#ifndef MESSAGE_OVERRIDE
+        _push_symbol(&func, sym, pc);
+#else
+        if (strcmp(sym->name, "message"))
+          _push_symbol(&func, sym, pc);
+        else
+          message = eina_list_append(message, sym);
+#endif
+     }
+
+   buf = eina_strbuf_new();
+
+   if (vars)
+     {
+        count = 0;
+        EINA_LIST_FOREACH(vars, l, sym)
+          {
+             if (!sym->is_public) continue;
+
+             if (count++)
+               eina_strbuf_append(buf, ", ");
+             else
+               eina_strbuf_append(buf, "public ");
+
+             if (sym->tag)
+               eina_strbuf_append_printf(buf, "%s:", sym->tag);
+             eina_strbuf_append(buf, sym->name);
+          }
+        if (count)
+          eina_strbuf_append(buf, ";\n");
+
+        count = 0;
+        EINA_LIST_FOREACH(vars, l, sym)
+          {
+             if (sym->is_public) continue;
+
+             if (count++)
+               eina_strbuf_append(buf, ", ");
+
+             if (sym->tag)
+               eina_strbuf_append_printf(buf, "%s:", sym->tag);
+             eina_strbuf_append(buf, sym->name);
+          }
+        if (count)
+          eina_strbuf_append(buf, ";\n");
+     }
+
+   if (func)
+     {
+        EINA_LIST_FOREACH(func, l, sym)
+          {
+             eina_strbuf_append(buf, "\n");
+             if (sym->is_public)
+               eina_strbuf_append(buf, "public ");
+
+             if (sym->tag)
+               eina_strbuf_append_printf(buf, "%s:", sym->tag);
+             eina_strbuf_append_printf(buf, "%s(", sym->name);
+
+             count = 0;
+             EINA_LIST_FOREACH(sym->args, ll, arg)
+               {
+                  if (count++)
+                    eina_strbuf_append(buf, ", ");
+
+                  if (arg->tag)
+                    eina_strbuf_append_printf(buf, "%s:", arg->tag);
+                  eina_strbuf_append(buf, arg->name);
+               }
+             eina_strbuf_append(buf, ") {");
+             if (sym->body)
+               {
+                  eina_strbuf_append(buf, sym->body);
+                  eina_strbuf_rtrim(buf);
+               }
+             eina_strbuf_append(buf, "\n}\n");
+          }
+     }
+
+#ifdef MESSAGE_OVERRIDE
+   if (message)
+     {
+        eina_strbuf_append(buf, "\npublic message(Msg_Type:type, id, ...) {");
+        EINA_LIST_FOREACH(message, l, sym)
+          {
+             eina_strbuf_append(buf, sym->body);
+             eina_strbuf_rtrim(buf);
+             eina_strbuf_append(buf, "\n");
+          }
+        eina_strbuf_append(buf, "}\n");
+     }
+#endif
+
+   code->shared = eina_strbuf_string_steal(buf);
+   code->original = strdup(code->shared);
+   eina_strbuf_free(buf);
+
+   eina_list_free(vars);
+   eina_list_free(func);
+}