aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am2
-rw-r--r--am/grecs.m47
-rwxr-xr-xbuild-aux/yyrename92
-rw-r--r--configure.ac2
-rw-r--r--doc/GRECS_SETUP.38
-rw-r--r--doc/grecs_parse.38
-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
20 files changed, 944 insertions, 241 deletions
diff --git a/Makefile.am b/Makefile.am
index d614a25..0696c72 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -16,7 +16,7 @@
ACLOCAL_AMFLAGS = -I am
SUBDIRS=. src @GRECS_TESTDIR@ @GRECS_DOCDIR@
-EXTRA_DIST=@GRECS_BUILD_AUX@ gitid.h
+EXTRA_DIST=build-aux/yyrename @GRECS_BUILD_AUX@ gitid.h
noinst_HEADERS = gitid.h
BUILT_SOURCES = gitid.h README
diff --git a/am/grecs.m4 b/am/grecs.m4
index 0d1b03d..8f4868b 100644
--- a/am/grecs.m4
+++ b/am/grecs.m4
@@ -148,7 +148,11 @@ AC_DEFUN([GRECS_SETUP],[
GRECS_BUILD_AUX="build-aux/getopt.m4"
])
_GRECS_IF_OPTION_SET([git2chg],[GRECS_BUILD_AUX="$GRECS_BUILD_AUX build-aux/git2chg.awk"])
-
+ AM_CONDITIONAL([GRECS_COND_META1_PARSER],
+ _GRECS_OPTION_SWITCH([parser-meta1],[true],
+ [all-parsers],[true],
+ [false]))
+
AC_SUBST([GRECS_SRCDIR],$1)
AC_SUBST([GRECS_BUILD_AUX])
AC_SUBST([GRECS_INCLUDES])
@@ -160,6 +164,7 @@ AC_DEFUN([GRECS_SETUP],[
AC_SUBST([GRECS_README])
AC_SUBST([GRECS_INCLUDES],['-I$(top_srcdir)/]grecsdir[src]')
AC_SUBST([GRECS_HOST_PROJECT_INCLUDES])
+
_GRECS_OPTION_SWITCH([install],[
LT_INIT
GRECS_LDADD=['$(top_builddir)/]grecsdir[src/libgrecs.la']
diff --git a/build-aux/yyrename b/build-aux/yyrename
new file mode 100755
index 0000000..5e7b1db
--- /dev/null
+++ b/build-aux/yyrename
@@ -0,0 +1,92 @@
+#! /bin/sh
+# Rename yy.* identifiers to avoid name clashes. This file is part of Grecs.
+# Copyright (C) 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, 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/>.
+
+# Usage: yyrename [-f OUTFILE [OUTFILE...]] COMMAND INFILE
+# Makefile.am:
+# LEXCOMPILE = yyrename -f $(LEX_OUTPUT_ROOT).c \
+# '$(LEX) $(LFLAGS) $(AM_LFLAGS)'
+# YACCCOMPILE = yyrename '$(YACC) $(YFLAGS) $(AM_YFLAGS)'
+#
+# This script runs COMMAND with INFILE as its argument and scans
+# OUTFILEs for identifiers starting with 'yy'. These identifiers
+# are renamed by replacing 'yy' with the selected prefix.
+#
+# The prefix is looked up in the file yytrans, located in the INFILE's
+# directory. If this file does not exist, the prefix is constructed
+# by concatenating the string 'grecs_' and the ``root name''. The root
+# name is build by removing '-lex.l' or '-gram.y' from the base name.
+# If the latter does not end in any of these, the root name
+# is constructed by removing the suffix from the base name.
+#
+# The yytrans file is a line-oriented lookup table. Empty lines are
+# ignored, usual UNIX comment lines are honored. The remaining lines
+# must consist of two words separated by any amount of whitespace.
+# The first word is a look-up key, the second one provides a translation
+# (yy replacement) for that key.
+#
+# Two look-ups are tried: first the base name and then the root name.
+# If both result in a non-empty replacement, the former is preferred
+# over the latter.
+#
+# The -f option supplies a list of output file names generated by COMMAND.
+# If not supplied, the following defaults are used: y.tab.c and y.tab.h, if
+# INFILE ends in '.y', and yy.lex.c, if it ends in '.l'. If INFILE does not
+# end in any of these suffixes, error is reported.
+#
+# BUGS: Any occurrence of 'yy' is replaced, not only 'yy' prefixes.
+#
+case $1 in
+-f) files=$2
+ shift
+ shift
+esac
+
+base=`expr "$2" : '.*/\(.*\)\.[ly]'`
+dir=`dirname "$2"`
+case $2 in
+*.y) test -z "$files" && files="y.tab.c y.tab.h"
+ root=`expr "$2" : '.*/\(.*\)-gram\.y'`;;
+*.l) test -z "$files" && files=lex.yy.c
+ root=`expr "$2" : '.*/\(.*\)-lex\.l'`;;
+*) if test -z "$files"; then
+ echo >&2 "$0: suffix unknown, files must be given (use -f)"
+ exit 1
+ fi
+ root=$base
+esac
+
+if test -f $dir/yytrans; then
+ pfx=`awk '
+{ sub(/#.*$/,"") }
+NF == 2 && $1=="'$base'" { exact=$2 }
+NF == 2 && $1=="'$root'" { root=$2 }
+{ next }
+END { print exact ? exact : root ? root : "" }' $dir/yytrans`
+else
+ pfx=
+fi
+if test -z "$pfx"; then
+ pfx=grecs_`echo $root | tr .- __`
+fi
+
+eval $* || exit $?
+
+for file in $files
+do
+ mv $file ${file}.tmp
+ sed "s/yy/$pfx/g" ${file}.tmp > $file
+done
diff --git a/configure.ac b/configure.ac
index 980c713..816de11 100644
--- a/configure.ac
+++ b/configure.ac
@@ -37,6 +37,6 @@ AC_HEADER_STDC
# Grecs subsystem
-GRECS_SETUP(., [install tests git2chg])
+GRECS_SETUP(., [install tests git2chg all-parsers])
AC_OUTPUT
diff --git a/doc/GRECS_SETUP.3 b/doc/GRECS_SETUP.3
index c24399e..13b568e 100644
--- a/doc/GRECS_SETUP.3
+++ b/doc/GRECS_SETUP.3
@@ -14,7 +14,7 @@
.\" You should have received a copy of the GNU General Public License
.\" along with Grecs. If not, see <http://www.gnu.org/licenses/>.
.\"
-.TH GRECS_SETUP 3 "May 8, 2011" "GRECS" "Grecs User Reference"
+.TH GRECS_SETUP 3 "May 15, 2011" "GRECS" "Grecs User Reference"
.SH NAME
GRECS_SETUP \- Initialize \fBgrecs\fR submodule.
.SH SYNOPSIS
@@ -44,6 +44,12 @@ The
argument is a space-separated list of options. The following options
are understood:
.TP
+.B all-parsers
+Compile all available parsers.
+.TP
+.B parser-meta1
+Build the parser for MeTA1 configuration files.
+.TP
.B no-preproc
Disable the use of preprocessor.
.TP
diff --git a/doc/grecs_parse.3 b/doc/grecs_parse.3
index 2abc417..dff6759 100644
--- a/doc/grecs_parse.3
+++ b/doc/grecs_parse.3
@@ -149,13 +149,11 @@ screen:
int
main(int argc, char **argv)
{
- struct grecs_node *tree, *node;
+ struct grecs_node *tree;
tree = grecs_parse(argv[1]);
- for (node = tree; node; node = node->next) {
- grecs_format_node(node, GRECS_NODE_FLAG_DEFAULT, stdout);
- fputc('\n', stdout);
- }
+ grecs_format_node(tree, GRECS_NODE_FLAG_DEFAULT, stdout);
+ fputc('\\n', stdout);
grecs_tree_free(tree);
exit(0);
}
diff --git a/src/.gitignore b/src/.gitignore
index 9ef1b57..feb5040 100644
--- a/src/.gitignore
+++ b/src/.gitignore
@@ -2,6 +2,10 @@ 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
@@ -14,26 +14,36 @@
# 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
@@ -41,3 +51,6 @@ 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
@@ -18,7 +18,6 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
-#include "yygrecs.h"
#include <grecs.h>
#include <grecs-gram.h>
#include <stdlib.h>
@@ -26,11 +25,10 @@
#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 {
@@ -239,74 +237,13 @@ yyerror(char *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)
@@ -319,11 +256,6 @@ grecs_parse(const char *name)
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
@@ -3,7 +3,6 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
-#include "yygrecs.h"
}
%{
/* grecs - Gray's Extensible Configuration System
@@ -48,12 +47,9 @@ grecs_locus_t grecs_current_locus; /* Input file location */
*/
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);
@@ -113,9 +109,10 @@ P [1-9][0-9]*
\"[^\\"\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);
@@ -170,20 +167,12 @@ yywrap()
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;
@@ -211,7 +200,7 @@ grecs_lex_begin(const char *name)
void
grecs_lex_end(int err)
{
- grecs_list_clear(line_acc);
+ grecs_line_acc_free();
}
static int
@@ -239,95 +228,21 @@ multiline_strip_tabs(char *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)
{
@@ -374,30 +289,6 @@ multiline_begin(char *p)
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()
{
@@ -416,12 +307,6 @@ ident()
return IDENT;
}
-void
-grecs_lex_trace(int n)
-{
- yy_flex_debug = -n;
-}
-
grecs_value_t *
grecs_value_ptr_from_static(grecs_value_t *input)
{
diff --git a/src/grecs.h b/src/grecs.h
index 61a915b..ff47698 100644
--- a/src/grecs.h
+++ b/src/grecs.h
@@ -198,18 +198,39 @@ 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,
@@ -295,6 +316,13 @@ 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 {
@@ -363,6 +391,9 @@ 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);
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)