aboutsummaryrefslogtreecommitdiff
path: root/mfd/drivers.c
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2008-02-10 14:08:36 +0000
committerSergey Poznyakoff <gray@gnu.org.ua>2008-02-10 14:08:36 +0000
commita1871918823166cac9f0c12164ba27cb3b1f0c2c (patch)
tree7260d118aa5af6a8f006f9b2dac6cabde782db8c /mfd/drivers.c
parent455b645247bdc0f01239b2352a9d8f07f446024f (diff)
downloadmailfromd-a1871918823166cac9f0c12164ba27cb3b1f0c2c.tar.gz
mailfromd-a1871918823166cac9f0c12164ba27cb3b1f0c2c.tar.bz2
Merged HEAD from branches/gmach
git-svn-id: file:///svnroot/mailfromd/trunk@1612 7a8a7f39-df28-0410-adc6-e0d955640f24
Diffstat (limited to 'mfd/drivers.c')
-rw-r--r--mfd/drivers.c2308
1 files changed, 2308 insertions, 0 deletions
diff --git a/mfd/drivers.c b/mfd/drivers.c
new file mode 100644
index 00000000..0d4bb601
--- /dev/null
+++ b/mfd/drivers.c
@@ -0,0 +1,2308 @@
+/* This file is part of Mailfromd.
+ Copyright (C) 2005, 2006, 2007, 2008 Sergey Poznyakoff
+
+ 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 3, 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, see <http://www.gnu.org/licenses/>. */
+
+#define MARK_LOCUS() \
+ do if (!*old_locus || !LOCUS_EQ(*old_locus, &node->locus)) { \
+ struct literal *lit = literal_lookup(node->locus.file); \
+ *old_locus = &node->locus; \
+ code_op(opcode_locus); \
+ code_immediate((void*)lit->off); \
+ code_immediate((void*)node->locus.line); \
+ } while (0)
+
+
+
+void
+code_memref(NODE *node)
+{
+ switch (node->v.var_ref.variable->storage_class) {
+ case storage_extern:
+ code_op(opcode_push);
+ code_immediate((void*)node->v.var_ref.variable->off);
+ break;
+
+ case storage_auto:
+ code_op(opcode_memstk);
+ code_immediate((void*)node->v.var_ref.nframes);
+ code_immediate((void*)-node->v.var_ref.variable->off);
+ break;
+
+ case storage_param:
+ code_op(opcode_memstk);
+ code_immediate((void*)node->v.var_ref.nframes);
+ code_immediate((void*)(node->v.var_ref.variable->off + 2));
+ break;
+ }
+}
+
+
+/* type noop */
+/* Empty node, nothing to print, mark, optimize or code */
+
+
+/* type string */
+
+void
+print_type_string(NODE *node, int level)
+{
+ print_level(level);
+ printf("STRING: \"");
+ print_quoted_string(node->v.literal->text);
+ printf("\"\n");
+}
+
+void
+mark_type_string(NODE *node)
+{
+ node->v.literal->flags |= VAR_REFERENCED;
+}
+
+void
+code_type_string(NODE *node, struct locus **old_locus)
+{
+ MARK_LOCUS();
+ code_op(opcode_push);
+ code_immediate((const void*)node->v.literal->off);
+}
+
+
+/* type symbol */
+
+void
+print_type_symbol(NODE *node, int level)
+{
+ print_level(level);
+ printf("SYMBOL: %s\n", node->v.literal->text);
+}
+
+void
+code_type_symbol(NODE *node, struct locus **old_locus)
+{
+ MARK_LOCUS();
+ code_op(opcode_symbol);
+ code_immediate((void*)node->v.literal->off);
+}
+
+void
+mark_type_symbol(NODE *node)
+{
+ node->v.literal->flags |= VAR_REFERENCED;
+}
+
+
+/* type number */
+void
+print_type_number(NODE *node, int level)
+{
+ print_level(level);
+ printf("NUMBER: %ld\n", node->v.number);
+}
+
+void
+code_type_number(NODE *node, struct locus **old_locus)
+{
+ MARK_LOCUS();
+ code_op(opcode_push);
+ code_immediate((void*)node->v.number);
+}
+
+
+/* type if */
+
+void
+print_type_if(NODE *node, int level)
+{
+ print_level(level);
+ printf("COND: \n");
+ print_node(node->v.cond.cond, level);
+ print_level(level);
+ printf("IFTRUE\n");
+ print_node_list(node->v.cond.if_true, level+1);
+ print_level(level);
+ printf("IFFALSE\n");
+ print_node_list(node->v.cond.if_false, level+1);
+}
+
+void
+mark_type_if(NODE *node)
+{
+ mark(node->v.cond.cond);
+ mark(node->v.cond.if_true);
+ mark(node->v.cond.if_false);
+}
+
+void
+optimize_type_if(NODE *node)
+{
+ NODE *p = node->v.cond.cond;
+ optimize(p);
+ optimize(node->v.cond.if_true);
+ optimize(node->v.cond.if_false);
+ if (p->type == node_type_number) {
+ NODE *head;
+ NODE *tail = node->next;
+ if (p->v.number) {
+ head = node->v.cond.if_true;
+ free_subtree(node->v.cond.if_false);
+ } else {
+ head = node->v.cond.if_false;
+ free_subtree(node->v.cond.if_true);
+ }
+
+ if (head) {
+ *node = *head;
+ free_node(head);
+
+ for (; node->next; node = node->next)
+ ;
+ node->next = tail;
+ } else
+ node->type = node_type_noop;
+ } else if (p->type == node_type_string) {
+ NODE *head;
+ NODE *tail = p->next;
+ if (p->v.literal->text[0]) {
+ head = node->v.cond.if_true;
+ free_subtree(node->v.cond.if_false);
+ } else {
+ head = node->v.cond.if_false;
+ free_subtree(node->v.cond.if_true);
+ }
+
+ if (head) {
+ *node = *head;
+ free_node(head);
+
+ for (; node->next; node = node->next)
+ ;
+ node->next = tail;
+ } else
+ node->type = node_type_noop;
+ }
+}
+
+void
+code_type_if(NODE *node, struct locus **old_locus)
+{
+ prog_counter_t pos1, pos2, endpos;
+
+ code_node(node->v.cond.cond);
+ MARK_LOCUS();
+ code_op(opcode_bz);
+ pos1 = code_immediate(NULL);
+ traverse_tree(node->v.cond.if_true);
+ if (node->v.cond.if_false) {
+ code_op(opcode_jmp);
+ pos2 = code_immediate(NULL);
+ traverse_tree(node->v.cond.if_false);
+ endpos = code_get_counter ();
+ code_put(pos1, (void *)(pos2 - pos1));
+ code_put(pos2, (void *)(endpos - pos2 - 1));
+ } else
+ code_put(pos1, (void *)(code_get_counter () - pos1 - 1));
+}
+
+
+/* type bin */
+
+void
+print_type_bin(NODE *node, int level)
+{
+ print_level(level);
+ print_bin_op(node->v.bin.opcode);
+ if (node->v.bin.opcode == bin_match
+ || node->v.bin.opcode == bin_fnmatch) {
+ if (node->v.bin.qualifier & QUALIFIER_MX)
+ printf(",MX");
+ }
+ printf("\n");
+ print_node(node->v.bin.arg[0], level+1);
+ print_node(node->v.bin.arg[1], level+1);
+}
+
+void
+mark_type_bin(NODE *node)
+{
+ mark(node->v.bin.arg[0]);
+ mark(node->v.bin.arg[1]);
+}
+
+static void
+optimize_arith(NODE *node)
+{
+ NODE *arg0 = node->v.bin.arg[0];
+ NODE *arg1 = node->v.bin.arg[1];
+
+ if (arg0->type == node_type_number
+ && arg1->type == node_type_number) {
+ switch (node->v.bin.opcode) {
+ case bin_add:
+ node->v.number = arg0->v.number + arg1->v.number;
+ break;
+
+ case bin_sub:
+ node->v.number = arg0->v.number - arg1->v.number;
+ break;
+
+ case bin_mul:
+ node->v.number = arg0->v.number * arg1->v.number;
+ break;
+
+ case bin_div:
+ if (arg1->v.number == 0) {
+ parse_error_locus(&node->locus,
+ _("division by zero"));
+ break;
+ }
+ node->v.number = arg0->v.number / arg1->v.number;
+ break;
+
+ case bin_logand:
+ node->v.number = arg0->v.number & arg1->v.number;
+ break;
+
+ case bin_logor:
+ node->v.number = arg0->v.number | arg1->v.number;
+ break;
+
+ case bin_logxor:
+ node->v.number = arg0->v.number ^ arg1->v.number;
+ break;
+
+ default:
+ return;
+ }
+ node->type = node_type_number;
+ free_node(arg0);
+ free_node(arg1);
+ } else if (node_type(arg0) != dtype_number) {
+ parse_error_locus(&arg0->locus,
+ _("left-hand side argument to the "
+ "arithmetical operation is "
+ "of wrong data type"));
+ } else if (node_type(arg1) != dtype_number) {
+ parse_error_locus(&arg1->locus,
+ _("right-hand side argument to the "
+ "arithmetical operation is "
+ "of wrong data type"));
+ } else if (arg0->type == node_type_number) {
+ switch (node->v.bin.opcode) {
+ case bin_add:
+ if (arg0->v.number == 0) {
+ copy_node(node, arg1);
+ free_node(arg0);
+ free(arg1);
+ }
+ break;
+
+ case bin_sub:
+ if (arg0->v.number == 0) {
+ NODE *n = alloc_node(node_type_un,
+ &node->locus);
+ n->v.un.opcode = unary_minus;
+ n->v.un.arg = arg1;
+ copy_node(node, n);
+ free_node(arg0);
+ free(n);
+ }
+ break;
+
+ case bin_mul:
+ if (arg0->v.number == 0) {
+ node->type = node_type_number;
+ node->v.number = 0;
+ free_node(arg0);
+ free_node(arg1);
+ } else if (arg0->v.number == 1) {
+ copy_node(node, arg1);
+ free_node(arg0);
+ }
+ break;
+
+ case bin_div:
+ if (arg0->v.number == 0) {
+ node->type = node_type_number;
+ node->v.number = 0;
+ free_node(arg0);
+ free_node(arg1);
+ }
+ break;
+
+ case bin_logand:
+ if (arg0->v.number == 0) {
+ node->type = node_type_number;
+ node->v.number = 0;
+ free_node(arg0);
+ free_node(arg1);
+ } else if (arg0->v.number == ~(unsigned long)0) {
+ copy_node(node, arg1);
+ free_node(arg0);
+ }
+ break;
+
+ case bin_logor:
+ if (arg0->v.number == 0) {
+ copy_node(node, arg1);
+ free_node(arg0);
+ } else if (arg0->v.number == ~(unsigned long)0) {
+ node->type = node_type_number;
+ node->v.number = ~(unsigned long)0;
+ free_node(arg0);
+ free_node(arg1);
+ }
+ break;
+
+ case bin_logxor:
+ if (arg0->v.number == 0) {
+ copy_node(node, arg1);
+ free_node(arg0);
+ }
+ break;
+
+ default:
+ return;
+ }
+ } else if (arg1->type == node_type_number) {
+ switch (node->v.bin.opcode) {
+ case bin_add:
+ case bin_sub:
+ if (arg1->v.number == 0) {
+ copy_node(node, arg0);
+ free_node(arg1);
+ free(arg0);
+ }
+ break;
+
+ case bin_mul:
+ if (arg1->v.number == 0) {
+ node->type = node_type_number;
+ node->v.number = 0;
+ free_node(arg0);
+ free_node(arg1);
+ } else if (arg1->v.number == 1) {
+ copy_node(node, arg0);
+ free_node(arg1);
+ }
+ break;
+
+ case bin_div:
+ if (arg1->v.number == 0) {
+ parse_error_locus(&node->locus,
+ _("division by zero"));
+ } else if (arg1->v.number == 1) {
+ copy_node(node, arg0);
+ free_node(arg1);
+ }
+ break;
+
+ case bin_logand:
+ if (arg1->v.number == 0) {
+ node->type = node_type_number;
+ node->v.number = 0;
+ free_node(arg0);
+ free_node(arg1);
+ } else if (arg1->v.number == ~(unsigned long)0) {
+ copy_node(node, arg0);
+ free_node(arg1);
+ }
+ break;
+
+ case bin_logor:
+ if (arg1->v.number == 0) {
+ copy_node(node, arg0);
+ free_node(arg1);
+ } else if (arg1->v.number == ~(unsigned long)0) {
+ node->type = node_type_number;
+ node->v.number = ~(unsigned long)0;
+ free_node(arg0);
+ free_node(arg1);
+ }
+ break;
+
+ case bin_logxor:
+ if (arg1->v.number == 0) {
+ copy_node(node, arg0);
+ free_node(arg0);
+ }
+ break;
+
+ default:
+ return;
+ }
+ }
+}
+
+void
+optimize_relational(NODE *node)
+{
+ NODE *arg0 = node->v.bin.arg[0];
+ NODE *arg1 = node->v.bin.arg[1];
+ if (arg0->type == node_type_number
+ && arg1->type == node_type_number) {
+ switch (node->v.bin.opcode) {
+ case bin_and:
+ node->v.number = arg0->v.number && arg1->v.number;
+ break;
+
+ case bin_or:
+ node->v.number = arg0->v.number || arg1->v.number;
+ break;
+
+ case bin_eq:
+ node->v.number = arg0->v.number == arg1->v.number;
+ break;
+
+ case bin_ne:
+ node->v.number = arg0->v.number != arg1->v.number;
+ break;
+
+ case bin_lt:
+ node->v.number = arg0->v.number < arg1->v.number;
+ break;
+
+ case bin_le:
+ node->v.number = arg0->v.number <= arg1->v.number;
+ break;
+
+ case bin_gt:
+ node->v.number = arg0->v.number > arg1->v.number;
+ break;
+
+ case bin_ge:
+ node->v.number = arg0->v.number >= arg1->v.number;
+ break;
+
+ default:
+ return;
+ }
+ } else if (arg0->type == node_type_string
+ && arg1->type == node_type_string) {
+ switch (node->v.bin.opcode) {
+ case bin_and:
+ node->v.number = arg0->v.literal->text[0] != 0
+ && arg1->v.literal->text[0] != 0;
+ break;
+
+ case bin_or:
+ node->v.number = arg0->v.literal->text[0] != 0
+ || arg1->v.literal->text[0] != 0;
+ break;
+
+ case bin_eq:
+ /* NOTE: This case and the one below make use of the
+ fact that no two entries in the symbol table can
+ contain lexicographically equal literals */
+ node->v.number = arg0->v.literal == arg1->v.literal;
+ break;
+
+ case bin_ne:
+ node->v.number = arg0->v.literal != arg1->v.literal;
+ break;
+
+ case bin_lt:
+ node->v.number = strcmp(arg0->v.literal->text,
+ arg1->v.literal->text) < 0;
+ break;
+
+ case bin_le:
+ node->v.number = strcmp(arg0->v.literal->text,
+ arg1->v.literal->text) <= 0;
+ break;
+
+ case bin_gt:
+ node->v.number = strcmp(arg0->v.literal->text,
+ arg1->v.literal->text) > 0;
+ break;
+
+ case bin_ge:
+ node->v.number = strcmp(arg0->v.literal->text,
+ arg1->v.literal->text) >= 0;
+ break;
+
+ default:
+ return;
+ }
+ } else
+ return;
+ node->type = node_type_number;
+ free_node(arg0);
+ free_node(arg1);
+}
+
+static int
+node_regmatch(NODE *node, struct literal *lit)
+{
+ regex_t re;
+ struct sym_regex *sym = node->v.regex;
+ int rc;
+
+ rc = regcomp(&re, sym->lit->text, sym->regflags);
+ if (rc) {
+ char errbuf[512];
+ regerror(rc, &re, errbuf, sizeof(errbuf));
+ parse_error_locus(&node->locus,
+ _("Cannot compile regex: %s"),
+ errbuf);
+ return 1;
+ }
+ rc = regexec(&re, lit->text, 0, NULL, 0);
+ regfree(&re);
+ return rc;
+}
+
+void
+optimize_type_bin(NODE *node)
+{
+ NODE *arg0, *arg1;
+
+ arg0 = node->v.bin.arg[0];
+ arg1 = node->v.bin.arg[1];
+ optimize(arg0);
+ optimize(arg1);
+
+ switch (node->v.bin.opcode) {
+ case bin_and:
+ case bin_or:
+ case bin_eq:
+ case bin_ne:
+ case bin_lt:
+ case bin_le:
+ case bin_gt:
+ case bin_ge:
+ optimize_relational(node);
+ break;
+
+ case bin_add:
+ case bin_sub:
+ case bin_mul:
+ case bin_div:
+ case bin_logand:
+ case bin_logor:
+ case bin_logxor:
+ optimize_arith(node);
+ break;
+
+ case bin_match:
+ if (node_type(arg0) != dtype_string) {
+ parse_error_locus(&arg0->locus,
+ _("left-hand side argument "
+ "to match is "
+ "of wrong data type"));
+ } else if (arg1->type == node_type_regex) {
+ if (arg0->type == node_type_string) {
+ node->v.number =
+ node_regmatch(arg1,
+ arg0->v.literal) == 0;
+ node->type = node_type_number;
+ free_node(arg0);
+ free_node(arg1);
+ }
+ } else if (arg1->type != node_type_regcomp) {
+ parse_error_locus(&arg1->locus,
+ _("right-hand side argument "
+ "to match is "
+ "of wrong data type "
+ "(should not happen)"));
+ }
+ break;
+
+ case bin_fnmatch:
+ if (arg0->type == node_type_string
+ && arg1->type == node_type_string) {
+ node->v.number = fnmatch(arg1->v.literal->text,
+ arg0->v.literal->text,
+ 0) == 0;
+ node->type = node_type_number;
+ free_node(arg0);
+ free_node(arg1);
+ } else if (node_type(arg0) != dtype_string) {
+ parse_error_locus(&arg0->locus,
+ _("left-hand side argument "
+ "to fnmatch is of wrong data type"));
+ } else if (node_type(arg1) != dtype_string) {
+ parse_error_locus(&arg1->locus,
+ _("right-hand side argument "
+ "to fnmatch is of wrong data type"));
+ }
+ break;
+ }
+}
+
+#define __code_cat3__(a,b,c) a ## b ## c
+
+#define CODE_BINARY(op, node) do { \
+ switch (node_type(node->v.bin.arg[0])) { \
+ case dtype_number: \
+ code_op(__code_cat3__(opcode_,op,n)); \
+ break; \
+ case dtype_string: \
+ code_op(__code_cat3__(opcode_,op,s)); \
+ break; \
+ default: \
+ parse_error_locus(&node->locus, \
+ _("Invalid argument type in binary operation")); \
+ break; \
+ } \
+} while (0)
+
+void
+code_type_bin(NODE *node, struct locus **old_locus)
+{
+ prog_counter_t pos1, pos2;
+
+ code_node(node->v.bin.arg[0]);
+
+ switch (node->v.bin.opcode) {
+ case bin_and:
+ MARK_LOCUS();
+ /* cond1
+ if not true goto X
+ cond2
+ if true goto Y
+ X: push 0
+ goto Z
+ Y: push 1
+ Z: */
+ code_op(opcode_bz);
+ pos1 = code_immediate(NULL);
+ code_node(node->v.bin.arg[1]);
+ code_op(opcode_bnz);
+ pos2 = code_immediate((void *)4);
+ code_op(opcode_push);
+ code_immediate((void *)0);
+ code_op(opcode_jmp);
+ code_immediate((void *)2);
+ code_op(opcode_push);
+ code_immediate((void*)1);
+ code_put(pos1, (void *)(pos2 - pos1));
+ break;
+
+ case bin_or:
+ MARK_LOCUS();
+ /* cond1
+ if true goto X
+ cond2
+ if not true goto Y
+ X: push 1
+ goto Z
+ Y: push 0
+ Z: */
+
+ code_op(opcode_bnz);
+ pos1 = code_immediate(NULL);
+ code_node(node->v.bin.arg[1]);
+ code_op(opcode_bz);
+ pos2 = code_immediate((void *)4);
+ code_op(opcode_push);
+ code_immediate((void *)1);
+ code_op(opcode_jmp);
+ code_immediate((void *)2);
+ code_op(opcode_push);
+ code_immediate((void *)0);
+ code_put(pos1, (void *)(pos2 - pos1));
+ break;
+
+ case bin_eq:
+ code_node(node->v.bin.arg[1]);
+ CODE_BINARY(eq, node);
+ break;
+
+ case bin_ne:
+ code_node(node->v.bin.arg[1]);
+ CODE_BINARY(ne, node);
+ break;
+
+ case bin_lt:
+ code_node(node->v.bin.arg[1]);
+ CODE_BINARY(lt, node);
+ break;
+
+ case bin_le:
+ code_node(node->v.bin.arg[1]);
+ CODE_BINARY(le, node);
+ break;
+
+ case bin_gt:
+ code_node(node->v.bin.arg[1]);
+ CODE_BINARY(gt, node);
+ break;
+
+ case bin_ge:
+ code_node(node->v.bin.arg[1]);
+ CODE_BINARY(ge, node);
+ break;
+
+ case bin_match:
+ code_node(node->v.bin.arg[1]);
+ MARK_LOCUS();
+ if (node->v.bin.qualifier & QUALIFIER_MX)
+ code_op(opcode_regmatch_mx);
+ else
+ code_op(opcode_regmatch);
+ break;
+
+ case bin_fnmatch:
+ code_node(node->v.bin.arg[1]);
+ MARK_LOCUS();
+ if (node->v.bin.qualifier & QUALIFIER_MX)
+ code_op(opcode_fnmatch_mx);
+ else
+ code_op(opcode_fnmatch);
+ break;
+
+ case bin_add:
+ code_node(node->v.bin.arg[1]);
+ MARK_LOCUS();
+ code_op(opcode_add);
+ break;
+
+ case bin_sub:
+ code_node(node->v.bin.arg[1]);
+ MARK_LOCUS();
+ code_op(opcode_sub);
+ break;
+
+ case bin_mul:
+ code_node(node->v.bin.arg[1]);
+ MARK_LOCUS();
+ code_op(opcode_mul);
+ break;
+
+ case bin_div:
+ code_node(node->v.bin.arg[1]);
+ MARK_LOCUS();
+ code_op(opcode_div);
+ break;
+
+ case bin_logand:
+ code_node(node->v.bin.arg[1]);
+ MARK_LOCUS();
+ code_op(opcode_logand);
+ break;
+
+ case bin_logor:
+ code_node(node->v.bin.arg[1]);
+ MARK_LOCUS();
+ code_op(opcode_logor);
+ break;
+
+ case bin_logxor:
+ code_node(node->v.bin.arg[1]);
+ MARK_LOCUS();
+ code_op(opcode_logxor);
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+/* type un */
+
+void
+print_type_un(NODE *node, int level)
+{
+ print_level(level);
+ switch (node->v.un.opcode) {
+ case unary_not:
+ printf("NOT\n");
+ break;
+ case unary_minus:
+ printf("NEG\n");
+ break;
+ default:
+ abort();
+ }
+ print_node(node->v.un.arg, level+1);
+}
+
+void
+mark_type_un(NODE *node)
+{
+ mark(node->v.un.arg);
+}
+
+void
+optimize_type_un(NODE *node)
+{
+ NODE *p = node->v.un.arg;
+ optimize(p);
+ if (p->type == node_type_number) {
+ switch (node->v.un.opcode) {
+ case unary_not:
+ node->v.number = !p->v.number;
+ break;
+
+ case unary_minus:
+ node->v.number = -p->v.number;
+ break;
+
+ case unary_lognot:
+ node->v.number = ~p->v.number;
+ break;
+ }
+ node->type = node_type_number;
+ free_node(p);
+ }
+}
+
+void
+code_type_un(NODE *node, struct locus **old_locus)
+{
+ code_node(node->v.un.arg);
+ MARK_LOCUS();
+ switch (node->v.un.opcode) {
+ case unary_not:
+ code_op(opcode_not);
+ break;
+
+ case unary_minus:
+ code_op(opcode_neg);
+ break;
+
+ case unary_lognot:
+ code_op(opcode_lognot);
+ break;
+
+ default:
+ abort();
+ }
+}
+
+
+/* type result */
+
+void
+print_type_result(NODE *node, int level)
+{
+ if (node->v.ret.code) {
+ const char *s = NULL;
+ int expr = 0;
+
+ print_level(level);
+ if (node->v.ret.message) {
+ if (node->v.ret.message->type == node_type_string)
+ s = node->v.ret.message->v.literal->text;
+ else {
+ expr = 1;
+ s = "(expression)";
+ }
+ }
+ dbg_setreply(NULL,
+ (char*) LITERAL_TEXT(node->v.ret.code),
+ (char*) LITERAL_TEXT(node->v.ret.xcode),
+ (char*) s);
+ if (expr)
+ print_node(node->v.ret.message, level+1);
+ }
+ print_level(level);
+ print_stat(node->v.ret.stat);
+ printf("\n");
+}
+
+void
+mark_type_result(NODE *node)
+{
+ if (node->v.ret.code)
+ node->v.ret.code->flags |= VAR_REFERENCED;
+ if (node->v.ret.xcode)
+ node->v.ret.xcode->flags |= VAR_REFERENCED;
+ mark(node->v.ret.message);
+}
+
+void
+optimize_type_result(NODE *node)
+{
+ optimize(node->v.ret.message);
+}
+
+void
+code_type_result(NODE *node, struct locus **old_locus)
+{
+ MARK_LOCUS();
+ if (node->v.ret.message)
+ code_node(node->v.ret.message);
+ else {
+ code_op(opcode_push);
+ code_immediate(NULL);
+ }
+ MARK_LOCUS();
+ code_op(opcode_result);
+ code_immediate((void*)node->v.ret.stat);
+ code_immediate((void*)LITERAL_OFF(node->v.ret.code));
+ code_immediate((void*)LITERAL_OFF(node->v.ret.xcode));
+ code_op(opcode_nil);
+}
+
+
+/* type header */
+
+void
+print_type_header(NODE *node, int level)
+{
+ print_level(level);
+ printf("%s %s: \n", msgmod_opcode_str(node->v.hdr.opcode),
+ node->v.hdr.name->text);
+ print_node_list(node->v.hdr.value, level+1);
+}
+
+void
+mark_type_header(NODE *node)
+{
+ node->v.hdr.name->flags |= VAR_REFERENCED;
+ mark(node->v.hdr.value);
+}
+
+void
+optimize_type_header(NODE *node)
+{
+ optimize(node->v.hdr.value);
+}
+
+void
+code_type_header(NODE *node, struct locus **old_locus)
+{
+ MARK_LOCUS();
+ if (node->v.hdr.value)
+ code_node(node->v.hdr.value);
+ else {
+ code_op(opcode_push);
+ code_immediate(0);
+ }
+ code_op(opcode_header);
+ code_immediate((void*)node->v.hdr.opcode);
+ code_immediate((void*)node->v.hdr.name->off);
+}
+
+
+/* type builtin */
+
+void
+print_type_builtin(NODE *node, int level)
+{
+ print_level(level);
+ printf("BUILTIN %s\n", node->v.builtin.builtin->name);
+ print_node_list_reverse(node->v.builtin.args, level+1);
+}
+
+void
+mark_type_builtin(NODE *node)
+{
+ unsigned i;
+ NODE *p;
+ struct literal *s = literal_lookup(node->v.builtin.builtin->name);
+
+ s->flags |= VAR_REFERENCED;
+ for (i = 0, p = node->v.builtin.args; p; i++, p = p->next)
+ mark(p);
+}
+
+void
+optimize_type_builtin(NODE *node)
+{
+ size_t i;
+ NODE *p;
+
+ for (i = 0, p = node->v.builtin.args; p; i++, p = p->next)
+ optimize(p);
+ if (strcmp(node->v.builtin.builtin->name, "interval") == 0) {
+ if (node->v.builtin.args->type == node_type_string) {
+ time_t t;
+ const char *endp;
+
+ if (parse_time_interval(
+ node->v.builtin.args->v.literal->text,
+ &t, &endp)) {
+ parse_error_locus(&node->locus,
+ _("Unrecognized time format (near `%s')"),
+ endp);
+ return;
+ }
+ /* Replace this node */
+ node->type = node_type_number;
+ node->v.number = t;
+ }
+ }
+}
+
+void
+code_type_builtin(NODE *node, struct locus **old_locus)
+{
+ NODE *p;
+ int i;
+ const struct builtin *bp = node->v.builtin.builtin;
+ struct literal *s;
+
+ /* Pass arguments */
+ for (p = node->v.builtin.args, i = 0; p; p = p->next, i++)
+ code_node(p);
+ if (bp->optcount || bp->varargs) {
+ /* Pass the number of actual arguments in a hidden arg */
+ code_op(opcode_push);
+ code_immediate((void*)i);
+ }
+
+ MARK_LOCUS();
+ code_op(opcode_builtin);
+ s = literal_lookup(node->v.builtin.builtin->name);
+ code_immediate((void*)s->off);
+ code_immediate((void*)node->v.builtin.builtin->handler);
+}
+
+
+/* type concat */
+
+void
+print_type_concat(NODE *node, int level)
+{
+ print_level(level);
+ printf("CONCAT:\n");
+ print_node(node->v.concat.arg[0], level+1);
+ print_node(node->v.concat.arg[1], level+1);
+}
+
+void
+mark_type_concat(NODE *node)
+{
+ mark(node->v.concat.arg[0]);
+ mark(node->v.concat.arg[1]);
+}
+
+void
+optimize_type_concat(NODE *node)
+{
+ NODE *arg0, *arg1;
+
+ optimize(node->v.concat.arg[0]);
+ optimize(node->v.concat.arg[1]);
+ arg0 = node->v.concat.arg[0];
+ arg1 = node->v.concat.arg[1];
+ if (arg0->type == node_type_string
+ && arg1->type == node_type_string) {
+ string_begin();
+ string_add(arg0->v.literal->text,
+ strlen(arg0->v.literal->text));
+ string_add(arg1->v.literal->text,
+ strlen(arg1->v.literal->text));
+ node->v.literal = string_finish();
+ node->type = node_type_string;
+ free_node(arg0);
+ free_node(arg1);
+ } else if (arg0->type == node_type_string
+ && arg0->v.literal->text[0] == 0) {
+ copy_node(node, arg1);
+ free_node(arg0);
+ } else if (arg1->type == node_type_string
+ && arg1->v.literal->text[0] == 0) {
+ copy_node(node, arg0);
+ free_node(arg1);
+ }
+}
+
+void
+code_type_concat(NODE *node, struct locus **old_locus)
+{
+ code_node(node->v.concat.arg[0]);
+ code_node(node->v.concat.arg[1]);
+ code_op(opcode_concat);
+}
+
+
+/* type variable */
+
+void
+print_type_variable(NODE *node, int level)
+{
+ print_level(level);
+ printf("VARIABLE %s %s %lu(%lu)\n",
+ storage_class_str(node->v.var_ref.variable->storage_class),
+ node->v.var_ref.variable->name,
+ (unsigned long) node->v.var_ref.nframes,
+ (unsigned long) node->v.var_ref.variable->off);
+}
+
+void
+mark_type_variable(NODE *node)
+{
+ if (node->v.var_ref.variable->storage_class == storage_extern)
+ node->v.var_ref.variable->flags |= VAR_REFERENCED;
+}
+
+void
+code_type_variable(NODE *node, struct locus **old_locus)
+{
+ MARK_LOCUS();
+ code_memref(node);
+ code_op(opcode_deref);
+}
+
+
+/* type asgn */
+
+void
+print_type_asgn(NODE *node, int level)
+{
+ print_level(level);
+ printf("SET %s %s %lu(%lu)\n",
+ storage_class_str(node->v.asgn.var->storage_class),
+ node->v.asgn.var->name,
+ (unsigned long) node->v.asgn.nframes,
+ (unsigned long) node->v.asgn.var->off);
+ print_node(node->v.asgn.node, level + 1);
+}
+
+void
+mark_type_asgn(NODE *node)
+{
+ mark(node->v.asgn.node);
+}
+
+void
+optimize_type_asgn(NODE *node)
+{
+ optimize(node->v.asgn.node);
+}
+
+void
+code_type_asgn(NODE *node, struct locus **old_locus)
+{
+ code_node(node->v.asgn.node);
+ node->v.asgn.var->type = node_type(node->v.asgn.node);
+ code_memref(node);
+ code_op(opcode_asgn);
+}
+
+
+/* type arg */
+
+void
+print_type_arg(NODE *node, int level)
+{
+ print_level(level);
+ printf("ARG %u\n", node->v.arg.number);
+}
+
+void
+code_type_arg(NODE *node, struct locus **old_locus)
+{
+ MARK_LOCUS();
+ code_op(opcode_memstk);
+ code_immediate((void*)0);
+ code_immediate((void*)(node->v.arg.number + 2));
+ code_op(opcode_deref);
+}
+
+
+
+char *
+regex_flags_to_string(int flags, char *buf, size_t size)
+{
+ static struct {
+ unsigned flag;
+ char *name;
+ } regflg[] = {
+ { REG_EXTENDED, REG_EXTENDED_NAME },
+ { REG_ICASE, REG_ICASE_NAME },
+ { REG_NEWLINE, REG_NEWLINE_NAME }
+ };
+ char *p;
+ int i;
+
+ p = buf;
+ size--;
+ for (i = 0; i < NELEMS(regflg) && size > 0; i++) {
+ if (regflg[i].flag & flags) {
+ size_t len = strlen(regflg[i].name);
+ if (p > buf)
+ len++;
+ if (len > size)
+ len = size;
+ if (p > buf) {
+ *p++ = ',';
+ len--;
+ }
+ if (len > 0) {
+ memcpy(p, regflg[i].name, len);
+ p += len;
+ }
+ size -= len;
+ }
+ }
+ *p = 0;
+ return buf;
+}
+
+/* type regex */
+void
+print_type_regex(NODE *node, int level)
+{
+ char buffer[REGEX_STRING_BUFSIZE];
+ print_