%{
/* grecs - Gray's Extensible Configuration System
Copyright (C) 2007-2012, 2015 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 . */
#ifdef HAVE_CONFIG_H
# include
#endif
#include
#include
#include
#include
#include
#include
#include
int yylex(void);
int yyerror(char const *s);
static struct grecs_node *parse_tree;
extern int yy_flex_debug;
extern int grecs_dhcpd_new_source(const char *name, grecs_locus_t *loc);
extern void grecs_dhcpd_close_sources(void);
extern void grecs_dhcpd_begin_bool(void);
extern void grecs_dhcpd_begin_expr(void);
/* NOTE: STRING must be allocated */
static grecs_value_t *
make_string_value(char *string)
{
grecs_value_t *val;
val = grecs_malloc(sizeof(val[0]));
val->type = GRECS_TYPE_STRING;
val->v.string = string;
return val;
}
%}
%error-verbose
%locations
%union {
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 DHCPD_IF DHCPD_ELSIF DHCPD_EXPR
%token DHCPD_ELSE
%token DHCPD_STRING DHCPD_IDENT
%type string
%type value
%type vallist tag
%type vlist strlist
%type stmt simple block maybe_stmtlist
%type cond elsecond opt_elsecond elsifcond
%type stmtlist elsifchain opt_elsifchain
%%
input : maybe_stmtlist
{
parse_tree = grecs_node_create(grecs_node_root, &@1);
parse_tree->v.texttab = grecs_text_table();
grecs_node_bind(parse_tree, $1, 1);
}
;
maybe_stmtlist:
/* empty */
{
$$ = NULL;
}
| stmtlist
{
$$ = $1.head;
}
;
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
| cond
;
simple : DHCPD_IDENT vallist ';'
{
if (strcmp($1, "include") == 0 &&
$2->type == GRECS_TYPE_STRING) {
grecs_dhcpd_new_source($2->v.string, &@1);
$$ = NULL;
} else {
$$ = grecs_node_create_points(grecs_node_stmt,
@1.beg, @2.end);
$$->ident = $1;
$$->idloc = @1;
$$->v.value = $2;
}
}
| DHCPD_IDENT '=' { grecs_dhcpd_begin_expr(); } DHCPD_EXPR ';'
{
$$ = grecs_node_create_points(grecs_node_stmt,
@1.beg, @5.end);
$$->ident = $1;
$$->idloc = @1;
$$->v.value = make_string_value($4);
}
| string ';'
{
$$ = grecs_node_create(grecs_node_stmt, &@1);
$$->ident = $1;
$$->idloc = @1;
$$->v.value = NULL;
}
;
block : DHCPD_IDENT tag '{' maybe_stmtlist '}' opt_semi
{
$$ = grecs_node_create_points(grecs_node_block,
@1.beg, @5.end);
$$->ident = $1;
$$->idloc = @1;
$$->v.value = $2;
grecs_node_bind($$, $4, 1);
}
;
opt_semi: /* empty */
| ';'
;
tag : /* empty */
{
$$ = NULL;
}
| vallist
;
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;
$$->locus = @1;
$$->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));
}
;
value : string
{
$$.type = GRECS_TYPE_STRING;
$$.locus = @1;
$$.v.string = $1;
}
| strlist
{
$$.type = GRECS_TYPE_LIST;
$$.locus = @1;
$$.v.list = $1;
}
;
string : DHCPD_STRING
| DHCPD_IDENT
;
strlist : DHCPD_STRING ',' DHCPD_STRING
{
grecs_value_t val;
$$ = grecs_value_list_create();
val.type = GRECS_TYPE_STRING;
val.locus = @1;
val.v.string = $1;
grecs_list_append($$, grecs_value_ptr_from_static(&val));
val.type = GRECS_TYPE_STRING;
val.locus = @3;
val.v.string = $3;
grecs_list_append($$, grecs_value_ptr_from_static(&val));
}
| strlist ',' DHCPD_STRING
{
grecs_value_t val;
val.type = GRECS_TYPE_STRING;
val.locus = @3;
val.v.string = $3;
grecs_list_append($1, grecs_value_ptr_from_static(&val));
}
;
cond : if DHCPD_EXPR '{' maybe_stmtlist '}' opt_elsifchain opt_elsecond
{
$$ = grecs_node_create_points(grecs_node_block,
@1.beg, @7.end);
grecs_line_begin();
grecs_line_add("if", 2);
$$->ident = grecs_line_finish();
$$->idloc = @1;
$$->v.value = make_string_value ($2);
grecs_node_bind($$, $4, 1);
if ($6.head) {
grecs_node_bind($6.tail, $7, 0);
grecs_node_bind($$, $6.head, 0);
}
}
;
if : DHCPD_IF
{
grecs_dhcpd_begin_bool();
}
;
elsif : DHCPD_ELSIF
{
grecs_dhcpd_begin_bool();
}
;
opt_elsifchain: /* empty */
{
$$.head = $$.tail = NULL;
}
| elsifchain
;
elsifchain: elsifcond
{
$$.head = $$.tail = $1;
}
| elsifchain elsifcond
{
if ($2) {
if (!$1.head)
$1.head = $1.tail = $2;
else
grecs_node_bind($1.tail, $2, 0);
}
$$ = $1;
}
;
elsifcond: elsif DHCPD_EXPR '{' maybe_stmtlist '}'
{
$$ = grecs_node_create_points(grecs_node_block,
@1.beg, @5.end);
grecs_line_begin();
grecs_line_add("elsif", 5);
$$->ident = grecs_line_finish();
$$->idloc = @1;
$$->v.value = make_string_value ($2);
grecs_node_bind($$, $4, 1);
}
;
opt_elsecond: /* empty */
{
$$ = NULL;
}
| elsecond
;
elsecond: DHCPD_ELSE '{' maybe_stmtlist '}'
{
$$ = grecs_node_create_points(grecs_node_block,
@1.beg, @4.end);
grecs_line_begin();
grecs_line_add("else", 4);
$$->ident = grecs_line_finish();
$$->idloc = @1;
$$->v.value = NULL;
grecs_node_bind($$, $3, 1);
}
;
%%
int
yyerror(char const *s)
{
grecs_error(&yylloc, 0, "%s", s);
return 0;
}
struct grecs_node *
grecs_dhcpd_parser(const char *name, int traceflags)
{
int rc;
if (grecs_dhcpd_new_source(name, NULL))
return NULL;
yy_flex_debug = traceflags & GRECS_TRACE_LEX;
yydebug = traceflags & GRECS_TRACE_GRAM;
parse_tree = NULL;
grecs_line_acc_create();
rc = yyparse();
grecs_dhcpd_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;
}