diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2016-11-23 13:26:23 +0200 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2016-11-23 13:44:34 +0200 |
commit | 15404d914aec7e80c884fc63dbfa5d3e347b32cb (patch) | |
tree | 5b3c17759d19c8a02ab1bafb722e0c00f5f28e2f /libmu_sieve/sieve.y | |
parent | a04c6feb5007aeeb133b15558f13733743dee3e8 (diff) | |
download | mailutils-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.y | 1071 |
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 (©); +} + +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 |