diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2011-05-06 11:58:47 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2011-05-06 13:00:06 +0300 |
commit | 52c12573a505bf29a0301a1c9553e88c3270713e (patch) | |
tree | 642047e5b3de469199b68a76064f756990a3b249 /src/tree.c | |
parent | 8465fa7066d3c922b738346335f801cf9ea2243f (diff) | |
download | grecs-52c12573a505bf29a0301a1c9553e88c3270713e.tar.gz grecs-52c12573a505bf29a0301a1c9553e88c3270713e.tar.bz2 |
Rewrite list support to keep doubly-linked lists. Implement tree reduction.
* src/format.c (grecs_format_node)
(grecs_format_node_path): Handle grecs_node_root.
* src/grecs-gram.y (input production): Create root node.
* src/grecs.h (grecs_list_entry)<prev>: New member.
(grecs_node_root): New node type.
(grecs_node_eq): New proto.
(grecs_list_add,grecs_tree_reduce): New protos.
* src/list.c: Rewrite as a doubly-linked list.
* src/tree.c (grecs_node_bind): Bugfix.
(grecs_node_unlink): New function.
(_tree_recurse): Allow for removal of the current node.
(grecs_node_eq): New function.
(grecs_tree_reduce): New function.
(grecs_tree_process): Descend into the first subnode at once.
* src/lookup.c (node_finder): Handle grecs_node_root.
* tests/reduce00.at: New testcase.
* tests/reduce01.at: New testcase.
* tests/reduce02.at: New testcase.
* tests/testsuite.at (GRECS_TEST): New macro.
Include reduce0[0-2].at.
* tests/gcffmt.c: New option -reduce.
* tests/gcfpeek.c: Likewise.
* tests/gcfset.c: Likewise.
Diffstat (limited to 'src/tree.c')
-rw-r--r-- | src/tree.c | 174 |
1 files changed, 172 insertions, 2 deletions
@@ -57,7 +57,7 @@ grecs_node_bind(struct grecs_node *master, struct grecs_node *node, int dn) } else { if (!master->next) { master->next = node; - node->prev = NULL; + node->prev = master; } else { for (np = master->next; np->next; np = np->next) ; @@ -67,6 +67,21 @@ grecs_node_bind(struct grecs_node *master, struct grecs_node *node, int dn) node->up = master->up; } } + +int +grecs_node_unlink(struct grecs_node *node) +{ + if (node->prev) + node->prev->next = node->next; + else if (node->up) + node->up->down = node->next; + else + return 1; + if (node->next) + node->next->prev = node->prev; + return 0; +} + static enum grecs_tree_recurse_res @@ -83,7 +98,8 @@ _tree_recurse(struct grecs_node *node, grecs_tree_recursor_t recfun, break; \ } - for (; node; node = node->next) { + while (node) { + struct grecs_node *next = node->next; if (node->type == grecs_node_stmt) { res = recfun(grecs_tree_recurse_set, node, data); CKRES(); @@ -104,6 +120,7 @@ _tree_recurse(struct grecs_node *node, grecs_tree_recursor_t recfun, break; } } + node = next; } return grecs_tree_recurse_ok; #undef CKRES @@ -771,8 +788,161 @@ grecs_tree_process(struct grecs_node *node, struct grecs_keyword *kwd) config_keywords.kwd = kwd; clos.cursect = &config_keywords; clos.sections = grecs_list_create(); + if (node->type == grecs_node_root) + node = node->down; rc = grecs_tree_recurse(node, nodeproc, &clos); grecs_list_free(clos.sections); return rc; } + +int +grecs_node_eq(struct grecs_node *a, struct grecs_node *b) +{ + if (a->type != b->type) + return 1; + if (a->type == grecs_node_root) + return 0; + if (strcmp(a->ident, b->ident)) + return 1; + if (a->type == grecs_node_block && + !grecs_value_eq(&a->value, &b->value)) + return 1; + return 0; +} + +static void +value_to_list(struct grecs_value *val) +{ + struct grecs_list *list; + int i; + + if (val->type != GRECS_TYPE_LIST) + return; + list = _grecs_simple_list_create(0);/* FIXME: this relies on + grecs_value_dup registering its + return in string_list */ + switch (val->type) { + case GRECS_TYPE_STRING: + grecs_list_append(list, grecs_value_dup(val)); + break; + + case GRECS_TYPE_ARRAY: + for (i = 0; i < val->v.arg.c; i++) + grecs_list_append(list, + grecs_value_dup(&val->v.arg.v[i])); + } + val->type = GRECS_TYPE_LIST; + val->v.list = list; +} + +static void +node_aggregate_stmt(struct grecs_node *dst, struct grecs_node *src, int type) +{ + value_to_list(&dst->value); + value_to_list(&src->value); + grecs_list_add(dst->value.v.list, src->value.v.list); +} + +static void +node_merge_stmt(struct grecs_node *to_node, struct grecs_node *from_node, + struct grecs_keyword *kwp) +{ + if (kwp && GRECS_IS_LIST(kwp->type) && (kwp->type & GRECS_AGGR)) + node_aggregate_stmt(to_node, from_node, kwp->type); + else { + /*FIXME: grecs_value_free(from_node->value) */ + from_node->value.type = GRECS_TYPE_STRING; + from_node->value.v.string = NULL; + } +} + +static void +node_merge_block(struct grecs_node *to_node, struct grecs_node *from_node, + struct grecs_keyword *kwp) +{ + struct grecs_node *sp; + + if (!from_node->down) + return; + for (sp = from_node->down; ; sp = sp->next) { + sp->up = to_node; + if (!sp->next) + break; + } + sp->next = to_node->down; + to_node->down = from_node->down; + from_node->down = NULL; +} + +static void +node_reduce(struct grecs_node *node, struct grecs_keyword *kwp) +{ + struct grecs_node *p; + + for (p = node->next; p; p = p->next) + if (grecs_node_eq(p, node) == 0) + break; + if (p) { + switch (node->type) { + case grecs_node_root: + return; + case grecs_node_stmt: + node_merge_stmt(p, node, kwp); + break; + case grecs_node_block: + node_merge_block(p, node, kwp); + break; + } + grecs_node_unlink(node); + grecs_node_free(node); + } +} + +static enum grecs_tree_recurse_res +reduceproc(enum grecs_tree_recurse_op op, struct grecs_node *node, void *data) +{ + struct nodeproc_closure *clos = data; + + if (op == grecs_tree_recurse_post) { + if (clos->sections) + grecs_list_pop(clos->sections); + } else { + struct grecs_keyword *kwp = NULL; + if (clos->cursect) { + kwp = find_keyword(clos->cursect, node->ident); + if (!kwp) { + grecs_error(&node->locus, 0, + _("Unknown keyword")); + return grecs_tree_recurse_skip; + } + if (op == grecs_tree_recurse_pre) { + grecs_list_push(clos->sections, clos->cursect); + clos->cursect = kwp; + } + } + node_reduce(node, kwp); + } + return grecs_tree_recurse_ok; +} + +int +grecs_tree_reduce(struct grecs_node *node, struct grecs_keyword *kwd) +{ + int rc; + struct nodeproc_closure clos; + struct grecs_keyword config_keywords; + + config_keywords.kwd = kwd; + if (kwd) { + clos.cursect = &config_keywords; + clos.sections = grecs_list_create(); + } else { + clos.cursect = NULL; + clos.sections = NULL; + } + rc = grecs_tree_recurse(node->down, reduceproc, &clos); + grecs_list_free(clos.sections); + return rc; +} + |