Mercurial > audlegacy
changeset 3409:86dafe2300f7 trunk
Added Tuplez compiler (not used yet, though) and some related changes in
tuple code.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Thu, 30 Aug 2007 23:41:33 +0300 |
parents | aea3349e2c62 |
children | e251841884fc |
files | src/audacious/Makefile src/audacious/tuple.c src/audacious/tuple.h src/audacious/tuple_compiler.c src/audacious/tuple_compiler.h src/audacious/tuple_formatter.c |
diffstat | 6 files changed, 888 insertions(+), 25 deletions(-) [+] |
line wrap: on
line diff
--- a/src/audacious/Makefile Thu Aug 30 17:41:40 2007 +0200 +++ b/src/audacious/Makefile Thu Aug 30 23:41:33 2007 +0300 @@ -56,6 +56,7 @@ strings.h \ tuple.h \ tuple_formatter.h \ + tuple_compiler.h \ ui_fileinfopopup.h \ ui_lastfm.h\ ui_plugin_menu.h \ @@ -103,6 +104,7 @@ strings.c \ tuple.c \ tuple_formatter.c \ + tuple_compiler.c \ skin.c \ ui_about.c \ ui_albumart.c \
--- a/src/audacious/tuple.c Thu Aug 30 17:41:40 2007 +0200 +++ b/src/audacious/tuple.c Thu Aug 30 23:41:33 2007 +0300 @@ -23,19 +23,6 @@ #include "tuple.h" -struct _Tuple { - mowgli_object_t parent; - mowgli_dictionary_t *dict; -}; - -typedef struct { - TupleValueType type; - union { - gchar *string; - gint integer; - } value; -} TupleValue; - static mowgli_heap_t *tuple_heap = NULL; static mowgli_heap_t *tuple_value_heap = NULL; static mowgli_object_class_t tuple_klass; @@ -124,8 +111,8 @@ g_return_val_if_fail(tuple != NULL, FALSE); g_return_val_if_fail(field != NULL, FALSE); - if (mowgli_dictionary_find(tuple->dict, field)) - tuple_disassociate(tuple, field); + if ((value = mowgli_dictionary_delete(tuple->dict, field))) + tuple_disassociate_now(value); if (string == NULL) return TRUE; @@ -147,8 +134,8 @@ g_return_val_if_fail(tuple != NULL, FALSE); g_return_val_if_fail(field != NULL, FALSE); - if (mowgli_dictionary_find(tuple->dict, field)) - tuple_disassociate(tuple, field); + if ((value = mowgli_dictionary_delete(tuple->dict, field))) + tuple_disassociate_now(value); value = mowgli_heap_alloc(tuple_value_heap); value->type = TUPLE_INT; @@ -160,6 +147,15 @@ } void +tuple_disassociate_now(TupleValue *value) +{ + if (value->type == TUPLE_STRING) + g_free(value->value.string); + + mowgli_heap_free(tuple_value_heap, value); +} + +void tuple_disassociate(Tuple *tuple, const gchar *field) { TupleValue *value; @@ -170,11 +166,8 @@ /* why _delete()? because _delete() returns the dictnode's data on success */ if ((value = mowgli_dictionary_delete(tuple->dict, field)) == NULL) return; - - if (value->type == TUPLE_STRING) - g_free(value->value.string); - - mowgli_heap_free(tuple_value_heap, value); + + tuple_disassociate_now(value); } TupleValueType
--- a/src/audacious/tuple.h Thu Aug 30 17:41:40 2007 +0200 +++ b/src/audacious/tuple.h Thu Aug 30 23:41:33 2007 +0300 @@ -24,8 +24,11 @@ #include <glib.h> #include <mowgli.h> -struct _Tuple; -typedef struct _Tuple Tuple; +typedef struct _Tuple { + mowgli_object_t parent; + mowgli_dictionary_t *dict; +} Tuple; + typedef enum { TUPLE_STRING, @@ -33,11 +36,20 @@ TUPLE_UNKNOWN } TupleValueType; +typedef struct { + TupleValueType type; + union { + gchar *string; + gint integer; + } value; +} TupleValue; + Tuple *tuple_new(void); Tuple *tuple_new_from_filename(const gchar *filename); gboolean tuple_associate_string(Tuple *tuple, const gchar *field, const gchar *string); gboolean tuple_associate_int(Tuple *tuple, const gchar *field, gint integer); void tuple_disassociate(Tuple *tuple, const gchar *field); +void tuple_disassociate_now(TupleValue *value); TupleValueType tuple_get_value_type(Tuple *tuple, const gchar *field); const gchar *tuple_get_string(Tuple *tuple, const gchar *field); int tuple_get_int(Tuple *tuple, const gchar *field);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/audacious/tuple_compiler.c Thu Aug 30 23:41:33 2007 +0300 @@ -0,0 +1,748 @@ +/* + * Audacious - Tuplez compiler + * Copyright (c) 2007 Matti 'ccr' Hämäläinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; under version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses>. + * + * The Audacious team does not consider modular code linking to + * Audacious or using our public API to be a derived work. + */ + +/* + * What's this? + * ------------ + * Nothing really. A prototype / pseudo-C for an improved Tuple formatting + * system, where the format string is "compiled" into a tree structure, + * which can then be traversed fast while "evaluating". This file does + * not represent anything but some of my (ccr) ideas for the concept. + * + * The basic ideas are: + * 1) compiled structure for faster traversing + * 2) sub-expression removal / constant elimination + * 3) indexes and/or hashes for tuple entries for faster access + * 4) avoid expensive memory re-allocation + * + * and possibly 5) caching of certain things + * + * + * TODO: + * - implement definitions (${=foo,"baz"} ${=foo,1234}) + * - implement functions + * - implement handling of external expressions + * - error handling issues? + * - evaluation context: how local variables should REALLY work? + */ + +#include "config.h" +#include <assert.h> +#include <stdarg.h> +#include "tuple_compiler.h" + + +void tuple_error(const char *fmt, ...) +{ + va_list ap; + fprintf(stderr, "compiler: "); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + exit(5); +} + + +static void tuple_evalctx_free_var(TupleEvalVar *var) +{ + assert(var != NULL); + + g_free(var->defval); + g_free(var->name); + g_free(var); +} + + +static void tuple_evalctx_free_function(TupleEvalFunc *func) +{ + assert(func != NULL); + + g_free(func->name); + g_free(func); +} + + +/* Initialize an evaluation context + */ +TupleEvalContext * tuple_evalctx_new(void) +{ + return g_new0(TupleEvalContext, 1); +} + + +/* "Reset" the evaluation context, clean up locally set variables, + * but leave globals. + */ +void tuple_evalctx_reset(TupleEvalContext *ctx) +{ + gint i; + + /* Free local variables */ + for (i = 0; i < ctx->nvariables; i++) + if (ctx->variables[i]) { + ctx->variables[i]->dictref = NULL; + + if (ctx->variables[i]->islocal) + tuple_evalctx_free_var(ctx->variables[i]); + } + +} + + +/* Free an evaluation context and associated data + */ +void tuple_evalctx_free(TupleEvalContext *ctx) +{ + gint i; + + if (!ctx) return; + + /* Deallocate variables */ + for (i = 0; i < ctx->nvariables; i++) + if (ctx->variables[i]) + tuple_evalctx_free_var(ctx->variables[i]); + + g_free(ctx->variables); + + /* Deallocate functions */ + for (i = 0; i < ctx->nfunctions; i++) + if (ctx->functions[i]) + tuple_evalctx_free_function(ctx->functions[i]); + + g_free(ctx->functions); + + /* Zero context */ + memset(ctx, 0, sizeof(TupleEvalContext)); +} + + +gint tuple_evalctx_add_var(TupleEvalContext *ctx, gchar *name, gboolean islocal, gint type) +{ + gint i; + TupleEvalVar * tmp = g_new0(TupleEvalVar, 1); + + tmp->name = g_strdup(name); + tmp->islocal = islocal; + tmp->type = type; + + /* Find a free slot */ + for (i = 0; i < ctx->nvariables; i++) + if (!ctx->variables[i]) { + ctx->variables[i] = tmp; + return i; + } + + i = ctx->nvariables; + ctx->variables = g_renew(TupleEvalVar *, ctx->variables, ctx->nvariables + MIN_ALLOC_NODES); + memset(&(ctx->variables[ctx->nvariables]), 0, MIN_ALLOC_NODES * sizeof(TupleEvalVar *)); + ctx->nvariables += MIN_ALLOC_NODES; + ctx->variables[i] = tmp; + + return i; +} + + +gint tuple_evalctx_add_function(TupleEvalContext *ctx, gchar *name) +{ + assert(ctx != NULL); + assert(name != NULL); + + return -1; +} + + +static void tuple_evalnode_insert(TupleEvalNode **nodes, TupleEvalNode *node) +{ + assert(nodes != NULL); + assert(node != NULL); + + if (*nodes) { + node->prev = (*nodes)->prev; + (*nodes)->prev->next = node; + (*nodes)->prev = node; + node->next = NULL; + } else { + *nodes = node; + node->prev = node; + node->next = NULL; + } +} + + +static TupleEvalNode *tuple_evalnode_new(void) +{ + return g_new0(TupleEvalNode, 1); +} + + +void tuple_evalnode_free(TupleEvalNode *expr) +{ + TupleEvalNode *curr = expr, *next; + + while (curr) { + next = curr->next; + + g_free(curr->text); + + if (curr->children) + tuple_evalnode_free(curr->children); + + g_free(curr); + + curr = next; + } +} + + +static TupleEvalNode *tuple_compiler_pass1(gint *level, TupleEvalContext *ctx, gchar **expression); + + +static gboolean tc_get_item(gchar **str, gchar *buf, size_t max, gchar endch, gboolean *literal, gchar *errstr, gchar *item) +{ + size_t i = 0; + gchar *s = *str, tmpendch; + assert(str != NULL); + assert(buf != NULL); + + if (*s == '"') { + if (*literal == FALSE) { + tuple_error("Literal string value not allowed in '%s'.\n", item); + return FALSE; + } + s++; + *literal = TRUE; + tmpendch = '"'; + } else { + *literal = FALSE; + tmpendch = endch; + } + + if (*literal == FALSE) { + while (*s != '\0' && *s != tmpendch && (isalnum(*s) || *s == '-') && i < (max - 1)) { + buf[i++] = *(s++); + } + + if (*s != tmpendch && *s != '}' && !isalnum(*s) && *s != '-') { + tuple_error("Invalid field '%s' in '%s'.\n", *str, item); + return FALSE; + } else if (*s != tmpendch) { + tuple_error("Expected '%c' in '%s'.\n", tmpendch, item); + return FALSE; + } + } else { + while (*s != '\0' && *s != tmpendch && i < (max - 1)) { + if (*s == '\\') s++; + buf[i++] = *(s++); + } + } + buf[i] = '\0'; + + if (*literal) { + if (*s == tmpendch) + s++; + else { + tuple_error("Expected literal string end ('%c') in '%s'.\n", tmpendch, item); + return FALSE; + } + } + + if (*s != endch) { + tuple_error("Expected '%c' after %s in '%s'\n", endch, errstr, item); + return FALSE; + } else { + *str = s; + return TRUE; + } +} + + +static gint tc_get_variable(TupleEvalContext *ctx, gchar *name, gint type) +{ + gint i; + + if (*name == '\0') return -1; + + for (i = 0; i < ctx->nvariables; i++) + if (ctx->variables[i] && !strcmp(ctx->variables[i]->name, name)) + return i; + + return tuple_evalctx_add_var(ctx, name, FALSE, type); +} + + +static gboolean tc_parse_construct1(TupleEvalContext *ctx, TupleEvalNode **res, gchar *item, gchar **c, gint *level, gint opcode) +{ + gchar tmps1[MAX_STR], tmps2[MAX_STR]; + gboolean literal1 = TRUE, literal2 = TRUE; + + (*c)++; + if (tc_get_item(c, tmps1, MAX_STR, ',', &literal1, "tag1", item)) { + (*c)++; + if (tc_get_item(c, tmps2, MAX_STR, ':', &literal2, "tag2", item)) { + TupleEvalNode *tmp = tuple_evalnode_new(); + (*c)++; + + tmp->opcode = opcode; + if ((tmp->var[0] = tc_get_variable(ctx, tmps1, literal1 ? VAR_CONST : VAR_FIELD)) < 0) { + tuple_evalnode_free(tmp); + tuple_error("Invalid variable '%s' in '%s'.\n", tmps1, item); + return FALSE; + } + if ((tmp->var[1] = tc_get_variable(ctx, tmps2, literal2 ? VAR_CONST : VAR_FIELD)) < 0) { + tuple_evalnode_free(tmp); + tuple_error("Invalid variable '%s' in '%s'.\n", tmps2, item); + return FALSE; + } + tmp->children = tuple_compiler_pass1(level, ctx, c); + tuple_evalnode_insert(res, tmp); + } else + return FALSE; + } else + return FALSE; + + return TRUE; +} + + +/* Compile format expression into TupleEvalNode tree. + * A "simple" straight compilation is sufficient in first pass, later + * passes can perform subexpression removal and other optimizations. + */ +static TupleEvalNode *tuple_compiler_pass1(gint *level, TupleEvalContext *ctx, gchar **expression) +{ + TupleEvalNode *res = NULL, *tmp = NULL; + gchar *c = *expression, *item, tmps1[MAX_STR]; + gboolean literal, end = FALSE; + assert(ctx != NULL); + assert(expression != NULL); + + (*level)++; + + while (*c != '\0' && !end) { + tmp = NULL; + if (*c == '}') { + c++; + (*level)--; + end = TRUE; + } else if (*c == '$') { + /* Expression? */ + item = c++; + if (*c == '{') { + gint opcode; + gchar *expr = ++c; + + switch (*c) { + case '?': c++; + /* Exists? */ + literal = FALSE; + if (tc_get_item(&c, tmps1, MAX_STR, ':', &literal, "tag", item)) { + c++; + tmp = tuple_evalnode_new(); + tmp->opcode = OP_EXISTS; + if ((tmp->var[0] = tc_get_variable(ctx, tmps1, VAR_FIELD)) < 0) { + tuple_error("Invalid variable '%s' in '%s'.\n", tmps1, expr); + goto ret_error; + } + tmp->children = tuple_compiler_pass1(level, ctx, &c); + tuple_evalnode_insert(&res, tmp); + } else + goto ret_error; + break; + + case '=': c++; + if (*c != '=') { + /* Definition */ + literal = FALSE; + if (tc_get_item(&c, tmps1, MAX_STR, ',', &literal, "variable", item)) { + c++; + if (*c == '"') { + /* String */ + c++; + } else if (isdigit(*c)) { + /* Integer */ + } + + tuple_error("definitions not yet supported!\n"); + goto ret_error; + } else + goto ret_error; + } else { + /* Equals? */ + if (!tc_parse_construct1(ctx, &res, item, &c, level, OP_EQUALS)) + goto ret_error; + } + break; + + case '!': c++; + if (*c != '=') goto ext_expression; + if (!tc_parse_construct1(ctx, &res, item, &c, level, OP_NOT_EQUALS)) + goto ret_error; + break; + + case '<': c++; + if (*c == '=') { + opcode = OP_LTEQ; + c++; + } else + opcode = OP_LT; + + if (!tc_parse_construct1(ctx, &res, item, &c, level, opcode)) + goto ret_error; + break; + + case '>': c++; + if (*c == '=') { + opcode = OP_GTEQ; + c++; + } else + opcode = OP_GT; + + if (!tc_parse_construct1(ctx, &res, item, &c, level, opcode)) + goto ret_error; + break; + + case '(': c++; + if (!strncmp(c, "empty)?", 7)) { + c += 7; + } else + goto ext_expression; + break; + + default: + ext_expression: + /* Get expression content */ + c = expr; + literal = FALSE; + if (tc_get_item(&c, tmps1, MAX_STR, '}', &literal, "field", item)) { + /* FIXME!! FIX ME! Check for external expressions */ + + /* I HAS A FIELD - A field. You has it. */ + tmp = tuple_evalnode_new(); + tmp->opcode = OP_FIELD; + if ((tmp->var[0] = tc_get_variable(ctx, tmps1, VAR_FIELD)) < 0) { + tuple_error("Invalid variable '%s' in '%s'.\n", tmps1, expr); + goto ret_error; + } + tuple_evalnode_insert(&res, tmp); + c++; + + } else + goto ret_error; + } + } else { + tuple_error("Expected '{', got '%c' in '%s'.\n", *c, c); + goto ret_error; + } + + } else if (*c == '%') { + /* Function? */ + item = c++; + if (*c == '{') { + size_t i = 0; + c++; + + while (*c != '\0' && (isalnum(*c) || *c == '-') && *c != '}' && *c != ':' && i < (MAX_STR - 1)) + tmps1[i++] = *(c++); + tmps1[i] = '\0'; + + if (*c == ':') { + c++; + } else if (*c == '}') { + c++; + } else if (*c == '\0') { + tuple_error("Expected '}' or function arguments in '%s'\n", item); + goto ret_error; + } + } else { + tuple_error("Expected '{', got '%c' in '%s'.\n", *c, c); + goto ret_error; + } + } else { + /* Parse raw/literal text */ + size_t i = 0; + while (*c != '\0' && *c != '$' && *c != '%' && *c != '}' && i < (MAX_STR - 1)) { + if (*c == '\\') c++; + tmps1[i++] = *(c++); + } + tmps1[i] = '\0'; + + tmp = tuple_evalnode_new(); + tmp->opcode = OP_RAW; + tmp->text = g_strdup(tmps1); + tuple_evalnode_insert(&res, tmp); + } + } + + if (*level <= 0) { + tuple_error("Syntax error! Uneven/unmatched nesting of elements in '%s'!\n", c); + goto ret_error; + } + + *expression = c; + return res; + +ret_error: + tuple_evalnode_free(tmp); + tuple_evalnode_free(res); + return NULL; +} + + +static TupleEvalNode *tuple_compiler_pass2(gboolean *changed, TupleEvalContext *ctx, TupleEvalNode *expr) +{ + TupleEvalNode *curr = expr, *res = NULL; + *changed = FALSE; + assert(ctx != NULL); + assert(expr != NULL); + + return res; +} + + +TupleEvalNode *tuple_formatter_compile(TupleEvalContext *ctx, gchar *expr) +{ + gint level = 0; + gboolean changed = FALSE; + gchar *tmpexpr = expr; + TupleEvalNode *res1, *res2; + + res1 = tuple_compiler_pass1(&level, ctx, &tmpexpr); + + if (level != 1) { + tuple_error("Syntax error! Uneven/unmatched nesting of elements! (%d)\n", level); + tuple_evalnode_free(res1); + return NULL; + } + + res2 = tuple_compiler_pass2(&changed, ctx, res1); + + if (changed) { + tuple_evalnode_free(res1); + return res2; + } else { + tuple_evalnode_free(res2); + return res1; + } +} + + +/* Evaluate tuple in given TupleEval expression in given + * context and return resulting string. + */ +static TupleValue * tf_get_dictref(TupleEvalVar *var, Tuple *tuple) +{ + if (var->type == VAR_FIELD && var->dictref == NULL) + var->dictref = mowgli_dictionary_retrieve(tuple->dict, var->name); + + return var->dictref; +} + +static TupleValueType tf_get_var(gchar **tmps, gint *tmpi, TupleEvalVar *var, Tuple *tuple) +{ + TupleValueType type = TUPLE_UNKNOWN; + + switch (var->type) { + case VAR_DEF: *tmps = var->defval; type = TUPLE_STRING; break; + case VAR_CONST: *tmps = var->name; type = TUPLE_STRING; break; + case VAR_FIELD: + if (tf_get_dictref(var, tuple)) { + if (var->dictref->type == TUPLE_STRING) + *tmps = var->dictref->value.string; + else + *tmpi = var->dictref->value.integer; + type = var->dictref->type; + } + break; + default: + tmps = NULL; + tmpi = 0; + } + + return type; +} + + +static gboolean tuple_formatter_eval_do(TupleEvalContext *ctx, TupleEvalNode *expr, Tuple *tuple, gchar **res, size_t *resmax, size_t *reslen) +{ + TupleEvalNode *curr = expr; + gchar tmps[MAX_STR], *tmps2; + gboolean result; + gint resulti; + TupleEvalVar *var0, *var1; + gint tmpi0, tmpi1; + gchar *tmps0, *tmps1; + TupleValueType type0, type1; + + if (!expr) return FALSE; + + while (curr) { + const gchar *str = NULL; + + switch (curr->opcode) { + case OP_RAW: + str = curr->text; + break; + + case OP_FIELD: + var0 = ctx->variables[curr->var[0]]; + + switch (var0->type) { + case VAR_DEF: + str = var0->defval; + break; + + case VAR_FIELD: + if (tf_get_dictref(var0, tuple)) { + switch (var0->dictref->type) { + case TUPLE_STRING: + str = var0->dictref->value.string; + break; + + case TUPLE_INT: + snprintf(tmps, sizeof(tmps), "%d", var0->dictref->value.integer); + str = tmps; + break; + + default: + str = NULL; + } + } + break; + } + break; + + case OP_EXISTS: + if (mowgli_dictionary_retrieve(tuple->dict, ctx->variables[curr->var[0]]->name)) { + if (!tuple_formatter_eval_do(ctx, curr->children, tuple, res, resmax, reslen)) + return FALSE; + } + break; + + case OP_EQUALS: + case OP_NOT_EQUALS: + case OP_LT: case OP_LTEQ: + case OP_GT: case OP_GTEQ: + var0 = ctx->variables[curr->var[0]]; + var1 = ctx->variables[curr->var[1]]; + + type0 = tf_get_var(&tmps0, &tmpi0, var0, tuple); + type1 = tf_get_var(&tmps1, &tmpi1, var1, tuple); + + if (type0 == type1) { + if (type0 == TUPLE_STRING) + resulti = strcmp(tmps0, tmps1); + else + resulti = tmpi0 - tmpi1; + + switch (curr->opcode) { + case OP_EQUALS: result = (resulti == 0); break; + case OP_NOT_EQUALS: result = (resulti != 0); break; + case OP_LT: result = (resulti < 0); break; + case OP_LTEQ: result = (resulti <= 0); break; + case OP_GT: result = (resulti > 0); break; + case OP_GTEQ: result = (resulti >= 0); break; + default: result = FALSE; + } + + if (result && !tuple_formatter_eval_do(ctx, curr->children, tuple, res, resmax, reslen)) + return FALSE; + } + break; + + case OP_IS_EMPTY: + var0 = ctx->variables[curr->var[0]]; + + if (var0->dictref == NULL) + var0->dictref = mowgli_dictionary_retrieve(tuple->dict, var0->name); + + switch (var0->dictref->type) { + case TUPLE_INT: + result = (var0->dictref->value.integer == 0); + break; + + case TUPLE_STRING: + result = TRUE; + tmps2 = var0->dictref->value.string; + + while (result && *tmps2 != '\0') { + if (*tmps2 == ' ') + tmps2++; + else + result = FALSE; + } + break; + + default: + result = TRUE; + } + + if (result) { + if (!tuple_formatter_eval_do(ctx, curr->children, tuple, res, resmax, reslen)) + return FALSE; + } + break; + + default: + tuple_error("Unimplemented opcode %d!\n", curr->opcode); + return FALSE; + break; + } + + if (str) { + /* (Re)allocate res for more space, if needed. */ + *reslen += strlen(str); + if (*res) { + if (*reslen >= *resmax) { + *resmax += MIN_ALLOC_BUF; + *res = g_realloc(*res, *resmax); + } + + strcat(*res, str); + } else { + *resmax = *reslen + MIN_ALLOC_BUF; + *res = g_malloc(*resmax); + + strcpy(*res, str); + } + } + + curr = curr->next; + } + + return TRUE; +} + + +gchar *tuple_formatter_eval(TupleEvalContext *ctx, TupleEvalNode *expr, Tuple *tuple) +{ + gchar *res = g_strdup(""); + size_t resmax = 0, reslen = 0; + assert(ctx != NULL); + assert(tuple != NULL); + + if (!expr) return NULL; + + if (!tuple_formatter_eval_do(ctx, expr, tuple, &res, &resmax, &reslen)) { + g_free(res); + return NULL; + } + + return res; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/audacious/tuple_compiler.h Thu Aug 30 23:41:33 2007 +0300 @@ -0,0 +1,108 @@ +/* + * Audacious - Tuplez compiler + * Copyright (c) 2007 Matti 'ccr' Hämäläinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; under version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses>. + * + * The Audacious team does not consider modular code linking to + * Audacious or using our public API to be a derived work. + */ +#ifndef __AUDACIOUS_TUPLE_COMPILER_H__ +#define __AUDACIOUS_TUPLE_COMPILER_H__ + +#include <glib.h> +#include <mowgli.h> +#include "tuple.h" + + +#define MAX_VAR (4) +#define MAX_STR (256) +#define MIN_ALLOC_NODES (8) +#define MIN_ALLOC_BUF (64) + + +enum { + OP_RAW = 0, /* plain text */ + OP_FIELD, /* a field/variable */ + OP_EXISTS, + OP_DEF_STRING, + OP_DEF_INT, + OP_EQUALS, + OP_NOT_EQUALS, + OP_GT, + OP_GTEQ, + OP_LT, + OP_LTEQ, + OP_IS_EMPTY, + + OP_FUNCTION, /* function */ + OP_EXPRESSION /* additional registered expressions */ +}; + + +enum { + VAR_FIELD = 0, + VAR_CONST, + VAR_DEF +}; + + +/* Caching structure for deterministic functions + */ +typedef struct { + gchar *name; + gboolean isdeterministic; + gchar *(*func)(Tuple *tuple, gchar **argument); +} TupleEvalFunc; + + +typedef struct { + gchar *name; + gboolean islocal; /* Local? true = will be cleaned with tuple_evalctx_reset() */ + gint type; /* Type of this "variable", see VAR_* */ + gchar *defval; + TupleValue *dictref; /* Cached tuple value ref */ +} TupleEvalVar; + + +typedef struct _TupleEvalNode { + gint opcode; /* operator, see OP_ enums */ + gint var[MAX_VAR]; /* tuple / global variable references (perhaps hashes, or just indexes to a list?) */ + gboolean global[MAX_VAR]; + gchar *text; /* raw text, if any (OP_RAW) */ + gint function, expression; /* for OP_FUNCTION and OP_EXPRESSION */ + struct _TupleEvalNode *children, *next, *prev; /* children of this struct, and pointer to next node. */ +} TupleEvalNode; + + +typedef struct { + gint nvariables, nfunctions, nexpressions; + TupleEvalVar **variables; + TupleEvalFunc **functions; +} TupleEvalContext; + + +TupleEvalContext * tuple_evalctx_new(void); +void tuple_evalctx_reset(TupleEvalContext *ctx); +void tuple_evalctx_free(TupleEvalContext *ctx); +gint tuple_evalctx_add_var(TupleEvalContext *ctx, gchar *name, gboolean islocal, gint type); + +void tuple_evalnode_free(TupleEvalNode *expr); + +TupleEvalNode *tuple_formatter_compile(TupleEvalContext *ctx, gchar *expr); +gchar *tuple_formatter_eval(TupleEvalContext *ctx, TupleEvalNode *expr, Tuple *tuple); +/* +gchar *tuple_formatter_make_title_string(Tuple *tuple, const gchar *string); +*/ + +#endif
--- a/src/audacious/tuple_formatter.c Thu Aug 30 17:41:40 2007 +0200 +++ b/src/audacious/tuple_formatter.c Thu Aug 30 23:41:33 2007 +0300 @@ -25,7 +25,7 @@ #include "tuple.h" #include "tuple_formatter.h" -#define _DEBUG +//#define _DEBUG #ifdef _DEBUG # define _TRACE(fmt, ...) g_print("[tuple-fmt] %s(%d) " fmt "\n", __FILE__, __LINE__, __VA_ARGS__);