diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2011-05-16 01:08:45 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2011-05-16 01:17:20 +0300 |
commit | e559f3f36c99988ee80ac4ec01f80ea6578beebe (patch) | |
tree | e9cd0ab93dc2901fce901a062c7c1ee7442979e5 /src/bind-gram.y | |
parent | a0826b4c7cd66c4862d9b61bb7e14d73fcba28f6 (diff) | |
download | grecs-e559f3f36c99988ee80ac4ec01f80ea6578beebe.tar.gz grecs-e559f3f36c99988ee80ac4ec01f80ea6578beebe.tar.bz2 |
Implement BIND config parser.
* am/grecs.m4: New option: parser-bind.
* src/.gitignore: Update.
* src/Make.am [GRECS_COND_BIND_PARSER]: Set GRECS_PARSER_BIND
and GRECS_EXTRA_BIND.
(GRECS_SRC): Include GRECS_PARSER_BIND.
(EXTRA_DIST): Include GRECS_EXTRA_BIND.
* src/bind-gram.y: New file.
* src/bind-lex.l: New file.
* src/format.c (grecs_txtacc_format_value): New function.
* src/grecs.h (grecs_bind_parser): New proto.
* src/parser.c: Set grecs_current_locus.
* src/tree.c (grecs_node_bind): Return immediately if node==NULL.
* src/yytrans: Update.
Diffstat (limited to 'src/bind-gram.y')
-rw-r--r-- | src/bind-gram.y | 296 |
1 files changed, 296 insertions, 0 deletions
diff --git a/src/bind-gram.y b/src/bind-gram.y new file mode 100644 index 0000000..621070e --- /dev/null +++ b/src/bind-gram.y @@ -0,0 +1,296 @@ +%{ +/* grecs - Gray's Extensible Configuration System + Copyright (C) 2007-2011 Sergey Poznyakoff + + Grecs 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 of the License, or (at your + option) any later version. + + Grecs 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 Grecs. If not, see <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#include <grecs.h> +#include <grecs-gram.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <errno.h> + +int yylex(void); +int yyerror(char *s); + +static struct grecs_node *parse_tree; +extern int yy_flex_debug; +extern int grecs_bind_new_source(const char *name); +extern void grecs_bind_close_sources(void); + +static struct grecs_value *stmtlist_to_value(struct grecs_node *node); +%} + +%union { + struct { + grecs_locus_t locus; + char *string; + } ident; + char *string; + grecs_value_t svalue, *pvalue; + struct grecs_list *list; + struct grecs_node *node; + grecs_locus_t locus; + struct { struct grecs_node *head, *tail; } node_list; +} + +%token <ident> BIND_IDENT +%token <string> BIND_STRING +%type <string> string +%type <svalue> value +%type <pvalue> vallist tag +%type <list> vlist +%type <node> stmt simple block +%type <node_list> stmtlist + +%% + +input : stmtlist + { + parse_tree = grecs_node_create(grecs_node_root, + &grecs_current_locus); + parse_tree->v.texttab = grecs_text_table(); + grecs_node_bind(parse_tree, $1.head, 1); + } + ; + +stmtlist: stmt + { + $$.head = $$.tail = $1; + } + | stmtlist stmt + { + if ($2) { + if (!$1.head) + $1.head = $1.tail = $2; + else + grecs_node_bind($1.tail, $2, 0); + } + $$ = $1; + } + ; + +stmt : simple + | block + ; + +simple : BIND_IDENT vallist ';' + { + if (strcmp($1.string, "include") == 0 && + $2->type == GRECS_TYPE_STRING) { + grecs_bind_new_source($2->v.string); + $$ = NULL; + } else { + $$ = grecs_node_create(grecs_node_stmt, &$1.locus); + $$->ident = $1.string; + $$->v.value = $2; + } + } + | string ';' + { + $$ = grecs_node_create(grecs_node_stmt, + &grecs_current_locus); + $$->ident = $1; + $$->v.value = NULL; + } + ; + +block : BIND_IDENT tag '{' stmtlist '}' opt_sc + { + $$ = grecs_node_create(grecs_node_block, &$1.locus); + $$->ident = $1.string; + $$->v.value = $2; + grecs_node_bind($$, $4.head, 1); + } + ; + +tag : /* empty */ + { + $$ = NULL; + } + | BIND_IDENT + { + $$ = grecs_malloc(sizeof($$[0])); + $$->type = GRECS_TYPE_STRING; + $$->v.string = $1.string; + } + | BIND_STRING + { + $$ = grecs_malloc(sizeof($$[0])); + $$->type = GRECS_TYPE_STRING; + $$->v.string = $1; + } + ; + +vallist : vlist + { + size_t n; + + if ((n = grecs_list_size($1)) == 1) { + $$ = grecs_list_index($1, 0); + } else { + size_t i; + struct grecs_list_entry *ep; + + $$ = grecs_malloc(sizeof($$[0])); + $$->type = GRECS_TYPE_ARRAY; + $$->v.arg.c = n; + $$->v.arg.v = grecs_calloc(n, + sizeof($$->v.arg.v[0])); + for (i = 0, ep = $1->head; ep; i++, ep = ep->next) + $$->v.arg.v[i] = ep->data; + } + $1->free_entry = NULL; + grecs_list_free($1); + } + ; + +vlist : value + { + $$ = grecs_value_list_create(); + grecs_list_append($$, grecs_value_ptr_from_static(&$1)); + } + | vlist value + { + grecs_list_append($1, grecs_value_ptr_from_static(&$2)); + } + | vlist BIND_IDENT '{' stmtlist '}' + /* This production handles constructs that don't fit well into + hierarchical structure, namely, the "controls" statement, e.g.: + + controls { + inet 127.0.0.1 port 953 + allow { 127.0.0.1; 127.0.0.2; } keys { "rndc-key"; }; + }; + + This statement produces: + + .controls.inet: 127.0.0.1 port 953 allow (127.0.0.1) keys + (rndc-key) + */ + { + struct grecs_value *val = grecs_malloc(sizeof(*val)); + val->type = GRECS_TYPE_STRING; + val->v.string = $2.string; + grecs_list_append($1, val); + + val = stmtlist_to_value($4.head); + /* FIXME: Free $4 */ + grecs_list_append($1, val); + } + ; + +value : string + { + $$.type = GRECS_TYPE_STRING; + $$.v.string = $1; + } + ; + +string : BIND_STRING + | BIND_IDENT + { + $$ = $1.string; + } + ; + +opt_sc : /* empty */ + | ';' + ; + +%% + +int +yyerror(char *s) +{ + grecs_error(&grecs_current_locus, 0, "%s", s); + return 0; +} + +struct grecs_node * +grecs_bind_parser(const char *name, int traceflags) +{ + int rc; + + if (grecs_bind_new_source(name)) + return NULL; + yy_flex_debug = traceflags & GRECS_TRACE_LEX; + yydebug = traceflags & GRECS_TRACE_GRAM; + parse_tree = NULL; + grecs_line_acc_create(); + rc = yyparse(); + grecs_bind_close_sources(); + if (grecs_error_count) + rc = 1; + grecs_line_acc_free(); + if (rc) { + grecs_tree_free(parse_tree); + parse_tree = NULL; + } + return parse_tree; +} + +static struct grecs_value * +node_to_value(struct grecs_node *node, struct grecs_txtacc *acc) +{ + struct grecs_value *val = grecs_malloc(sizeof(*val)); + int delim = 0; + + if (node->ident) { + grecs_txtacc_grow(acc, node->ident, strlen(node->ident)); + delim = 1; + } + + if (node->v.value) { + if (delim) + grecs_txtacc_grow_char(acc, ' '); + grecs_txtacc_format_value(node->v.value, 0, acc); + delim = 1; + } + if (node->type == grecs_node_stmt) { + val->type = GRECS_TYPE_STRING; + grecs_txtacc_grow_char(acc, 0); + val->v.string = grecs_txtacc_finish(acc, 1); + } else if (node->down) { + struct grecs_list *list = grecs_value_list_create(); + struct grecs_node *np; + + if (delim) + grecs_txtacc_grow_char(acc, ' '); + for (np = node->down; np; np = np->next) + grecs_list_append(list, node_to_value(np, acc)); + val->type = GRECS_TYPE_LIST; + val->v.list = list; + } + return val; +} + +static struct grecs_value * +stmtlist_to_value(struct grecs_node *node) +{ + struct grecs_txtacc *acc = grecs_txtacc_create(); + struct grecs_value *val; + struct grecs_node parent; + + memset(&parent, 0, sizeof(parent)); + parent.type = grecs_node_block; + parent.down = node; + val = node_to_value(&parent, acc); + grecs_txtacc_free(acc); + return val; +} |