summaryrefslogtreecommitdiffabout
path: root/src/bind-gram.y
authorSergey Poznyakoff <gray@gnu.org.ua>2011-05-15 22:08:45 (GMT)
committer Sergey Poznyakoff <gray@gnu.org.ua>2011-05-15 22:17:20 (GMT)
commite559f3f36c99988ee80ac4ec01f80ea6578beebe (patch) (side-by-side diff)
treee9cd0ab93dc2901fce901a062c7c1ee7442979e5 /src/bind-gram.y
parenta0826b4c7cd66c4862d9b61bb7e14d73fcba28f6 (diff)
downloadgrecs-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') (more/less context) (ignore whitespace changes)
-rw-r--r--src/bind-gram.y296
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
--- a/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;
+}

Return to:

Send suggestions and report system problems to the System administrator.