diff options
Diffstat (limited to 'e-util')
-rw-r--r-- | e-util/Makefile.am | 8 | ||||
-rw-r--r-- | e-util/e-sexp.c | 1088 | ||||
-rw-r--r-- | e-util/e-sexp.h | 115 |
3 files changed, 1208 insertions, 3 deletions
diff --git a/e-util/Makefile.am b/e-util/Makefile.am index c6058791dd..2b1098a5d2 100644 --- a/e-util/Makefile.am +++ b/e-util/Makefile.am @@ -1,9 +1,9 @@ INCLUDES = \ $(GNOME_INCLUDEDIR) -noinst_LIBRARIES = libeutil.a +noinst_LTLIBRARIES = libeutil.la -libeutil_a_SOURCES = \ +libeutil_la_SOURCES = \ e-canvas.c \ e-canvas.h \ e-canvas-utils.c\ @@ -17,4 +17,6 @@ libeutil_a_SOURCES = \ e-util.c \ e-util.h \ e-xml-utils.c \ - e-xml-utils.h + e-xml-utils.h \ + e-sexp.c \ + s-sexp.h diff --git a/e-util/e-sexp.c b/e-util/e-sexp.c new file mode 100644 index 0000000000..b1438c1468 --- /dev/null +++ b/e-util/e-sexp.c @@ -0,0 +1,1088 @@ +/* + * Copyright 2000 HelixCode (http://www.helixcode.com). + * + * A simple, extensible s-exp evaluation engine. + * + * Author : + * Michael Zucchi <notzed@helixcode.com> + + * 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; either version 2 of the + * License, or (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +/* + The following built-in s-exp's are supported: + + list = (and list*) + perform an intersection of a number of lists, and return that. + + bool = (and bool*) + perform a boolean AND of boolean values. + + list = (or list*) + perform a union of a number of lists, returning the new list. + + bool = (or bool*) + perform a boolean OR of boolean values. + + int = (+ int*) + Add integers. + + string = (+ string*) + Concat strings. + + int = (- int int*) + Subtract integers from the first. + + Comparison operators: + + bool = (< int int) + bool = (> int int) + bool = (= int int) + + bool = (< string string) + bool = (> string string) + bool = (= string string) + Perform a comparision of 2 integers, or 2 string values. + + Function flow: + + type = (if bool function) + type = (if bool function function) + Choose a flow path based on a boolean value + + type = (begin func func func) + Execute a sequence. The last function return is the return type. +*/ + +#include <glib.h> +#include <stdio.h> +#include <time.h> +#include <string.h> + +#include "e-sexp.h" + +#define p(x) /* parse debug */ +#define r(x) /* run debug */ +#define d(x) /* general debug */ + + +static struct _ESExpTerm * parse_list(ESExp *f, int gotbrace); +static struct _ESExpTerm * parse_value(ESExp *f); + +static void parse_dump_term(struct _ESExpTerm *t, int depth); + +static GtkObjectClass *parent_class; + + + +static GScannerConfig scanner_config = +{ + ( " \t\r\n") /* cset_skip_characters */, + ( G_CSET_a_2_z + "_+<=>" + G_CSET_A_2_Z) /* cset_identifier_first */, + ( G_CSET_a_2_z + "_0123456789-<>" + G_CSET_A_2_Z + G_CSET_LATINS + G_CSET_LATINC ) /* cset_identifier_nth */, + ( ";\n" ) /* cpair_comment_single */, + + FALSE /* case_sensitive */, + + TRUE /* skip_comment_multi */, + TRUE /* skip_comment_single */, + TRUE /* scan_comment_multi */, + TRUE /* scan_identifier */, + TRUE /* scan_identifier_1char */, + FALSE /* scan_identifier_NULL */, + TRUE /* scan_symbols */, + FALSE /* scan_binary */, + TRUE /* scan_octal */, + TRUE /* scan_float */, + TRUE /* scan_hex */, + FALSE /* scan_hex_dollar */, + TRUE /* scan_string_sq */, + TRUE /* scan_string_dq */, + TRUE /* numbers_2_int */, + FALSE /* int_2_float */, + FALSE /* identifier_2_string */, + TRUE /* char_2_token */, + FALSE /* symbol_2_token */, + FALSE /* scope_0_fallback */, +}; + +struct _ESExpResult * +e_sexp_result_new(int type) +{ + struct _ESExpResult *r = g_malloc0(sizeof(*r)); + r->type = type; + return r; +} + +void +e_sexp_result_free(struct _ESExpResult *t) +{ + if (t == NULL) + return; + + switch(t->type) { + case ESEXP_RES_ARRAY_PTR: + g_ptr_array_free(t->value.ptrarray, TRUE); + break; + case ESEXP_RES_BOOL: + case ESEXP_RES_INT: + break; + case ESEXP_RES_STRING: + g_free(t->value.string); + break; + case ESEXP_RES_UNDEFINED: + break; + } + g_free(t); +} + +/* implementations for the builtin functions */ + +/* can you tell, i dont like glib? */ +/* we can only itereate a hashtable from a called function */ +struct _glib_sux_donkeys { + int count; + GPtrArray *uids; +}; + +/* ok, store any values that are in all sets */ +static void +g_lib_sux_htand(char *key, int value, struct _glib_sux_donkeys *fuckup) +{ + if (value == fuckup->count) { + g_ptr_array_add(fuckup->uids, key); + } +} + +/* or, store all unique values */ +static void +g_lib_sux_htor(char *key, int value, struct _glib_sux_donkeys *fuckup) +{ + g_ptr_array_add(fuckup->uids, key); +} + +static ESExpResult * +term_eval_and(struct _ESExp *f, int argc, struct _ESExpTerm **argv, void *data) +{ + struct _ESExpResult *r, *r1; + GHashTable *ht = g_hash_table_new(g_str_hash, g_str_equal); + struct _glib_sux_donkeys lambdafoo; + int type=-1; + int bool = TRUE; + int i; + + r(printf("( and\n")); + + r = e_sexp_result_new(ESEXP_RES_UNDEFINED); + + for (i=0;bool && i<argc;i++) { + r1 = e_sexp_term_eval(f, argv[i]); + if (type == -1) + type = r1->type; + if (type != r1->type) { + printf("invalid types in and operation, all types must be the same\n"); + } else if ( r1->type == ESEXP_RES_ARRAY_PTR ) { + char **a1; + int l1, j; + + a1 = (char **)r1->value.ptrarray->pdata; + l1 = r1->value.ptrarray->len; + for (j=0;j<l1;j++) { + int n; + n = (int)g_hash_table_lookup(ht, a1[j]); + g_hash_table_insert(ht, a1[j], (void *)n+1); + } + } else if ( r1->type == ESEXP_RES_BOOL ) { + bool &= r1->value.bool; + } + e_sexp_result_free(r1); + } + + if (type == ESEXP_RES_ARRAY_PTR) { + lambdafoo.count = argc; + lambdafoo.uids = g_ptr_array_new(); + g_hash_table_foreach(ht, (GHFunc)g_lib_sux_htand, &lambdafoo); + r->type = ESEXP_RES_ARRAY_PTR; + r->value.ptrarray = lambdafoo.uids; + } else if (type == ESEXP_RES_BOOL) { + r->type = ESEXP_RES_BOOL; + r->value.bool = bool; + } + + g_hash_table_destroy(ht); + + return r; +} + +static ESExpResult * +term_eval_or(struct _ESExp *f, int argc, struct _ESExpTerm **argv, void *data) +{ + struct _ESExpResult *r, *r1; + GHashTable *ht = g_hash_table_new(g_str_hash, g_str_equal); + struct _glib_sux_donkeys lambdafoo; + int type = -1; + int bool = FALSE; + int i; + + r(printf("(or \n")); + + r = e_sexp_result_new(ESEXP_RES_UNDEFINED); + + for (i=0;!bool && i<argc;i++) { + r1 = e_sexp_term_eval(f, argv[i]); + if (type == -1) + type = r1->type; + if (r1->type != type) { + printf("wrong types in or operation\n"); + } else if (r1->type == ESEXP_RES_ARRAY_PTR) { + char **a1; + int l1, j; + + a1 = (char **)r1->value.ptrarray->pdata; + l1 = r1->value.ptrarray->len; + for (j=0;j<l1;j++) { + g_hash_table_insert(ht, a1[j], (void *)1); + } + } else if (r1->type == ESEXP_RES_BOOL) { + bool |= r1->value.bool; + } + e_sexp_result_free(r1); + } + + if (type == ESEXP_RES_ARRAY_PTR) { + lambdafoo.count = argc; + lambdafoo.uids = g_ptr_array_new(); + g_hash_table_foreach(ht, (GHFunc)g_lib_sux_htor, &lambdafoo); + r->type = ESEXP_RES_ARRAY_PTR; + r->value.ptrarray = lambdafoo.uids; + } else if (type == ESEXP_RES_BOOL) { + r->type = ESEXP_RES_BOOL; + r->value.bool = bool; + } + g_hash_table_destroy(ht); + + return r; +} + +static ESExpResult * +term_eval_not(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data) +{ + int res = TRUE; + ESExpResult *r; + + if (argc>0) { + if (argv[0]->type == ESEXP_RES_BOOL + && argv[0]->value.bool) + res = FALSE; + } + r = e_sexp_result_new(ESEXP_RES_BOOL); + r->value.bool = res; + return r; +} + +/* this should support all arguments ...? */ +static ESExpResult * +term_eval_lt(struct _ESExp *f, int argc, struct _ESExpTerm **argv, void *data) +{ + struct _ESExpResult *r, *r1, *r2; + + r = e_sexp_result_new(ESEXP_RES_UNDEFINED); + + if (argc == 2) { + r1 = e_sexp_term_eval(f, argv[0]); + r2 = e_sexp_term_eval(f, argv[1]); + if (r1->type != r2->type) { + printf("error, invalid types in compare\n"); + } else if (r1->type == ESEXP_RES_INT) { + r->type = ESEXP_RES_BOOL; + r->value.bool = r1->value.number < r2->value.number; + } else if (r1->type == ESEXP_RES_STRING) { + r->type = ESEXP_RES_BOOL; + r->value.bool = strcmp(r1->value.string, r2->value.string) < 0; + } + } + return r; +} + +/* this should support all arguments ...? */ +static ESExpResult * +term_eval_gt(struct _ESExp *f, int argc, struct _ESExpTerm **argv, void *data) +{ + struct _ESExpResult *r, *r1, *r2; + + r = e_sexp_result_new(ESEXP_RES_UNDEFINED); + + if (argc == 2) { + r1 = e_sexp_term_eval(f, argv[0]); + r2 = e_sexp_term_eval(f, argv[1]); + if (r1->type != r2->type) { + printf("error, invalid types in compare\n"); + } else if (r1->type == ESEXP_RES_INT) { + r->type = ESEXP_RES_BOOL; + r->value.bool = r1->value.number > r2->value.number; + } else if (r1->type == ESEXP_RES_STRING) { + r->type = ESEXP_RES_BOOL; + r->value.bool = strcmp(r1->value.string, r2->value.string) > 0; + } + } + return r; +} + +/* this should support all arguments ...? */ +static ESExpResult * +term_eval_eq(struct _ESExp *f, int argc, struct _ESExpTerm **argv, void *data) +{ + struct _ESExpResult *r, *r1, *r2; + + r = e_sexp_result_new(ESEXP_RES_BOOL); + + if (argc == 2) { + r1 = e_sexp_term_eval(f, argv[0]); + r2 = e_sexp_term_eval(f, argv[1]); + if (r1->type != r2->type) { + r->value.bool = FALSE; + } else if (r1->type == ESEXP_RES_INT) { + r->value.bool = r1->value.number == r2->value.number; + } else if (r1->type == ESEXP_RES_BOOL) { + r->value.bool = r1->value.bool == r2->value.bool; + } else if (r1->type == ESEXP_RES_STRING) { + r->value.bool = strcmp(r1->value.string, r2->value.string) == 0; + } + } + return r; +} + +static ESExpResult * +term_eval_plus(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data) +{ + struct _ESExpResult *r=NULL; + int type; + int i; + + if (argc>0) { + type = argv[0]->type; + switch(type) { + case ESEXP_RES_INT: { + int total = argv[0]->value.number; + for (i=1;i<argc && argv[i]->type == ESEXP_RES_INT;i++) { + total += argv[i]->value.number; + } + if (i<argc) { + g_warning("Wrong type trying to add integers: ignored"); + } + r = e_sexp_result_new(ESEXP_RES_INT); + r->value.number = total; + break; } + case ESEXP_RES_STRING: { + GString *s = g_string_new(argv[0]->value.string); + for (i=1;i<argc && argv[i]->type == ESEXP_RES_STRING;i++) { + g_string_append(s, argv[i]->value.string); + } + if (i<argc) { + g_warning("Wrong type trying to concat strings: ignored"); + } + r = e_sexp_result_new(ESEXP_RES_STRING); + r->value.string = s->str; + g_string_free(s, FALSE); + break; } + + } + } + + if (!r) { + r = e_sexp_result_new(ESEXP_RES_INT); + r->value.number = 0; + } + return r; +} + +static ESExpResult * +term_eval_sub(struct _ESExp *f, int argc, struct _ESExpResult **argv, void *data) +{ + struct _ESExpResult *r=NULL; + int type; + int i; + + if (argc>0) { + type = argv[0]->type; + switch(type) { + case ESEXP_RES_INT: { + int total = argv[0]->value.number; + for (i=1;i<argc && argv[i]->type == ESEXP_RES_INT;i++) { + total -= argv[i]->value.number; + } + if (i<argc) { + g_warning("Wrong type trying to subtract numbers: ignored"); + } + r = e_sexp_result_new(ESEXP_RES_INT); + r->value.number = total; + break; } + } + } + + if (!r) { + r = e_sexp_result_new(ESEXP_RES_INT); + r->value.number = 0; + } + return r; +} + +/* implements 'if' function */ +static ESExpResult * +term_eval_if(struct _ESExp *f, int argc, struct _ESExpTerm **argv, void *data) +{ + struct _ESExpResult *r; + int doit; + + if (argc >=2 && argc<=3) { + r = e_sexp_term_eval(f, argv[0]); + doit = (r->type == ESEXP_RES_BOOL && r->value.bool); + e_sexp_result_free(r); + if (doit) { + return e_sexp_term_eval(f, argv[1]); + } else if (argc>2) { + return e_sexp_term_eval(f, argv[2]); + } + } + return e_sexp_result_new(ESEXP_RES_UNDEFINED); +} + +/* implements 'begin' statement */ +static ESExpResult * +term_eval_begin(struct _ESExp *f, int argc, struct _ESExpTerm **argv, void *data) +{ + struct _ESExpResult *r=NULL; + int i; + + for (i=0;i<argc;i++) { + if (r) + e_sexp_result_free(r); + r = e_sexp_term_eval(f, argv[i]); + } + if (r) + return r; + else + return e_sexp_result_new(ESEXP_RES_UNDEFINED); +} + + +struct _ESExpResult * +e_sexp_term_eval(struct _ESExp *f, struct _ESExpTerm *t) +{ + struct _ESExpResult *r, *r1; + int i; + + r(printf("eval term :\n")); + r(parse_dump_term(t, 0)); + + r = g_malloc0(sizeof(*r)); + r->type = ESEXP_RES_UNDEFINED; + + switch (t->type) { + case ESEXP_TERM_STRING: + r(printf(" (string \"%s\")\n", t->value.string)); + r->type = ESEXP_RES_STRING; + /* erk, this shoul;dn't need to strdup this ... */ + r->value.string = g_strdup(t->value.string); + break; + case ESEXP_TERM_INT: + r(printf(" (int %d)\n", t->value.number)); + r->type = ESEXP_RES_INT; + r->value.number = t->value.number; + break; + case ESEXP_TERM_BOOL: + r(printf(" (int %d)\n", t->value.number)); + r->type = ESEXP_RES_BOOL; + r->value.bool = t->value.bool; + break; + case ESEXP_TERM_IFUNC: { + if (t->value.func.sym->f.ifunc) { + r1 = t->value.func.sym->f.ifunc(f, t->value.func.termcount, t->value.func.terms, t->value.func.sym->data); + if (r1) { + e_sexp_result_free(r); + r = r1; + } + } + break; } + case ESEXP_TERM_FUNC: { + struct _ESExpResult **argv; + + /* first evaluate all arguments to result types */ + argv = alloca(sizeof(argv[0]) * t->value.func.termcount); + for (i=0;i<t->value.func.termcount;i++) { + argv[i] = e_sexp_term_eval(f, t->value.func.terms[i]); + } + /* call the function */ + if (t->value.func.sym->f.func) { + r1 = t->value.func.sym->f.func(f, t->value.func.termcount, argv, t->value.func.sym->data); + if (r1) { + e_sexp_result_free(r); + r = r1; + } + } + for (i=0;i<t->value.func.termcount;i++) { + e_sexp_result_free(argv[i]); + } + break; } + default: + printf("Warning: Unknown type encountered in parse tree: %d\n", t->type); + r->type = ESEXP_RES_UNDEFINED; + } + + return r; +} + + +static void +eval_dump_result(ESExpResult *r, int depth) +{ + int i; + + if (r==NULL) { + printf("null result???\n"); + return; + } + + for (i=0;i<depth;i++) + printf(" "); + + switch (r->type) { + case ESEXP_RES_ARRAY_PTR: + printf("array pointers\n"); + break; + case ESEXP_RES_INT: + printf("int: %d\n", r->value.number); + break; + case ESEXP_RES_STRING: + printf("string: '%s'\n", r->value.string); + break; + case ESEXP_RES_BOOL: + printf("bool: %c\n", r->value.bool?'t':'f'); + break; + case ESEXP_RES_UNDEFINED: + printf(" <undefined>\n"); + break; + } + printf("\n"); +} + +static void +parse_dump_term(struct _ESExpTerm *t, int depth) +{ + int i; + + if (t==NULL) { + printf("null term??\n"); + return; + } + + for (i=0;i<depth;i++) + printf(" "); + + switch (t->type) { + case ESEXP_TERM_STRING: + printf(" \"%s\"", t->value.string); + break; + case ESEXP_TERM_INT: + printf(" %d", t->value.number); + break; + case ESEXP_TERM_BOOL: + printf(" #%c", t->value.bool?'t':'f'); + break; + case ESEXP_TERM_IFUNC: + case ESEXP_TERM_FUNC: + printf(" (function %s\n", t->value.func.sym->name); + /*printf(" [%d] ", t->value.func.termcount);*/ + for (i=0;i<t->value.func.termcount;i++) { + parse_dump_term(t->value.func.terms[i], depth+1); + } + for (i=0;i<depth;i++) + printf(" "); + printf(" )"); + break; + case ESEXP_TERM_VAR: + printf(" (variable %s )\n", t->value.var->name); + break; + default: + printf("unknown type: %d\n", t->type); + } + + printf("\n"); +} + +/* + PARSER +*/ + +static struct _ESExpTerm * +parse_new_term(int type) +{ + struct _ESExpTerm *s = g_malloc0(sizeof(*s)); + s->type = type; + return s; +} + +static void +parse_term_free(struct _ESExpTerm *t) +{ + int i; + + if (t==NULL) { + return; + } + + switch (t->type) { + case ESEXP_TERM_FUNC: + case ESEXP_TERM_IFUNC: + for (i=0;i<t->value.func.termcount;i++) { + parse_term_free(t->value.func.terms[i]); + } + g_free(t->value.func.terms); + break; + case ESEXP_TERM_VAR: + break; + case ESEXP_TERM_STRING: + g_free(t->value.string); + break; + case ESEXP_TERM_INT: + break; + default: + printf("parse_term_free: unknown type: %d\n", t->type); + } + g_free(t); +} + +static struct _ESExpTerm ** +parse_values(ESExp *f, int *len) +{ + int token; + struct _ESExpTerm **terms; + int i=0; + GScanner *gs = f->scanner; + + p(printf("parsing values\n")); + + /* FIXME: This hardcoded nonsense!!! :) */ + terms = g_malloc0(20*sizeof(*terms)); + + while ( (token = g_scanner_peek_next_token(gs)) != G_TOKEN_EOF + && token != ')') { + terms[i]=parse_value(f); + i++; + } + + p(printf("found %d subterms\n", i)); + *len = i; + + p(printf("dont parsing values\n")); + return terms; +} + +static struct _ESExpTerm * +parse_value(ESExp *f) +{ + int token; + struct _ESExpTerm *t = NULL; + GScanner *gs = f->scanner; + struct _ESExpSymbol *s; + + p(printf("parsing value\n")); + + token = g_scanner_get_next_token(gs); + switch(token) { + case G_TOKEN_LEFT_PAREN: + p(printf("got brace, its a list!\n")); + return parse_list(f, TRUE); + case G_TOKEN_STRING: + p(printf("got string\n")); + t = parse_new_term(ESEXP_TERM_STRING); + t->value.string = g_strdup(g_scanner_cur_value(gs).v_string); + break; + case G_TOKEN_INT: + t = parse_new_term(ESEXP_TERM_INT); + t->value.number = g_scanner_cur_value(gs).v_int; + p(printf("got int\n")); + break; + case '#': + printf("got bool?\n"); + token = g_scanner_get_next_token(gs); + t = parse_new_term(ESEXP_TERM_BOOL); + t->value.bool = token=='t'; + break; + case G_TOKEN_SYMBOL: + s = g_scanner_cur_value(gs).v_symbol; + switch (s->type) { + case ESEXP_TERM_FUNC: + case ESEXP_TERM_IFUNC: + /* this is basically invalid, since we can't use function + pointers, but let the runtime catch it ... */ + t = parse_new_term(s->type); + t->value.func.sym = s; + t->value.func.terms = parse_values(f, &t->value.func.termcount); + break; + case ESEXP_TERM_VAR: + t = parse_new_term(s->type); + t->value.var = s; + break; + default: + printf("Invalid symbol type: %d\n", s->type); + } + break; + case G_TOKEN_IDENTIFIER: + printf("Unknown identifier encountered: %s\n", g_scanner_cur_value(gs).v_identifier); + break; + default: + printf("Innvalid token trying to parse a list of values\n"); + } + p(printf("done parsing value\n")); + return t; +} + +/* FIXME: this needs some robustification */ +static struct _ESExpTerm * +parse_list(ESExp *f, int gotbrace) +{ + int token; + struct _ESExpTerm *t = NULL; + GScanner *gs = f->scanner; + + p(printf("parsing list\n")); + if (gotbrace) + token = '('; + else + token = g_scanner_get_next_token(gs); + if (token =='(') { + token = g_scanner_get_next_token(gs); + switch(token) { + case G_TOKEN_SYMBOL: { + struct _ESExpSymbol *s; + + s = g_scanner_cur_value(gs).v_symbol; + p(printf("got funciton: %s\n", s->name)); + t = parse_new_term(s->type); + p(printf("created new list %p\n", t)); + /* if we have a variable, find out its base type */ + while (s->type == ESEXP_TERM_VAR) { + s = ((ESExpTerm *)(s->data))->value.var; + } + if (s->type == ESEXP_TERM_FUNC + || s->type == ESEXP_TERM_IFUNC) { + t->value.func.sym = s; + t->value.func.terms = parse_values(f, &t->value.func.termcount); + } else { + printf("Error, trying to call variable as function\n"); + } + break; } + case G_TOKEN_IDENTIFIER: + printf("Unknown identifier: %s\n", g_scanner_cur_value(gs).v_identifier); + break; + default: + printf("unknown sequence encountered, type = %d\n", token); + } + token = g_scanner_get_next_token(gs); + if (token != ')') { + printf("Error, expected ')' not found\n"); + } + } else { + printf("Error, list term without opening (\n"); + } + + p(printf("returning list %p\n", t)); + return t; +} + +#if 0 +GList * +camel_mbox_folder_search_by_expression(CamelFolder *folder, char *expression, CamelException *ex) +{ + GScanner *gs; + int i; + struct _ESExpTerm *t; + struct _searchcontext *ctx; + struct _ESExpResult *r; + GList *matches = NULL; + + gs = g_scanner_new(&scanner_config); + for(i=0;i<sizeof(symbols)/sizeof(symbols[0]);i++) + g_scanner_scope_add_symbol(gs, 0, symbols[i].name, &symbols[i]); + + g_scanner_input_text(gs, expression, strlen(expression)); + t = parse_list(gs, 0); + + if (t) { + ctx = g_malloc0(sizeof(*ctx)); + ctx->folder = folder; + ctx->summary = camel_folder_get_summary(folder, ex); + ctx->message_info = camel_folder_summary_get_message_info_list(ctx->summary); +#ifdef HAVE_IBEX + ctx->index = ibex_open(CAMEL_MBOX_FOLDER(folder)->index_file_path, FALSE); + if (!ctx->index) { + perror("Cannot open index file, body searches will be ignored\n"); + } +#endif + r = term_eval(ctx, t); + + /* now create a folder summary to return?? */ + if (r + && r->type == ESEXP_RES_ARRAY_PTR) { + d(printf("got result ...\n")); + for (i=0;i<r->value.ptrarray->len;i++) { + d(printf("adding match: %s\n", (char *)g_ptr_array_index(r->value.ptrarray, i))); + matches = g_list_prepend(matches, g_strdup(g_ptr_array_index(r->value.ptrarray, i))); + } + e_sexp_result_free(r); + } + + if (ctx->index) + ibex_close(ctx->index); + + gtk_object_unref((GtkObject *)ctx->summary); + g_free(ctx); + parse_term_free(t); + } else { + printf("Warning, Could not parse expression!\n %s\n", expression); + } + + g_scanner_destroy(gs); + + return matches; +} +#endif + + +static void +e_sexp_class_init (ESExpClass *class) +{ + GtkObjectClass *object_class; + + object_class = (GtkObjectClass *) class; + parent_class = gtk_type_class (gtk_object_get_type ()); +} + +/* 'builtin' functions */ +static struct { + char *name; + ESExpFunc *func; + int type; /* set to 1 if a function can perform shortcut evaluation, or + doesn't execute everything, 0 otherwise */ +} symbols[] = { + { "and", (ESExpFunc *)term_eval_and, 1 }, + { "or", (ESExpFunc *)term_eval_or, 1 }, + { "not", (ESExpFunc *)term_eval_not, 0 }, + { "<", (ESExpFunc *)term_eval_lt, 1 }, + { ">", (ESExpFunc *)term_eval_gt, 1 }, + { "=", (ESExpFunc *)term_eval_eq, 1 }, + { "+", (ESExpFunc *)term_eval_plus, 0 }, + { "-", (ESExpFunc *)term_eval_sub, 0 }, + { "if", (ESExpFunc *)term_eval_if, 1 }, + { "begin", (ESExpFunc *)term_eval_begin, 1 }, +}; + +static void +e_sexp_init (ESExp *s) +{ + int i; + + s->scanner = g_scanner_new(&scanner_config); + + /* load in builtin symbols? */ + for(i=0;i<sizeof(symbols)/sizeof(symbols[0]);i++) { + if (symbols[i].type == 1) { + e_sexp_add_ifunction(s, 0, symbols[i].name, (ESExpIFunc *)symbols[i].func, &symbols[i]); + } else { + e_sexp_add_function(s, 0, symbols[i].name, symbols[i].func, &symbols[i]); + } + } +} + +guint +e_sexp_get_type (void) +{ + static guint type = 0; + + if (!type) { + GtkTypeInfo type_info = { + "ESExp", + sizeof (ESExp), + sizeof (ESExpClass), + (GtkClassInitFunc) e_sexp_class_init, + (GtkObjectInitFunc) e_sexp_init, + (GtkArgSetFunc) NULL, + (GtkArgGetFunc) NULL + }; + + type = gtk_type_unique (gtk_object_get_type (), &type_info); + } + + return type; +} + +ESExp * +e_sexp_new (void) +{ + ESExp *f = E_SEXP ( gtk_type_new (e_sexp_get_type ())); + + return f; +} + +void +e_sexp_add_function(ESExp *f, int scope, char *name, ESExpFunc *func, void *data) +{ + struct _ESExpSymbol *s; + + g_return_if_fail(FILTER_IS_SEXP(f)); + g_return_if_fail(name != NULL); + + s = g_malloc0(sizeof(*s)); + s->name = g_strdup(name); + s->f.func = func; + s->type = ESEXP_TERM_FUNC; + s->data = data; + g_scanner_scope_add_symbol(f->scanner, scope, s->name, s); +} + +void +e_sexp_add_ifunction(ESExp *f, int scope, char *name, ESExpIFunc *ifunc, void *data) +{ + struct _ESExpSymbol *s; + + g_return_if_fail(FILTER_IS_SEXP(f)); + g_return_if_fail(name != NULL); + + s = g_malloc0(sizeof(*s)); + s->name = g_strdup(name); + s->f.ifunc = ifunc; + s->type = ESEXP_TERM_IFUNC; + s->data = data; + g_scanner_scope_add_symbol(f->scanner, scope, s->name, s); +} + +void +e_sexp_add_variable(ESExp *f, int scope, char *name, ESExpTerm *value) +{ + struct _ESExpSymbol *s; + + g_return_if_fail(FILTER_IS_SEXP(f)); + g_return_if_fail(name != NULL); + + s = g_malloc0(sizeof(*s)); + s->name = g_strdup(name); + s->type = ESEXP_TERM_VAR; + s->data = value; + g_scanner_scope_add_symbol(f->scanner, scope, s->name, s); +} + +void +e_sexp_remove_symbol(ESExp *f, int scope, char *name) +{ + int oldscope; + struct _ESExpSymbol *s; + + g_return_if_fail(FILTER_IS_SEXP(f)); + g_return_if_fail(name != NULL); + + oldscope = g_scanner_set_scope(f->scanner, scope); + s = g_scanner_lookup_symbol(f->scanner, name); + g_scanner_scope_remove_symbol(f->scanner, scope, name); + g_scanner_set_scope(f->scanner, oldscope); + if (s) { + g_free(s->name); + g_free(s); + } +} + +int +e_sexp_set_scope(ESExp *f, int scope) +{ + g_return_val_if_fail(FILTER_IS_SEXP(f), 0); + + return g_scanner_set_scope(f->scanner, scope); +} + +void +e_sexp_input_text(ESExp *f, char *text, int len) +{ + g_return_if_fail(FILTER_IS_SEXP(f)); + g_return_if_fail(text != NULL); + + g_scanner_input_text(f->scanner, text, len); +} + +void +e_sexp_input_file (ESExp *f, int fd) +{ + g_return_if_fail(FILTER_IS_SEXP(f)); + + g_scanner_input_file(f->scanner, fd); +} + +/* needs some error return? */ +void +e_sexp_parse(ESExp *f) +{ + g_return_if_fail(FILTER_IS_SEXP(f)); + + f->tree = parse_list(f, FALSE); + + if (f->tree) + parse_dump_term(f->tree, 0); +} + +struct _ESExpResult * +e_sexp_eval(ESExp *f) +{ + g_return_val_if_fail(FILTER_IS_SEXP(f), NULL); + g_return_val_if_fail(f->tree != NULL, NULL); + + return e_sexp_term_eval(f, f->tree); +} + +#ifdef TESTER +int main(int argc, char **argv) +{ + ESExp *f; + char *t = "(+ \"foo\" \"\\\"\" \"bar\" \"\\\\ blah \\x \")"; + ESExpResult *r; + + gtk_init(&argc, &argv); + + f = e_sexp_new(); + + e_sexp_add_variable(f, 0, "test", NULL); + + e_sexp_input_text(f, t, strlen(t)); + e_sexp_parse(f); + + if (f->tree) { + parse_dump_term(f->tree, 0); + } + + r = e_sexp_eval(f); + if (r) { + eval_dump_result(r, 0); + } else { + printf("no result?|\n"); + } + + return 0; +} +#endif diff --git a/e-util/e-sexp.h b/e-util/e-sexp.h new file mode 100644 index 0000000000..a41fdb0b58 --- /dev/null +++ b/e-util/e-sexp.h @@ -0,0 +1,115 @@ +/* + generic s-exp evaluator class +*/ +#ifndef _E_SEXP_H +#define _E_SEXP_H + +#include <glib.h> +#include <gtk/gtk.h> + +#define E_SEXP(obj) GTK_CHECK_CAST (obj, e_sexp_get_type (), ESExp) +#define E_SEXP_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, e_sexp_get_type (), ESExpClass) +#define FILTER_IS_SEXP(obj) GTK_CHECK_TYPE (obj, e_sexp_get_type ()) + +typedef struct _ESExp ESExp; +typedef struct _ESExpClass ESExpClass; + +typedef struct _ESExpSymbol ESExpSymbol; +typedef struct _ESExpResult ESExpResult; +typedef struct _ESExpTerm ESExpTerm; + +typedef struct _ESExpResult *(ESExpFunc)(struct _ESExp *sexp, + int argc, + struct _ESExpResult **argv, + void *data); + +typedef struct _ESExpResult *(ESExpIFunc)(struct _ESExp *sexp, + int argc, + struct _ESExpTerm **argv, + void *data); +enum _ESExpResultType { + ESEXP_RES_ARRAY_PTR=0, /* type is a ptrarray, what it points to is implementation dependant */ + ESEXP_RES_INT, /* type is a number */ + ESEXP_RES_STRING, /* type is a pointer to a single string */ + ESEXP_RES_BOOL, /* boolean type */ + ESEXP_RES_UNDEFINED /* unknown type */ +}; + +struct _ESExpResult { + enum _ESExpResultType type; + union { + GPtrArray *ptrarray; + int number; + char *string; + int bool; + } value; +}; + +enum _ESExpTermType { + ESEXP_TERM_INT = 0, /* integer literal */ + ESEXP_TERM_BOOL, /* boolean literal */ + ESEXP_TERM_STRING, /* string literal */ + ESEXP_TERM_FUNC, /* normal function, arguments are evaluated before calling */ + ESEXP_TERM_IFUNC, /* immediate function, raw terms are arguments */ + ESEXP_TERM_VAR, /* variable reference */ +}; + +struct _ESExpSymbol { + int type; /* ESEXP_TERM_FUNC or ESEXP_TERM_VAR */ + char *name; + void *data; + union { + ESExpFunc *func; + ESExpIFunc *ifunc; + } f; +}; + +struct _ESExpTerm { + enum _ESExpTermType type; + union { + char *string; + int number; + int bool; + struct { + struct _ESExpSymbol *sym; + struct _ESExpTerm **terms; + int termcount; + } func; + struct _ESExpSymbol *var; + } value; +}; + + + +struct _ESExp { + GtkObject object; + + GScanner *scanner; /* for parsing text version */ + ESExpTerm *tree; /* root of expression tree */ +}; + +struct _ESExpClass { + GtkObjectClass parent_class; + +}; + +guint e_sexp_get_type (void); +ESExp *e_sexp_new (void); +void e_sexp_add_function (ESExp *f, int scope, char *name, ESExpFunc *func, void *data); +void e_sexp_add_ifunction (ESExp *f, int scope, char *name, ESExpIFunc *func, void *data); +void e_sexp_add_variable (ESExp *f, int scope, char *name, ESExpTerm *value); +void e_sexp_remove_symbol (ESExp *f, int scope, char *name); +int e_sexp_set_scope (ESExp *f, int scope); + +void e_sexp_input_text (ESExp *f, char *text, int len); +void e_sexp_input_file (ESExp *f, int fd); + + +void e_sexp_parse (ESExp *f); +ESExpResult *e_sexp_eval (ESExp *f); + +ESExpResult *e_sexp_term_eval (struct _ESExp *f, struct _ESExpTerm *t); +ESExpResult *e_sexp_result_new (int type); +void e_sexp_result_free (struct _ESExpResult *t); + +#endif /* _E_SEXP_H */ |