aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2011-05-15 13:51:03 +0300
committerSergey Poznyakoff <gray@gnu.org.ua>2011-05-15 14:05:39 +0300
commita0826b4c7cd66c4862d9b61bb7e14d73fcba28f6 (patch)
tree71762fca263967e5803ce803b0a0aa94555e125d /src
parentfd64fa62bc68d8c2e0d693033e874fcc1f023544 (diff)
downloadgrecs-a0826b4c7cd66c4862d9b61bb7e14d73fcba28f6.tar.gz
grecs-a0826b4c7cd66c4862d9b61bb7e14d73fcba28f6.tar.bz2
Provide a framework for multiple parsers. Implement meta1 parser.
* Makefile.am (EXTRA_DIST): Add build-aux/yyrename * am/grecs.m4: New options: parser-meta1 and all-parsers * src/.gitignore: Update. * src/Make.am [GRECS_COND_META1_PARSER]: Set GRECS_PARSER_META1 and GRECS_EXTRA_META1 (GRECS_SRC): Add new files. (EXTRA_DIST): Add GRECS_EXTRA_META1. (LEXCOMPILE,YACCCOMPILE): Redefine * src/grecs-gram.y: Don't include yygrecs.h (grecs_vasprintf,grecs_asprintf): Move to a separate file. (grecs_parse): Rename to grecs_grecs_parser. Actual grecs_parse is defined in parser.c (grecs_gram_trace): Move to parser.c * src/grecs-lex.l: Don't include yygrecs.h Use line_acc functions. * src/grecs.h (grecs_trace_flags): New extern. (GRECS_TRACE_GRAM, GRECS_TRACE_LEX): New flags. (grecs_lex_begin): Change signature. (grecs_grecs_parser,grecs_meta1_parser): New protos. (grecs_line_acc_create) (grecs_line_acc_free,grecs_line_acc_grow_char) (grecs_line_acc_grow_char_unescape) (grecs_line_acc_grow) (grecs_line_acc_grow_unescape_last): New protos. (GRECS_TXTACC_BUFSIZE): New const (grecs_txtacc_create,grecs_txtacc_free) (grecs_txtacc_grow,grecs_txtacc_finish) (grecs_txtacc_free_string): New protos. (grecs_value_match): New proto. * src/wordsplit.c (quote_transtab): Translate \" * src/yygrecs.h: Remove. * src/yytrans: New file. * build-aux/yyrename: New file. * src/asprintf.c: New file. * src/lineacc.c: New file. * src/meta1-gram.y: New file. * src/meta1-lex.l: New file. * src/parser.c: New file. * src/txtacc.c: New file. * doc/grecs_parse.3: Update. * doc/GRECS_SETUP.3: Update.
Diffstat (limited to 'src')
-rw-r--r--src/.gitignore4
-rw-r--r--src/Make.am19
-rw-r--r--src/asprintf.c85
-rw-r--r--src/grecs-gram.y80
-rw-r--r--src/grecs-lex.l133
-rw-r--r--src/grecs.h35
-rw-r--r--src/lineacc.c82
-rw-r--r--src/meta1-gram.y217
-rw-r--r--src/meta1-lex.l114
-rw-r--r--src/parser.c60
-rw-r--r--src/txtacc.c187
-rw-r--r--src/wordsplit.c2
-rw-r--r--src/yygrecs.h28
-rw-r--r--src/yytrans20
14 files changed, 834 insertions, 232 deletions
diff --git a/src/.gitignore b/src/.gitignore
index 9ef1b57..feb5040 100644
--- a/src/.gitignore
+++ b/src/.gitignore
@@ -1,7 +1,11 @@
grecs-gram.c
grecs-gram.h
grecs-gram.output
grecs-lex.c
+meta1-gram.c
+meta1-gram.h
+meta1-gram.output
+meta1-lex.c
Make-inst.in
Make-shared.in
Make-static.in
diff --git a/src/Make.am b/src/Make.am
index 915e6c4..60c0f06 100644
--- a/src/Make.am
+++ b/src/Make.am
@@ -11,33 +11,46 @@
# 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/>.
+if GRECS_COND_META1_PARSER
+ GRECS_PARSER_META1 = meta1-gram.y meta1-lex.l
+ GRECS_EXTRA_META1 = meta1-gram.h
+endif
+
GRECS_SRC = \
+ asprintf.c\
diag.c\
format.c\
grecs-gram.y\
grecs-lex.l\
join.c\
+ lineacc.c\
list.c\
lookup.c\
mem.c\
+ parser.c\
preproc.c\
sort.c\
symtab.c\
text.c\
tree.c\
+ txtacc.c\
version.c\
- wordsplit.c
+ wordsplit.c\
+ $(GRECS_PARSER_META1)
-noinst_HEADERS = yygrecs.h
+noinst_HEADERS =
-EXTRA_DIST=grecs-gram.h $(PP_SETUP_FILE) Make.am Make-inst.am Make-shared.am Make-static.am
+EXTRA_DIST=grecs-gram.h $(GRECS_EXTRA_META1) $(PP_SETUP_FILE) Make.am Make-inst.am Make-shared.am Make-static.am
INCLUDES = -I$(srcdir) -I$(top_srcdir)/@GRECS_SUBDIR@ @GRECS_INCLUDES@ @GRECS_HOST_PROJECT_INCLUDES@
AM_YFLAGS = -dtv
AM_LFLAGS = -d
incdir=$(pkgdatadir)/$(VERSION)/include
inc_DATA = $(PP_SETUP_FILE)
+
+LEXCOMPILE = $(top_srcdir)/@GRECS_SUBDIR@/build-aux/yyrename '$(LEX) $(LFLAGS) $(AM_LFLAGS)'
+YACCCOMPILE = $(top_srcdir)/@GRECS_SUBDIR@/build-aux/yyrename '$(YACC) $(YFLAGS) $(AM_YFLAGS)' \ No newline at end of file
diff --git a/src/asprintf.c b/src/asprintf.c
new file mode 100644
index 0000000..d493fcb
--- /dev/null
+++ b/src/asprintf.c
@@ -0,0 +1,85 @@
+/* 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 <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include "grecs.h"
+
+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;
+}
diff --git a/src/grecs-gram.y b/src/grecs-gram.y
index 9f8d000..3452074 100644
--- a/src/grecs-gram.y
+++ b/src/grecs-gram.y
@@ -15,25 +15,23 @@
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 "yygrecs.h"
#include <grecs.h>
#include <grecs-gram.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
-static struct grecs_node *parse_tree;
-int grecs_error_count;
-
-int grecs_default_port = 0;
+int yylex(void);
+int yyerror(char *s);
+static struct grecs_node *parse_tree;
%}
%union {
struct {
grecs_locus_t locus;
char *string;
@@ -236,96 +234,30 @@ 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)
+grecs_grecs_parser(const char *name, int traceflags)
{
int rc;
- if (grecs_lex_begin(name))
+ if (grecs_lex_begin(name, traceflags & GRECS_TRACE_LEX))
return NULL;
+ yydebug = traceflags & GRECS_TRACE_GRAM;
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;
-}
diff --git a/src/grecs-lex.l b/src/grecs-lex.l
index 4d491fe..84ee858 100644
--- a/src/grecs-lex.l
+++ b/src/grecs-lex.l
@@ -1,12 +1,11 @@
/* grecs - Gray's Extensible Configuration System -*- c -*- */
%top {
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
-#include "yygrecs.h"
}
%{
/* grecs - Gray's Extensible Configuration System
Copyright (C) 2007-2011 Sergey Poznyakoff
Grecs is free software; you can redistribute it and/or modify it
@@ -45,18 +44,15 @@ grecs_locus_t grecs_current_locus; /* Input file location */
#line directives are offset by the value of XLINES.
Uff, running two preprocessors is confusing...
*/
static size_t xlines;
-static struct grecs_list *line_acc;
-
static void multiline_begin(char *);
static void multiline_add(char *);
static char *multiline_strip_tabs(char *text);
-static void line_add_unescape_last(char *text, size_t len);
static int ident(void);
static int isemptystr(int off);
static void parse_line(char *text, grecs_locus_t *ploc, size_t *pxlines);
static void parse_line_cpp(char *text, grecs_locus_t *ploc, size_t *pxlines);
@@ -110,15 +106,16 @@ P [1-9][0-9]*
grecs_line_add(yytext + 1, yyleng - 2);
yylval.string = grecs_line_finish();
return QSTRING; }
\"[^\\"\n]*\\. |
\"[^\\"\n]*\\\n { BEGIN(STR);
grecs_line_begin();
- line_add_unescape_last(yytext + 1, yyleng - 1); }
+ grecs_line_acc_grow_unescape_last(yytext + 1,
+ yyleng - 1); }
<STR>[^\\"\n]*\\. |
-<STR>\"[^\\"\n]*\\\n { line_add_unescape_last(yytext, yyleng); }
+<STR>\"[^\\"\n]*\\\n { grecs_line_acc_grow_unescape_last(yytext, yyleng); }
<STR>[^\\"\n]*\" { BEGIN(INITIAL);
if (yyleng > 1)
grecs_line_add(yytext, yyleng - 1);
yylval.string = grecs_line_finish();
return QSTRING; }
/* Multiline strings */
@@ -167,26 +164,18 @@ yywrap()
else
grecs_preproc_done();
grecs_current_locus.file = NULL;
return 1;
}
-static void
-line_acc_free_entry(void *ptr)
-{
- grecs_free(ptr);
-}
-
int
-grecs_lex_begin(const char *name)
+grecs_lex_begin(const char *name, int trace)
{
- if (yy_flex_debug > 0)
- yy_flex_debug = 0;
+ yy_flex_debug = trace;
- line_acc = grecs_list_create();
- line_acc->free_entry = line_acc_free_entry;
+ grecs_line_acc_create();
if (grecs_preprocessor) {
int fd;
fd = open(name, O_RDONLY);
if (fd == -1) {
@@ -208,13 +197,13 @@ grecs_lex_begin(const char *name)
return 0;
}
void
grecs_lex_end(int err)
{
- grecs_list_clear(line_acc);
+ grecs_line_acc_free();
}
static int
isemptystr(int off)
{
for (; yytext[off] && isspace(yytext[off]); off++)
@@ -236,101 +225,27 @@ multiline_strip_tabs(char *text)
if (char_to_strip)
for (; *text && char_to_strip(*text); text++)
;
return text;
}
-static int
-unquote_char(int c)
-{
- static char quote_transtab[] = "\\\\\"\"a\ab\bf\fn\nr\rt\tv\v";
-
- char *p;
-
- for (p = quote_transtab; *p; p += 2) {
- if (*p == c)
- return p[1];
- }
- return -1;
-}
-
-struct line_acc_entry
-{
- size_t size;
-};
-#define line_acc_ptr(entry) (char*)(entry + 1)
-
-static void
-line_acc_add_string(const char *str, size_t len)
-{
- struct line_acc_entry *ent = grecs_malloc(sizeof(*ent) + len + 1);
- char *p = line_acc_ptr(ent);
- memcpy(p, str, len);
- p[len] = 0;
- ent->size = len;
- grecs_list_append(line_acc, ent);
-}
-
-static void
-line_acc_add_char(int c)
-{
- char t = c;
- line_acc_add_string(&t, 1);
-}
-
-static void
-list_acc_unescape_char(int c)
-{
- if (c != '\n') {
- int t = unquote_char(c);
- if (t != -1)
- line_acc_add_char(t);
- else {
- grecs_warning(&grecs_current_locus, 0,
- _("unknown escape sequence '\\%c'"),
- c);
- line_acc_add_char(c);
- }
- }
-}
-
-void
-grecs_line_add(const char *text, size_t len)
-{
- line_acc_add_string(text, len);
-}
-
-/* Same, but unescapes the last character from yytext */
-static void
-line_add_unescape_last(char *text, size_t len)
-{
- line_acc_add_string(text, len - 2);
- list_acc_unescape_char(text[len - 1]);
-}
-
static void
multiline_add(char *s)
{
if (multiline_unescape) {
for (; *s; s++) {
if (*s == '\\') {
- list_acc_unescape_char(s[1]);
+ grecs_line_acc_grow_char_unescape(s[1]);
++s;
} else
- line_acc_add_char(*s);
+ grecs_line_acc_grow_char(*s);
}
} else
grecs_line_add(s, strlen(s));
}
-void
-grecs_line_begin()
-{
- /* FIXME: nothing so far. Maybe prepare stk by calling obstack_finish? */
-}
-
static int
is_tab(char c)
{
return c == '\t';
}
@@ -371,36 +286,12 @@ multiline_begin(char *p)
multiline_delimiter = grecs_malloc(multiline_delimiter_len + 1);
memcpy(multiline_delimiter, p, multiline_delimiter_len);
multiline_delimiter[multiline_delimiter_len] = 0;
grecs_line_begin();
}
-char *
-grecs_line_finish()
-{
- struct grecs_list_entry *ep;
- size_t size = 0;
- char *str, *p;
-
- for (ep = line_acc->head; ep; ep = ep->next) {
- struct line_acc_entry *ent = ep->data;
- size += ent->size;
- }
-
- str = grecs_malloc(size + 1);
- for (ep = line_acc->head, p = str; ep; ep = ep->next) {
- struct line_acc_entry *ent = ep->data;
- char *str = line_acc_ptr(ent);
- memcpy(p, str, ent->size);
- p += ent->size;
- }
- *p = 0;
- grecs_list_clear(line_acc);
- return str;
-}
-
static int
ident()
{
char *p;
char *str;
size_t len;
@@ -413,18 +304,12 @@ ident()
strcpy(str, p);
yylval.ident.locus = grecs_current_locus;
yylval.ident.string = str;
return IDENT;
}
-void
-grecs_lex_trace(int n)
-{
- yy_flex_debug = -n;
-}
-
grecs_value_t *
grecs_value_ptr_from_static(grecs_value_t *input)
{
grecs_value_t *ptr = grecs_malloc(sizeof(*ptr));
*ptr = *input;
return ptr;
diff --git a/src/grecs.h b/src/grecs.h
index 61a915b..ff47698 100644
--- a/src/grecs.h
+++ b/src/grecs.h
@@ -195,24 +195,45 @@ grecs_value_t *grecs_value_ptr_from_static(grecs_value_t *input);
extern void (*grecs_print_diag_fun)(grecs_locus_t *, int, int, const char*);
void grecs_warning(grecs_locus_t *locus, int errcode, const char *fmt, ...)
__attribute__ ((__format__ (__printf__, 3, 4)));
void grecs_error(grecs_locus_t *locus, int errcode, const char *fmt, ...)
__attribute__ ((__format__ (__printf__, 3, 4)));
+
+
+extern int grecs_trace_flags;
+
+#define GRECS_TRACE_GRAM 0x01
+#define GRECS_TRACE_LEX 0x02
void grecs_gram_trace(int n);
void grecs_lex_trace(int n);
+
-int grecs_lex_begin(const char*);
+int grecs_lex_begin(const char*, int);
void grecs_lex_end(int err);
struct grecs_node *grecs_parse(const char *name);
+
+extern struct grecs_node *(*grecs_parser_fun)(const char *name, int trace);
+
+/* Parsers: */
+struct grecs_node *grecs_grecs_parser(const char *name, int traceflags);
+struct grecs_node *grecs_meta1_parser(const char *name, int traceflags);
+
struct grecs_list *_grecs_simple_list_create(int dispose);
struct grecs_list *grecs_value_list_create(void);
+void grecs_line_acc_create(void);
+void grecs_line_acc_free(void);
+void grecs_line_acc_grow_char(int c);
+void grecs_line_acc_grow_char_unescape(int c);
+void grecs_line_acc_grow(const char *text, size_t len);
+void grecs_line_acc_grow_unescape_last(char *text, size_t len);
+
void grecs_line_begin(void);
-void grecs_line_add(const char *text, size_t len);
+#define grecs_line_add grecs_line_acc_grow
char *grecs_line_finish(void);
extern int grecs_string_convert(void *target, enum grecs_data_type type,
const char *string, grecs_locus_t *locus);
extern void grecs_process_ident(struct grecs_keyword *kwp,
grecs_value_t *value,
@@ -292,12 +313,19 @@ void grecs_list_clear(struct grecs_list *lp);
void grecs_list_free(struct grecs_list *lp);
void grecs_list_add(struct grecs_list *dst, struct grecs_list *src);
int grecs_vasprintf(char **pbuf, size_t *psize, const char *fmt, va_list ap);
int grecs_asprintf(char **pbuf, size_t *psize, const char *fmt, ...);
+#define GRECS_TXTACC_BUFSIZE 1024
+struct grecs_txtacc *grecs_txtacc_create(void);
+void grecs_txtacc_free(struct grecs_txtacc *acc);
+void grecs_txtacc_grow(struct grecs_txtacc *acc, const char *buf, size_t size);
+char *grecs_txtacc_finish(struct grecs_txtacc *acc, int steal);
+void grecs_txtacc_free_string(struct grecs_txtacc *acc, char *str);
+
struct grecs_symtab;
struct grecs_syment {
char *name;
};
@@ -360,12 +388,15 @@ struct grecs_node *grecs_match_first(struct grecs_node *tree,
const char *pattern,
grecs_match_buf_t *buf);
struct grecs_node *grecs_match_next(struct grecs_match_buf *buf);
void grecs_match_buf_free(struct grecs_match_buf *buf);
int grecs_value_eq(struct grecs_value *a, struct grecs_value *b);
+int grecs_value_match(struct grecs_value *pat, struct grecs_value *b,
+ int flags);
+
struct grecs_node *grecs_find_node(struct grecs_node *node, const char *path);
struct grecs_node *grecs_node_from_path(const char *path, const char *value);
int grecs_tree_reduce(struct grecs_node *node, struct grecs_keyword *kwd,
int flags);
diff --git a/src/lineacc.c b/src/lineacc.c
new file mode 100644
index 0000000..a3f4836
--- /dev/null
+++ b/src/lineacc.c
@@ -0,0 +1,82 @@
+/* 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 <wordsplit.h>
+
+static struct grecs_txtacc *line_acc;
+
+void
+grecs_line_acc_create()
+{
+ line_acc = grecs_txtacc_create();
+}
+
+void
+grecs_line_acc_free()
+{
+ grecs_txtacc_free(line_acc);
+ line_acc = NULL;
+}
+
+void
+grecs_line_acc_grow_char(int c)
+{
+ char t = c;
+ grecs_txtacc_grow(line_acc, &t, 1);
+}
+
+void
+grecs_line_acc_grow_char_unescape(int c)
+{
+ if (c != '\n')
+ grecs_line_acc_grow_char(wordsplit_c_unquote_char(c));
+}
+
+void
+grecs_line_acc_grow(const char *text, size_t len)
+{
+ grecs_txtacc_grow(line_acc, text, len);
+}
+
+/* Same, but unescapes the last character from text */
+void
+grecs_line_acc_grow_unescape_last(char *text, size_t len)
+{
+ grecs_txtacc_grow(line_acc, text, len - 2);
+ grecs_line_acc_grow_char_unescape(text[len - 1]);
+}
+
+void
+grecs_line_begin()
+{
+ if (!line_acc)
+ grecs_line_acc_create();
+}
+
+char *
+grecs_line_finish()
+{
+ grecs_line_acc_grow_char(0);
+ return grecs_txtacc_finish(line_acc, 1);
+}
+
+
+
+
diff --git a/src/meta1-gram.y b/src/meta1-gram.y
new file mode 100644
index 0000000..8ddcbfb
--- /dev/null
+++ b/src/meta1-gram.y
@@ -0,0 +1,217 @@
+%{
+/* MeTA1 configuration parser for Grecs.
+ 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 <errno.h>
+#include <string.h>
+#include "grecs.h"
+
+int yylex(void);
+int yyerror(char *s);
+
+static struct grecs_node *parse_tree;
+extern int yy_flex_debug;
+%}
+
+%union {
+ struct {
+ grecs_locus_t locus;
+ char *string;
+ } ident;
+ char *string;
+ grecs_value_t svalue, *pvalue;
+ struct grecs_list *list;
+ struct { struct grecs_node *head, *tail; } node_list;
+ struct grecs_node *node;
+}
+
+%token <ident> META1_IDENT
+%token <string> META1_STRING
+%type <node> stmt simple block
+%type <node_list> stmtlist
+%type <pvalue> tag value
+%type <string> string slist
+%type <list> slist0
+%type <list> values list
+%%
+
+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 : META1_IDENT '=' value opt_sc
+ {
+ $$ = grecs_node_create(grecs_node_stmt,
+ &$1.locus);
+ $$->ident = $1.string;
+ $$->v.value = $3;
+ }
+ ;
+
+block : META1_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;
+ }
+ | META1_IDENT
+ {
+ $$ = grecs_malloc(sizeof($$[0]));
+ $$->type = GRECS_TYPE_STRING;
+ $$->v.string = $1.string;
+ }
+ ;
+
+value : string
+ {
+ $$ = grecs_malloc(sizeof($$[0]));
+ $$->type = GRECS_TYPE_STRING;
+ $$->v.string = $1;
+ }
+ | list
+ {
+ $$ = grecs_malloc(sizeof($$[0]));
+ $$->type = GRECS_TYPE_LIST;
+ $$->v.list = $1;
+ }
+ ;
+
+string : META1_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 : META1_STRING
+ {
+ $$ = grecs_list_create();
+ grecs_list_append($$, $1);
+ }
+ | slist0 META1_STRING
+ {
+ grecs_list_append($1, $2);
+ $$ = $1;
+ }
+ ;
+
+list : '{' values '}'
+ {
+ $$ = $2;
+ }
+ | '{' values ',' '}'
+ {
+ $$ = $2;
+ }
+ ;
+
+values : value
+ {
+ $$ = grecs_value_list_create();
+ grecs_list_append($$, $1);
+ }
+ | values ',' value
+ {
+ grecs_list_append($1, $3);
+ $$ = $1;
+ }
+ ;
+
+opt_sc : /* empty */
+ | ';'
+ ;
+
+%%
+int
+yyerror(char *s)
+{
+ grecs_error(&grecs_current_locus, 0, "%s", s);
+ return 0;
+}
+
+struct grecs_node *
+grecs_meta1_parser(const char *name, int traceflags)
+{
+ int rc;
+ FILE *fp;
+
+ fp = fopen(name, "r");
+ if (!fp) {
+ grecs_error(NULL, errno, _("Cannot open `%s'"), name);
+ return NULL;
+ }
+ yyset_in(fp);
+
+ yy_flex_debug = traceflags & GRECS_TRACE_LEX;
+ yydebug = traceflags & GRECS_TRACE_GRAM;
+ parse_tree = NULL;
+ grecs_line_acc_create();
+ rc = yyparse();
+ fclose(fp);
+ if (grecs_error_count)
+ rc = 1;
+ grecs_line_acc_free();
+ if (rc) {
+ grecs_tree_free(parse_tree);
+ parse_tree = NULL;
+ }
+ return parse_tree;
+}
+
diff --git a/src/meta1-lex.l b/src/meta1-lex.l
new file mode 100644
index 0000000..cb2931d
--- /dev/null
+++ b/src/meta1-lex.l
@@ -0,0 +1,114 @@
+/* MeTA1 configuration lexer for Grecs. -*- c -*- */
+%top {
+/* MeTA1 configuration lexer for Grecs.
+ 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/>. */
+
+/* This file implements a lexical analyzer for MeTA1 main configuration file.
+ */
+
+#ifndef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include "grecs.h"
+#include "meta1-gram.h"
+#include <ctype.h>
+}
+
+%{
+static int yywrap(void);
+static void meta1_line_add_unescape_hex(const char *text, size_t len);
+%}
+
+%x COMMENT STR
+X [0-9a-fA-F]
+%%
+ /* C-style comments */
+"/*" BEGIN (COMMENT);
+<COMMENT>[^*\n]* /* eat anything that's not a '*' */
+<COMMENT>"*"+[^*/\n]* /* eat up '*'s not followed by '/'s */
+<COMMENT>\n ++grecs_current_locus.line;
+<COMMENT>"*"+"/" BEGIN (INITIAL);
+ /* End-of-line comments */
+#.*\n { grecs_current_locus.line++; }
+#.* /* end-of-file comment */;
+ /* Number */
+0[xX]{X}+ |
+0[0-7]+ |
+[1-9][0-9]+ { grecs_line_begin();
+ grecs_line_add(yytext, yyleng);
+ yylval.string = grecs_line_finish();
+ return META1_STRING; }
+ /* Identifiers (unquoted strings) */
+[a-zA-Z0-9_\./:\*-]+ { grecs_line_begin();
+ grecs_line_add(yytext, yyleng);
+ yylval.ident.locus = grecs_current_locus;
+ yylval.ident.string = grecs_line_finish();
+ return META1_IDENT; }
+ /* Quoted strings */
+\"[^\\"\n]*\" { grecs_line_begin();
+ grecs_line_add(yytext + 1, yyleng - 2);
+ yylval.string = grecs_line_finish();
+ return META1_STRING; }
+\"[^\\"\n]*\\x{X}{1,2} { BEGIN(STR);
+ grecs_line_begin();
+ meta1_line_add_unescape_hex(yytext + 1, yyleng - 1);
+ }
+\"[^\\"\n]*\\. { BEGIN(STR);
+ grecs_line_begin();
+ grecs_line_acc_grow_unescape_last(yytext + 1,
+ yyleng - 1); }
+<STR>[^\\"\n]*\\x{X}{1,2} { meta1_line_add_unescape_hex(yytext, yyleng); }
+<STR>[^\\"\n]*\\. { grecs_line_acc_grow_unescape_last(yytext, yyleng); }
+<STR>[^\\"\n]*\" { BEGIN(INITIAL);
+ if (yyleng > 1)
+ grecs_line_add(yytext, yyleng - 1);
+ yylval.string = grecs_line_finish();
+ return META1_STRING; }
+<STR>[^\\"\n]*\n { BEGIN(INITIAL);
+ grecs_error(&grecs_current_locus, 0,
+ _("newline in a string"));
+ grecs_line_add (yytext, yyleng - 1);
+ yylval.string = grecs_line_finish ();
+ return META1_STRING; }
+ /* Other tokens */
+[ \t\f][ \t\f]* ;
+\n { grecs_current_locus.line++; }
+[,;{}=] return yytext[0];
+. { grecs_error(&grecs_current_locus, 0,
+ (isascii(yytext[0]) && isprint(yytext[0])) ?
+ _("stray character %c") :
+ _("stray character \\%03o"),
+ (unsigned char) yytext[0]); }
+%%
+
+int
+yywrap()
+{
+ return 1;
+}
+
+static void
+meta1_line_add_unescape_hex(const char *text, size_t len)
+{
+ for (; text[len-1] != 'x' && len > 0; len--)
+ ;
+ grecs_line_acc_grow(text, len - 2);
+ grecs_line_acc_grow_char((char) strtoul (text + len, NULL, 16));
+}
+
+
+
+
diff --git a/src/parser.c b/src/parser.c
new file mode 100644
index 0000000..6f84bd8
--- /dev/null
+++ b/src/parser.c
@@ -0,0 +1,60 @@
+/* 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 th