efl/src/bin/edje/edje_cc_script.c

493 lines
12 KiB
C

#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 || code->parsed) return;
id = eina_list_data_idx(codes, code);
pcp = eina_list_nth(edje_collections, id);
EINA_LIST_FOREACH(pcp->base_codes, l, base)
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 = NULL;
Token *token, *tmp = NULL;
char *begin = code->shared;
char *end = begin + strlen(begin);
char *body;
Eina_Array *name_stack;
Eina_Bool is_args = EINA_FALSE;
Eina_Bool is_public = EINA_FALSE;
int depth = 0;
name_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))
{
if (token->str) free(token->str);
free(token);
token = tmp;
tmp = NULL;
break;
}
if (tmp->str) free(tmp->str);
free(tmp);
}
}
switch (token->type)
{
case TOKEN_TYPE_COLON:
if (!sym)
sym = mem_alloc(SZ(Code_Symbol));
sym->tag = eina_array_pop(name_stack);
break;
case TOKEN_TYPE_SEMICOLON:
if (eina_array_count(name_stack))
{
if (!sym)
sym = mem_alloc(SZ(Code_Symbol));
sym->name = eina_array_pop(name_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(name_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(name_stack);
}
else
{
if (eina_array_count(name_stack))
{
if (!sym)
sym = mem_alloc(SZ(Code_Symbol));
sym->name = eina_array_pop(name_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 (tmp->str) free(tmp->str);
free(tmp);
}
if (!sym) 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(name_stack, token->str);
token->str = NULL;
break;
default:
break;
}
if (token->str)
free(token->str);
free(token);
if (tmp)
{
if (tmp->str) free(tmp->str);
free(tmp);
tmp = NULL;
}
}
if (token)
{
if (token->str)
free(token->str);
free(token);
}
eina_array_free(name_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");
}
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(code->vars);
eina_list_free(code->func);
code->vars = vars;
code->func = func;
}