summaryrefslogtreecommitdiff
path: root/libmu_sieve/sieve.y
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2016-11-23 13:26:23 +0200
committerSergey Poznyakoff <gray@gnu.org.ua>2016-11-23 13:44:34 +0200
commit15404d914aec7e80c884fc63dbfa5d3e347b32cb (patch)
tree5b3c17759d19c8a02ab1bafb722e0c00f5f28e2f /libmu_sieve/sieve.y
parenta04c6feb5007aeeb133b15558f13733743dee3e8 (diff)
downloadmailutils-15404d914aec7e80c884fc63dbfa5d3e347b32cb.tar.gz
mailutils-15404d914aec7e80c884fc63dbfa5d3e347b32cb.tar.bz2
Rewrite sieve parser.
Three objectives: 1. Simplify code. 2. Produce optimized sieve code. 3. Improve error reporting. 4. Prepare for further extensions * include/mailutils/sieve.h (mu_sieve_tag_checker_t): Change signature (take mu_sieve_machine_t as the first arg). All uses changed. (mu_sieve_require): Likewise. (mu_sieve_yydebug): Remove global. * libmu_sieve/sieve-priv.h (mu_locus_range): New struct. (YYLTYPE): New define (mu_sieve_state): New enum. (mu_sieve_machine): New members: string_pool, state. (mu_sieve_node_type): New enum. (mu_sieve_node): New struct. Remove unused prototypes. * libmu_sieve/sieve.l: Keep track of code locations. Use opool for constructing string values. * libmu_sieve/sieve.y: Rewrite. First build the parse tree. Then optimize it. Finally, generate code. * libmu_sieve/tests.c (sieve_test_true,sieve_test_false): Remove. True and false tests are always optimized away. * libmu_sieve/util.c (mu_sv_compile_error): Remove. * libmu_sieve/actions.c: Use mu_diag_at_locus to report errors and mu_i_sv_error to mark sieve machine as being in error state. * libmu_sieve/comparator.c: Likewise. * libmu_sieve/prog.c (mu_sv_code): Replace with mu_i_sv_code. (mu_sv_code_instr,mu_sv_code_handler) (mu_sv_code_list,mu_sv_code_number) (mu_sv_code_string,mu_sv_code_source) (mu_sv_code_line,mu_sv_change_source) (mu_sv_code_action,mu_sv_code_test) (mu_sv_code_anyof,mu_sv_code_allof): Remove. (mu_i_sv_locus,mu_i_sv_code_action) (mu_i_sv_code_test): New function. (mu_sv_code_command): Replace with a static function. * libmu_sieve/require.c (mu_sieve_require): Take ptr to machine as the first arg. * libmu_sieve/runtime.c (mu_sieve_mailbox) (mu_sieve_message): Refuse to run if the machine is in error state. * sieve/sieve.c: Update. * sieve/tests/i-numeric.at: Update expected error message. * libmailutils/diag/diag.c (mu_diag_at_locus): Don't pass locus if mu_file is NULL. * libmu_auth/ldap.c (_mu_entry_to_auth_data): Remove leftover mu_error.
Diffstat (limited to 'libmu_sieve/sieve.y')
-rw-r--r--libmu_sieve/sieve.y1071
1 files changed, 882 insertions, 189 deletions
diff --git a/libmu_sieve/sieve.y b/libmu_sieve/sieve.y
index ce6fa4d18..66fa48522 100644
--- a/libmu_sieve/sieve.y
+++ b/libmu_sieve/sieve.y
@@ -24,220 +24,256 @@
#include <stdlib.h>
#include <assert.h>
#include <sieve-priv.h>
+#include <mailutils/stdstream.h>
mu_sieve_machine_t mu_sieve_machine;
int mu_sieve_error_count;
-
-static void branch_fixup (size_t start, size_t end);
+static struct mu_sieve_node *sieve_tree;
+
+static struct mu_sieve_node *node_alloc (enum mu_sieve_node_type,
+ struct mu_locus_range *);
+static void cond_join (struct mu_sieve_node *node);
+
+#define YYLLOC_DEFAULT(Current, Rhs, N) \
+ do \
+ { \
+ if (N) \
+ { \
+ (Current).beg = YYRHSLOC(Rhs, 1).beg; \
+ (Current).end = YYRHSLOC(Rhs, N).end; \
+ } \
+ else \
+ { \
+ (Current).beg = YYRHSLOC(Rhs, 0).end; \
+ (Current).end = (Current).beg; \
+ } \
+ } while (0)
+
+#define LOCUS_EQ(a,b) \
+ ((((a)->mu_file == (b)->mu_file) \
+ || ((a)->mu_file && (b)->mu_file \
+ && strcmp((a)->mu_file, (b)->mu_file) == 0)) \
+ && (a)->mu_line == (b)->mu_line)
+
+#define YY_LOCATION_PRINT(File, Loc) \
+ do \
+ { \
+ if (LOCUS_EQ(&(Loc).beg, &(Loc).end)) \
+ fprintf(File, "%s:%u.%u-%u.%u", \
+ (Loc).beg.mu_file, \
+ (Loc).beg.mu_line, (Loc).beg.mu_col, \
+ (Loc).end.mu_line, (Loc).end.mu_col); \
+ else \
+ fprintf(File, "%s:%u.%u-%s:%u.%u", \
+ (Loc).beg.mu_file, \
+ (Loc).beg.mu_line, (Loc).beg.mu_col, \
+ (Loc).end.mu_file, \
+ (Loc).end.mu_line, (Loc).end.mu_col); \
+ } \
+ while (0)
%}
+%error-verbose
+%locations
+
%union {
char *string;
size_t number;
- sieve_instr_t instr;
mu_sieve_value_t *value;
mu_list_t list;
- size_t pc;
- struct {
- size_t start;
- size_t end;
- } pclist;
- struct {
+ struct
+ {
char *ident;
mu_list_t args;
} command;
- struct {
- size_t begin;
- size_t cond;
- size_t branch;
- } branch;
+ struct node_list
+ {
+ struct mu_sieve_node *head, *tail;
+ } node_list;
+ struct mu_sieve_node *node;
}
%token <string> IDENT TAG
%token <number> NUMBER
%token <string> STRING MULTILINE
-%token REQUIRE IF ELSIF ELSE ANYOF ALLOF NOT
+%token REQUIRE IF ELSIF ELSE ANYOF ALLOF NOT FALSE TRUE
%type <value> arg
%type <list> slist stringlist stringorlist arglist maybe_arglist
%type <command> command
-%type <pclist> testlist
-%type <pc> action test statement list elsif else cond begin if block
-%type <branch> elsif_branch maybe_elsif else_part
+%type <node> action test statement block cond
+%type <node> else_part
+%type <node_list> list testlist elsif_branch maybe_elsif
%%
input : /* empty */
+ {
+ sieve_tree = NULL;
+ }
| list
- { /* to placate bison */ }
+ {
+ sieve_tree = $1.head;
+ }
;
list : statement
+ {
+ $$.head = $$.tail = $1;
+ }
| list statement
+ {
+ if ($2)
+ {
+ $2->prev = $1.tail;
+ if ($1.tail)
+ $1.tail->next = $2;
+ else
+ $1.head = $2;
+ $1.tail = $2;
+ }
+ $$ = $1;
+ }
;
statement : REQUIRE stringorlist ';'
{
- mu_sieve_require ($2);
+ mu_sieve_require (mu_sieve_machine, $2);
/* All the items in $2 are registered in memory_pool,
so we don't free them */
mu_list_destroy (&$2);
- $$ = mu_sieve_machine->pc;
+ $$ = NULL;
}
| action ';'
/* 1 2 3 4 */
- | if cond block else_part
+ | IF cond block else_part
{
- mu_sieve_machine->prog[$2].pc = $4.begin - $2 - 1;
- if ($4.branch)
- branch_fixup ($4.branch, mu_sieve_machine->pc);
+ $$ = node_alloc (mu_sieve_node_cond, &@1);
+ $$->v.cond.expr = $2;
+ $$->v.cond.iftrue = $3;
+ $$->v.cond.iffalse = $4;
}
;
-if : IF
- {
- $$ = mu_sieve_machine->pc;
- }
- ;
-
else_part : maybe_elsif
{
- if ($1.begin)
- mu_sieve_machine->prog[$1.cond].pc =
- mu_sieve_machine->pc - $1.cond - 1;
- else
- {
- $$.begin = mu_sieve_machine->pc;
- $$.branch = 0;
- }
+ cond_join ($1.head);
+ $$ = $1.head;
}
- | maybe_elsif else block
+ | maybe_elsif ELSE block
{
- if ($1.begin)
+ $3->prev = $1.tail;
+ if ($1.head)
{
- mu_sieve_machine->prog[$1.cond].pc = $3 - $1.cond - 1;
- mu_sieve_machine->prog[$2].pc = $1.branch;
- $$.begin = $1.begin;
- $$.branch = $2;
+ $1.tail->next = $3;
+ $1.tail = $3;
+ cond_join ($1.head);
+ $$ = $1.head;
}
else
- {
- $$.begin = $3;
- $$.branch = $2;
- }
+ $$ = $3;
}
;
maybe_elsif : /* empty */
{
- $$.begin = 0;
+ $$.head = $$.tail = NULL;
}
| elsif_branch
;
-elsif_branch : elsif begin cond block
+elsif_branch : ELSIF cond block
{
- $$.begin = $2;
- $$.branch = $1;
- $$.cond = $3;
+ struct mu_sieve_node *node =
+ node_alloc (mu_sieve_node_cond, &@1);
+ node->v.cond.expr = $2;
+ node->v.cond.iftrue = $3;
+ node->v.cond.iffalse = NULL;
+ $$.head = $$.tail = node;
}
- | elsif_branch elsif begin cond block
+ | elsif_branch ELSIF cond block
{
- mu_sieve_machine->prog[$1.cond].pc = $3 - $1.cond - 1;
- mu_sieve_machine->prog[$2].pc = $1.branch;
- $$.begin = $1.begin;
- $$.branch = $2;
- $$.cond = $4;
- }
- ;
-
-elsif : ELSIF
- {
- mu_sv_code_instr (_mu_sv_instr_branch);
- $$ = mu_sieve_machine->pc;
- mu_sv_code_number (0);
- }
- ;
-
-else : ELSE
- {
- mu_sv_code_instr (_mu_sv_instr_branch);
- $$ = mu_sieve_machine->pc;
- mu_sv_code_number (0);
+ struct mu_sieve_node *node =
+ node_alloc (mu_sieve_node_cond, &@2);
+ node->v.cond.expr = $3;
+ node->v.cond.iftrue = $4;
+ node->v.cond.iffalse = NULL;
+
+ node->prev = $1.tail;
+ $1.tail->next = node;
+ $1.tail = node;
+ $$ = $1;
}
;
block : '{' list '}'
{
- $$ = $2;
+ $$ = $2.head;
}
;
-testlist : cond_expr
- {
- $$.start = $$.end = mu_sieve_machine->pc;
- if (mu_sv_code_instr (_mu_sv_instr_brz)
- || mu_sv_code_number (0))
- YYERROR;
- }
- | testlist ',' cond_expr
+testlist : cond
{
- mu_sieve_machine->prog[$1.end+1].pc = mu_sieve_machine->pc;
- $1.end = mu_sieve_machine->pc;
- if (mu_sv_code_instr (_mu_sv_instr_brz)
- || mu_sv_code_number (0))
- YYERROR;
- $$ = $1;
+ $$.head = $$.tail = $1;
}
- ;
-
-cond : cond_expr
+ | testlist ',' cond
{
- mu_sv_code_instr (_mu_sv_instr_brz);
- $$ = mu_sieve_machine->pc;
- mu_sv_code_number (0);
+ $3->prev = $1.tail;
+ $1.tail->next = $3;
+ $1.tail = $3;
}
;
-cond_expr : test
- { /* to placate bison */ }
+cond : test
| ANYOF '(' testlist ')'
{
- mu_sv_code_anyof ($3.start);
+ $$ = node_alloc (mu_sieve_node_anyof, &@1);
+ $$->v.node = $3.head;
}
| ALLOF '(' testlist ')'
{
- mu_sv_code_allof ($3.start);
+ $$ = node_alloc (mu_sieve_node_allof, &@1);
+ $$->v.node = $3.head;
}
- | NOT cond_expr
+ | NOT cond
{
- if (mu_sv_code_instr (_mu_sv_instr_not))
- YYERROR;
+ $$ = node_alloc (mu_sieve_node_not, &@1);
+ $$->v.node = $2;
}
;
-begin : /* empty */
- {
- $$ = mu_sieve_machine->pc;
- }
- ;
-
test : command
{
- mu_sieve_register_t *reg =
- mu_sieve_test_lookup (mu_sieve_machine, $1.ident);
- $$ = mu_sieve_machine->pc;
+ mu_sieve_register_t *reg;
+ mu_sieve_machine->locus = @1.beg;
+ reg = mu_sieve_test_lookup (mu_sieve_machine, $1.ident);
if (!reg)
- mu_sv_compile_error (&mu_sieve_locus,
- _("unknown test: %s"),
- $1.ident);
+ {
+ mu_diag_at_locus (MU_LOG_ERROR, &@1.beg,
+ _("unknown test: %s"),
+ $1.ident);
+ mu_i_sv_error (mu_sieve_machine);
+ }
else if (!reg->required)
- mu_sv_compile_error (&mu_sieve_locus,
- _("test `%s' has not been required"),
- $1.ident);
- else if (mu_sv_code_test (reg, $1.args))
- YYERROR;
+ {
+ mu_diag_at_locus (MU_LOG_ERROR, &@1.beg,
+ _("test `%s' has not been required"),
+ $1.ident);
+ mu_i_sv_error (mu_sieve_machine);
+ }
+
+ $$ = node_alloc (mu_sieve_node_test, &@1);
+ $$->v.command.reg = reg;
+ $$->v.command.arg = $1.args;
+ }
+ | TRUE
+ {
+ $$ = node_alloc (mu_sieve_node_true, &@1);
+ }
+ | FALSE
+ {
+ $$ = node_alloc (mu_sieve_node_false, &@1);
}
;
@@ -250,20 +286,29 @@ command : IDENT maybe_arglist
action : command
{
- mu_sieve_register_t *reg =
- mu_sieve_action_lookup (mu_sieve_machine, $1.ident);
+ mu_sieve_register_t *reg;
+
+ mu_sieve_machine->locus = @1.beg;
+ reg = mu_sieve_action_lookup (mu_sieve_machine, $1.ident);
- $$ = mu_sieve_machine->pc;
if (!reg)
- mu_sv_compile_error (&mu_sieve_locus,
- _("unknown action: %s"),
- $1.ident);
+ {
+ mu_diag_at_locus (MU_LOG_ERROR, &@1.beg,
+ _("unknown action: %s"),
+ $1.ident);
+ mu_i_sv_error (mu_sieve_machine);
+ }
else if (!reg->required)
- mu_sv_compile_error (&mu_sieve_locus,
- _("action `%s' has not been required"),
- $1.ident);
- else if (mu_sv_code_action (reg, $1.args))
- YYERROR;
+ {
+ mu_diag_at_locus (MU_LOG_ERROR, &@1.beg,
+ _("action `%s' has not been required"),
+ $1.ident);
+ mu_i_sv_error (mu_sieve_machine);
+ }
+
+ $$ = node_alloc(mu_sieve_node_action, &@1);
+ $$->v.command.reg = reg;
+ $$->v.command.arg = $1.args;
}
;
@@ -287,7 +332,7 @@ arglist : arg
;
arg : stringlist
- {
+ {
$$ = mu_sieve_value_create (SVT_STRING_LIST, $1);
}
| STRING
@@ -339,10 +384,604 @@ slist : STRING
int
yyerror (const char *s)
{
- mu_sv_compile_error (&mu_sieve_locus, "%s", s);
+ extern struct mu_locus mu_sieve_locus;
+
+ mu_sieve_machine->locus = mu_sieve_locus;
+ mu_diag_at_locus (MU_LOG_ERROR, &mu_sieve_locus, "%s", s);
+ mu_i_sv_error (mu_sieve_machine);
+ return 0;
+}
+
+static void
+cond_join (struct mu_sieve_node *node)
+{
+ while (node)
+ {
+ struct mu_sieve_node *next = node->next;
+ node->prev = node->next = NULL;
+ node->v.cond.iffalse = next;
+ node = next;
+ }
+}
+
+static struct mu_sieve_node *
+node_alloc (enum mu_sieve_node_type type, struct mu_locus_range *lr)
+{
+ struct mu_sieve_node *node = malloc (sizeof (*node));
+ if (node)
+ {
+ node->prev = node->next = NULL;
+ node->type = type;
+ node->locus = *lr;
+ }
+ return node;
+}
+
+static void node_optimize (struct mu_sieve_node *node);
+static void node_free (struct mu_sieve_node *node);
+static void node_replace (struct mu_sieve_node *node,
+ struct mu_sieve_node *repl);
+static int node_code (struct mu_sieve_machine *mach,
+ struct mu_sieve_node *node);
+static void node_dump (mu_stream_t str, struct mu_sieve_node *node,
+ unsigned level);
+
+static void tree_free (struct mu_sieve_node **tree);
+static void tree_optimize (struct mu_sieve_node *tree);
+static int tree_code (struct mu_sieve_machine *mach,
+ struct mu_sieve_node *tree);
+static void tree_dump (mu_stream_t str, struct mu_sieve_node *tree, unsigned level);
+
+static void
+indent (mu_stream_t str, unsigned level)
+{
+#define tab " "
+#define tablen (sizeof (tab) - 1)
+ while (level--)
+ mu_stream_write (str, tab, tablen, NULL);
+}
+
+/* mu_sieve_node_noop */
+static void
+dump_node_noop (mu_stream_t str, struct mu_sieve_node *node, unsigned level)
+{
+ indent (str, level);
+ mu_stream_printf (str, "NOOP\n");
+}
+
+/* mu_sieve_node_false */
+static void
+dump_node_false (mu_stream_t str, struct mu_sieve_node *node, unsigned level)
+{
+ indent (str, level);
+ mu_stream_printf (str, "FALSE\n");
+}
+
+/* mu_sieve_node_true */
+static void
+dump_node_true (mu_stream_t str, struct mu_sieve_node *node, unsigned level)
+{
+ indent (str, level);
+ mu_stream_printf (str, "TRUE\n");
+}
+
+/* mu_sieve_node_test & mu_sieve_node_action */
+static void
+free_node_command (struct mu_sieve_node *node)
+{
+ mu_list_destroy (&node->v.command.arg);
+}
+
+static int
+code_node_test (struct mu_sieve_machine *mach, struct mu_sieve_node *node)
+{
+ return mu_i_sv_code_test (mach, node->v.command.reg, node->v.command.arg);
+}
+
+static int
+code_node_action (struct mu_sieve_machine *mach, struct mu_sieve_node *node)
+{
+ return mu_i_sv_code_action (mach, node->v.command.reg, node->v.command.arg);
+}
+
+struct string_dumper_data
+{
+ int init;
+ mu_stream_t stream;
+};
+
+static int
+string_dumper (void *item, void *data)
+{
+ struct string_dumper_data *dp = data;
+ if (dp->init == 0)
+ dp->init = 1;
+ else
+ mu_stream_printf (dp->stream, ", ");
+ mu_stream_printf (dp->stream, "\"%s\"", (char*)item);
+ return 0;
+}
+
+static int
+dump_val (void *item, void *data)
+{
+ mu_sieve_value_t *val = item;
+ mu_stream_t str = data;
+
+ mu_stream_printf (str, " ");
+ switch (val->type)
+ {
+ case SVT_VOID:
+ mu_stream_printf (str, "(void)");
+ break;
+
+ case SVT_NUMBER:
+ mu_stream_printf (str, "%zu", val->v.number);
+ break;
+
+ case SVT_STRING:
+ mu_stream_printf (str, "\"%s\"", val->v.string);
+ break;
+
+ case SVT_STRING_LIST:
+ {
+ struct string_dumper_data d;
+ d.init = 0;
+ d.stream = str;
+ mu_stream_printf (str, "[");
+ mu_list_foreach (val->v.list, string_dumper, &d);
+ mu_stream_printf (str, "]");
+ }
+ break;
+
+ case SVT_TAG:
+ mu_stream_printf (str, ":%s", val->v.string);
+ break;
+
+ case SVT_IDENT:
+ mu_stream_printf (str, "%s", val->v.string);
+ break;
+
+ case SVT_VALUE_LIST:
+ mu_stream_printf (str, "[");
+ mu_list_foreach (val->v.list, dump_val, str);
+ mu_stream_printf (str, "]");
+ break;
+
+ case SVT_POINTER:
+ mu_stream_printf (str, "%p", val->v.ptr);
+ break;
+
+ default:
+ abort ();
+ }
+ return 0;
+}
+
+static void
+dump_node_command (mu_stream_t str, struct mu_sieve_node *node, unsigned level)
+{
+ indent (str, level);
+ mu_stream_printf (str, "COMMAND %s", node->v.command.reg->name);
+ mu_list_foreach (node->v.command.arg, dump_val, str);
+ mu_stream_printf (str, "\n");
+}
+
+/* mu_sieve_node_cond */
+static void
+free_node_cond (struct mu_sieve_node *node)
+{
+ tree_free (&node->v.cond.expr);
+ tree_free (&node->v.cond.iftrue);
+ tree_free (&node->v.cond.iffalse);
+}
+
+static void
+optimize_node_cond (struct mu_sieve_node *node)
+{
+ tree_optimize (node->v.cond.expr);
+ switch (node->v.cond.expr->type)
+ {
+ case mu_sieve_node_true:
+ tree_optimize (node->v.cond.iftrue);
+ node_replace (node, node->v.cond.iftrue);
+ break;
+
+ case mu_sieve_node_false:
+ tree_optimize (node->v.cond.iffalse);
+ node_replace (node, node->v.cond.iffalse);
+ break;
+
+ default:
+ tree_optimize (node->v.cond.iftrue);
+ tree_optimize (node->v.cond.iffalse);
+ }
+}
+
+static int
+code_node_cond (struct mu_sieve_machine *mach, struct mu_sieve_node *node)
+{
+ size_t br1;
+
+ tree_code (mach, node->v.cond.expr);
+ mu_i_sv_code (mach, (sieve_op_t) _mu_sv_instr_brz);
+ br1 = mach->pc;
+ mu_i_sv_code (mach, (sieve_op_t) 0);
+ tree_code (mach, node->v.cond.iftrue);
+
+ if (node->v.cond.iffalse)
+ {
+ size_t br2;
+
+ mu_i_sv_code (mach, (sieve_op_t) _mu_sv_instr_branch);
+ br2 = mach->pc;
+ mu_i_sv_code (mach, (sieve_op_t) 0);
+
+ mach->prog[br1].pc = mach->pc - br1 - 1;
+
+ tree_code (mach, node->v.cond.iffalse);
+ mach->prog[br2].pc = mach->pc - br2 - 1;
+ }
+ else
+ mach->prog[br1].pc = mach->pc - br1 - 1;
+ return 0;
+}
+
+static void
+dump_node_cond (mu_stream_t str, struct mu_sieve_node *node, unsigned level)
+{
+ indent (str, level);
+ mu_stream_printf (str, "COND\n");
+
+ ++level;
+
+ indent (str, level);
+ mu_stream_printf (str, "EXPR:\n");
+ tree_dump (str, node->v.cond.expr, level + 1);
+
+ indent (str, level);
+ mu_stream_printf (str, "IFTRUE:\n");
+ tree_dump (str, node->v.cond.iftrue, level + 1);
+
+ indent (str, level);
+ mu_stream_printf (str, "IFFALSE:\n");
+ tree_dump (str, node->v.cond.iffalse, level + 1);
+}
+
+/* mu_sieve_node_anyof & mu_sieve_node_allof */
+static void
+free_node_x_of (struct mu_sieve_node *node)
+{
+ tree_free (&node->v.node);
+}
+
+static void
+optimize_x_of (struct mu_sieve_node *node, enum mu_sieve_node_type solve)
+{
+ struct mu_sieve_node *cur;
+ tree_optimize (node->v.node);
+ cur = node->v.node;
+ while (cur)
+ {
+ struct mu_sieve_node *next = cur->next;
+ switch (cur->type)
+ {
+ case mu_sieve_node_false:
+ case mu_sieve_node_true:
+ if (cur->type == solve)
+ {
+ tree_free (&node->v.node);
+ node->type = solve;
+ return;
+ }
+ else
+ {
+ if (cur->prev)
+ cur->prev->next = next;
+ else
+ node->v.node = next;
+ if (next)
+ next->prev = cur->prev;
+ node_free (cur);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ cur = next;
+ }
+
+ if (!node->v.node)
+ node->type = solve == mu_sieve_node_false ? mu_sieve_node_true : mu_sieve_node_false;
+}
+
+static int
+code_node_x_of (struct mu_sieve_machine *mach, struct mu_sieve_node *node,
+ sieve_op_t op)
+{
+ struct mu_sieve_node *cur = node->v.node;
+ size_t pc = 0;
+ size_t end;
+
+ while (cur)
+ {
+ node_code (mach, cur);
+ if (cur->next)
+ {
+ mu_i_sv_code (mach, op);
+ mu_i_sv_code (mach, (sieve_op_t) pc);
+ pc = mach->pc - 1;
+ }
+ cur = cur->next;
+ }
+
+ /* Fix-up locations */
+ end = mach->pc;
+ while (pc != 0)
+ {
+ size_t prev = mach->prog[pc].pc;
+ mach->prog[pc].pc = end - pc - 1;
+ pc = prev;
+ }
+
+ return 0;
+}
+
+static void
+dump_node_x_of (mu_stream_t str, struct mu_sieve_node *node, unsigned level)
+{
+ indent (str, level);
+ mu_stream_printf (str, "%s:\n",
+ node->type == mu_sieve_node_allof ? "ALLOF" : "ANYOF");
+
+ ++level;
+ node = node->v.node;
+ while (node)
+ {
+ node_dump (str, node, level + 1);
+ node = node->next;
+ if (node)
+ {
+ indent (str, level);
+ mu_stream_printf (str, "%s:\n",
+ node->type == mu_sieve_node_allof ? "AND" : "OR");
+ }
+ }
+}
+
+/* mu_sieve_node_anyof */
+static void
+optimize_node_anyof (struct mu_sieve_node *node)
+{
+ optimize_x_of (node, mu_sieve_node_true);
+}
+
+static int
+code_node_anyof (struct mu_sieve_machine *mach, struct mu_sieve_node *node)
+{
+ return code_node_x_of (mach, node, (sieve_op_t) _mu_sv_instr_brnz);
+}
+
+/* mu_sieve_node_allof */
+static void
+optimize_node_allof (struct mu_sieve_node *node)
+{
+ return optimize_x_of (node, mu_sieve_node_false);
+}
+
+static int
+code_node_allof (struct mu_sieve_machine *mach, struct mu_sieve_node *node)
+{
+ return code_node_x_of (mach, node, (sieve_op_t) _mu_sv_instr_brz);
+}
+
+/* mu_sieve_node_not */
+static void
+free_node_not (struct mu_sieve_node *node)
+{
+ tree_free (&node->v.node);
+}
+
+static void
+optimize_node_not (struct mu_sieve_node *node)
+{
+ tree_optimize (node->v.node);
+ switch (node->v.node->type)
+ {
+ case mu_sieve_node_false:
+ tree_free (&node->v.node);
+ node->type = mu_sieve_node_true;
+ break;
+
+ case mu_sieve_node_true:
+ tree_free (&node->v.node);
+ node->type = mu_sieve_node_false;
+ break;
+
+ default:
+ break;
+ }
+}
+
+static int
+code_node_not (struct mu_sieve_machine *mach, struct mu_sieve_node *node)
+{
+ node_code (mach, node->v.node);
+ return mu_i_sv_code (mach, (sieve_op_t) _mu_sv_instr_not);
+}
+
+static void
+dump_node_not (mu_stream_t str, struct mu_sieve_node *node, unsigned level)
+{
+ indent (str, level);
+ mu_stream_printf (str, "NOT\n");
+ node_dump (str, node->v.node, level + 1);
+}
+
+struct node_descr
+{
+ int (*code_fn) (struct mu_sieve_machine *mach, struct mu_sieve_node *node);
+ void (*optimize_fn) (struct mu_sieve_node *node);
+ void (*free_fn) (struct mu_sieve_node *node);
+ void (*dump_fn) (mu_stream_t str, struct mu_sieve_node *node, unsigned level);
+
+};
+
+static struct node_descr node_descr[] = {
+ [mu_sieve_node_noop] = { NULL, NULL, NULL, dump_node_noop },
+ [mu_sieve_node_false] = { NULL, NULL, NULL, dump_node_false },
+ [mu_sieve_node_true] = { NULL, NULL, NULL, dump_node_true },
+ [mu_sieve_node_test] = { code_node_test, NULL,
+ free_node_command, dump_node_command },
+ [mu_sieve_node_action] = { code_node_action, NULL,
+ free_node_command, dump_node_command },
+ [mu_sieve_node_cond] = { code_node_cond, optimize_node_cond,
+ free_node_cond, dump_node_cond },
+ [mu_sieve_node_anyof] = { code_node_anyof, optimize_node_anyof,
+ free_node_x_of, dump_node_x_of },
+ [mu_sieve_node_allof] = { code_node_allof, optimize_node_allof,
+ free_node_x_of, dump_node_x_of },
+ [mu_sieve_node_not] = { code_node_not, optimize_node_not,
+ free_node_not, dump_node_not },
+};
+
+static void
+node_optimize (struct mu_sieve_node *node)
+{
+ if ((int)node->type >= MU_ARRAY_SIZE (node_descr))
+ abort ();
+ if (node_descr[node->type].optimize_fn)
+ node_descr[node->type].optimize_fn (node);
+}
+
+static void
+node_free (struct mu_sieve_node *node)
+{
+ if ((int)node->type >= MU_ARRAY_SIZE (node_descr))
+ abort ();
+ if (node_descr[node->type].free_fn)
+ node_descr[node->type].free_fn (node);
+ free (node);
+}
+
+static void
+node_replace (struct mu_sieve_node *node, struct mu_sieve_node *repl)
+{
+ struct mu_sieve_node copy;
+
+ if ((int)node->type >= MU_ARRAY_SIZE (node_descr))
+ abort ();
+
+ copy = *node;
+ if (repl)
+ {
+ node->type = repl->type;
+ node->v = repl->v;
+
+ switch (copy.type)
+ {
+ case mu_sieve_node_cond:
+ if (repl == copy.v.cond.expr)
+ copy.v.cond.expr = NULL;
+ else if (repl == copy.v.cond.iftrue)
+ copy.v.cond.iftrue = NULL;
+ else if (repl == copy.v.cond.iffalse)
+ copy.v.cond.iffalse = NULL;
+ break;
+
+ case mu_sieve_node_not:
+ if (repl == copy.v.node)
+ copy.v.node = NULL;
+ break;
+
+ default:
+ break;
+ }
+ }
+ else
+ node->type = mu_sieve_node_noop;
+
+ if (node_descr[node->type].free_fn)
+ node_descr[node->type].free_fn (&copy);
+}
+
+static int
+node_code (struct mu_sieve_machine *mach, struct mu_sieve_node *node)
+{
+ if ((int)node->type >= MU_ARRAY_SIZE (node_descr))
+ abort ();
+
+ if (!node_descr[node->type].code_fn)
+ return 0;
+
+ if (mu_i_sv_locus (mach, &node->locus))
+ return 1;
+
+ return node_descr[node->type].code_fn (mach, node);
+}
+
+static void
+node_dump (mu_stream_t str, struct mu_sieve_node *node, unsigned level)
+{
+ if ((int)node->type >= MU_ARRAY_SIZE (node_descr)
+ || !node_descr[node->type].dump_fn)
+ abort ();
+ node_descr[node->type].dump_fn (str, node, level);
+}
+
+
+static void
+tree_free (struct mu_sieve_node **tree)
+{
+ struct mu_sieve_node *cur = *tree;
+ while (cur)
+ {
+ struct mu_sieve_node *next = cur->next;
+ node_free (cur);
+ cur = next;
+ }
+}
+
+static void
+tree_optimize (struct mu_sieve_node *tree)
+{
+ while (tree)
+ {
+ node_optimize (tree);
+ tree = tree->next;
+ }
+}
+
+static int
+tree_code (struct mu_sieve_machine *mach, struct mu_sieve_node *tree)
+{
+ while (tree)
+ {
+ if (node_code (mach, tree))
+ return 1;
+ tree = tree->next;
+ }
return 0;
}
+static void
+tree_dump (mu_stream_t str, struct mu_sieve_node *tree, unsigned level)
+{
+ while (tree)
+ {
+ node_dump (str, tree, level);
+ tree = tree->next;
+ }
+}
+
+void
+mu_i_sv_error (mu_sieve_machine_t mach)
+{
+ mach->state = mu_sieve_state_error;
+}
+
int
mu_sieve_machine_init_ex (mu_sieve_machine_t *pmach,
void *data, mu_stream_t errstream)
@@ -362,6 +1001,16 @@ mu_sieve_machine_init_ex (mu_sieve_machine_t *pmach,
return rc;
}
+ rc = mu_opool_create (&mach->string_pool, MU_OPOOL_DEFAULT);
+ if (rc)
+ {
+ mu_list_destroy (&mach->memory_pool);
+ free (mach);
+ return rc;
+ }
+
+ mach->source_list = NULL;
+
mach->data = data;
mach->errstream = errstream;
mu_stream_ref (errstream);
@@ -417,6 +1066,7 @@ mu_sieve_machine_dup (mu_sieve_machine_t const in, mu_sieve_machine_t *out)
mach->progsize = in->progsize;
mach->prog = in->prog;
+ mach->state = in->state;
mach->pc = 0;
mach->reg = 0;
mach->stack = NULL;
@@ -571,97 +1221,140 @@ mu_sieve_machine_destroy (mu_sieve_machine_t *pmach)
mu_list_destroy (&mach->test_list);
mu_list_destroy (&mach->comp_list);
mu_list_destroy (&mach->source_list);
+ mu_opool_destroy (&mach->string_pool);
mu_sieve_slist_destroy (&mach->memory_pool);
free (mach);
*pmach = NULL;
}
-static int
-string_comp (const void *item, const void *value)
-{
- return strcmp (item, value);
-}
-
-void
-mu_sieve_machine_begin (mu_sieve_machine_t mach, const char *file)
+static void
+sieve_machine_begin (mu_sieve_machine_t mach, const char *file)
{
- mu_sieve_machine = mach;
- mu_sieve_error_count = 0;
- mu_sv_code_instr (NULL);
-
- mu_list_create (&mach->source_list);
- mu_list_set_comparator (mach->source_list, string_comp);
-
mu_sv_register_standard_actions (mach);
mu_sv_register_standard_tests (mach);
mu_sv_register_standard_comparators (mach);
+ mu_sieve_machine = mach;
}
-void
-mu_sieve_machine_finish (mu_sieve_machine_t mach)
+static void
+sieve_machine_finish (void)
{
- mu_sv_code_instr (NULL);
+ //nothing
}
int
-mu_sieve_compile (mu_sieve_machine_t mach, const char *name)
+with_machine (mu_sieve_machine_t mach, char const *name,
+ int (*thunk) (void *), void *data)
{
int rc = 0;
+ mu_stream_t save_errstr = mu_strerr;
- mu_sieve_machine_begin (mach, name);
+ mu_stream_ref (save_errstr);
+ mu_strerr = mach->errstream;
+ mu_stream_ref (mu_strerr);
+
+ sieve_machine_begin (mach, name);
+
+ rc = thunk (data);
+
+ sieve_machine_finish ();
+
+ mu_stream_unref (save_errstr);
+ mu_strerr = save_errstr;
+ mu_stream_unref (mu_strerr);
- if (mu_sv_lex_begin (name) == 0)
- {
- if (yyparse () || mu_sieve_error_count)
- rc = MU_ERR_PARSE;
- mu_sv_lex_finish ();
- }
- else
- rc = MU_ERR_FAILURE;
-
- mu_sieve_machine_finish (mach);
return rc;
}
-int
-mu_sieve_compile_buffer (mu_sieve_machine_t mach,
- const char *buf, int bufsize,
- const char *fname, int line)
+static int
+sieve_parse (void)
{
int rc;
-
- mu_sieve_machine_begin (mach, fname);
- if (mu_sv_lex_begin_string (buf, bufsize, fname, line) == 0)
+ sieve_tree = NULL;
+ yydebug = mu_debug_level_p (mu_sieve_debug_handle, MU_DEBUG_TRACE3);
+
+ rc = yyparse ();
+ mu_i_sv_lex_finish (mu_sieve_machine);
+ if (rc)
+ mu_i_sv_error (mu_sieve_machine);
+ if (mu_sieve_machine->state == mu_sieve_state_init)
{
- rc = yyparse ();
- if (mu_sieve_error_count)
- rc = 1;
- mu_sv_lex_finish ();
+ if (mu_debug_level_p (mu_sieve_debug_handle, MU_DEBUG_TRACE1))
+ {
+ mu_error (_("Unoptimized parse tree"));
+ tree_dump (mu_strerr, sieve_tree, 0);
+ }
+ tree_optimize (sieve_tree);
+ if (mu_debug_level_p (mu_sieve_debug_handle, MU_DEBUG_TRACE2))
+ {
+ mu_error (_("Optimized parse tree"));
+ tree_dump (mu_strerr, sieve_tree, 0);
+ }
+ mu_i_sv_code (mu_sieve_machine, (sieve_op_t) 0);
+ rc = tree_code (mu_sieve_machine, s