%{
/* 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 . */
#ifdef HAVE_CONFIG_H
# include
#endif
#include "yygrecs.h"
#include
#include
#include
#include
#include
#include
static struct grecs_node *parse_tree;
int grecs_error_count;
int grecs_default_port = 0;
%}
%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
%token STRING QSTRING MSTRING
%type string slist
%type slist0
%type value
%type vallist tag
%type values list vlist
%type stmt simple block
%type 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
{
grecs_node_bind($1.tail, $2, 0);
}
;
stmt : simple
| block
;
simple : IDENT vallist ';'
{
$$ = grecs_node_create(grecs_node_stmt,
&$1.locus);
$$->ident = $1.string;
$$->v.value = $2;
}
| IDENT ';'
{
$$ = grecs_node_create(grecs_node_stmt,
&$1.locus);
$$->ident = $1.string;
$$->v.value = NULL;
}
;
block : 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;
}
| 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;
$$->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;
$$.v.string = $1;
}
| list
{
$$.type = GRECS_TYPE_LIST;
$$.v.list = $1;
}
| MSTRING
{
$$.type = GRECS_TYPE_STRING;
$$.v.string = $1;
}
;
string : STRING
| IDENT
{
$$ = $1.string;
}
| slist
;
slist : slist0
{
struct grecs_list_entry *ep;
grecs_line_begin();
for (ep = $1->head; ep; ep = ep->next) {
grecs_line_add(ep->data, strlen(ep->data));
free(ep->data);
ep->data = NULL;
}
$$ = grecs_line_finish();
grecs_list_free($1);
}
;
slist0 : QSTRING
{
$$ = grecs_list_create();
grecs_list_append($$, $1);
}
| slist0 QSTRING
{
grecs_list_append($1, $2);
$$ = $1;
}
;
list : '(' ')'
{
$$ = NULL;
}
| '(' values ')'
{
$$ = $2;
}
| '(' values ',' ')'
{
$$ = $2;
}
;
values : value
{
$$ = grecs_value_list_create();
grecs_list_append($$, grecs_value_ptr_from_static(&$1));
}
| values ',' value
{
grecs_list_append($1, grecs_value_ptr_from_static(&$3));
$$ = $1;
}
;
opt_sc : /* empty */
| ';'
;
%%
int
yyerror(char *s)
{
grecs_error(&grecs_current_locus, 0, "%s", s);
return 0;
}
int
grecs_vasprintf(char **pbuf, size_t *psize, const char *fmt, va_list ap)
{
char *buf = *pbuf;
size_t buflen = *psize;
int rc = 0;
if (!buf) {
if (buflen == 0)
buflen = 512; /* Initial allocation */
buf = calloc(1, buflen);
if (buf == NULL)
return ENOMEM;
}
for (;;) {
ssize_t n = vsnprintf(buf, buflen, fmt, ap);
if (n < 0 || n >= buflen || !memchr(buf, '\0', n + 1)) {
char *newbuf;
size_t newlen = buflen * 2;
if (newlen < buflen) {
rc = ENOMEM;
break;
}
newbuf = realloc(buf, newlen);
if (newbuf == NULL) {
rc = ENOMEM;
break;
}
buflen = newlen;
buf = newbuf;
} else
break;
}
if (rc) {
if (!*pbuf) {
/* We made first allocation, now free it */
free(buf);
buf = NULL;
buflen = 0;
}
}
*pbuf = buf;
*psize = buflen;
return rc;
}
int
grecs_asprintf(char **pbuf, size_t *psize, const char *fmt, ...)
{
int rc;
va_list ap;
va_start(ap, fmt);
rc = grecs_vasprintf(pbuf, psize, fmt, ap);
va_end(ap);
return rc;
}
struct grecs_node *
grecs_parse(const char *name)
{
int rc;
if (grecs_lex_begin(name))
return NULL;
parse_tree = NULL;
rc = yyparse();
if (grecs_error_count)
rc = 1;
grecs_lex_end(rc);
if (rc) {
grecs_tree_free(parse_tree);
parse_tree = NULL;
}
return parse_tree;
}
void
grecs_gram_trace(int n)
{
yydebug = n;
}