diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2011-05-15 13:51:03 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2011-05-15 14:05:39 +0300 |
commit | a0826b4c7cd66c4862d9b61bb7e14d73fcba28f6 (patch) | |
tree | 71762fca263967e5803ce803b0a0aa94555e125d | |
parent | fd64fa62bc68d8c2e0d693033e874fcc1f023544 (diff) | |
download | grecs-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.
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | am/grecs.m4 | 7 | ||||
-rwxr-xr-x | build-aux/yyrename | 92 | ||||
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | doc/GRECS_SETUP.3 | 8 | ||||
-rw-r--r-- | doc/grecs_parse.3 | 8 | ||||
-rw-r--r-- | src/.gitignore | 4 | ||||
-rw-r--r-- | src/Make.am | 19 | ||||
-rw-r--r-- | src/asprintf.c | 85 | ||||
-rw-r--r-- | src/grecs-gram.y | 80 | ||||
-rw-r--r-- | src/grecs-lex.l | 133 | ||||
-rw-r--r-- | src/grecs.h | 35 | ||||
-rw-r--r-- | src/lineacc.c | 82 | ||||
-rw-r--r-- | src/meta1-gram.y | 217 | ||||
-rw-r--r-- | src/meta1-lex.l | 114 | ||||
-rw-r--r-- | src/parser.c | 60 | ||||
-rw-r--r-- | src/txtacc.c | 187 | ||||
-rw-r--r-- | src/wordsplit.c | 2 | ||||
-rw-r--r-- | src/yygrecs.h | 28 | ||||
-rw-r--r-- | src/yytrans | 20 |
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 @@ -18,3 +18,3 @@ 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 diff --git a/am/grecs.m4 b/am/grecs.m4 index 0d1b03d..8f4868b 100644 --- a/am/grecs.m4 +++ b/am/grecs.m4 @@ -150,3 +150,7 @@ AC_DEFUN([GRECS_SETUP],[ _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) @@ -162,2 +166,3 @@ AC_DEFUN([GRECS_SETUP],[ AC_SUBST([GRECS_HOST_PROJECT_INCLUDES]) + _GRECS_OPTION_SWITCH([install],[ 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 @@ -39,3 +39,3 @@ AC_HEADER_STDC -GRECS_SETUP(., [install tests git2chg]) +GRECS_SETUP(., [install tests git2chg all-parsers]) 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 @@ -16,3 +16,3 @@ .\" -.TH GRECS_SETUP 3 "May 8, 2011" "GRECS" "Grecs User Reference" +.TH GRECS_SETUP 3 "May 15, 2011" "GRECS" "Grecs User Reference" .SH NAME @@ -46,2 +46,8 @@ 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 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 @@ -151,9 +151,7 @@ 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); diff --git a/src/.gitignore b/src/.gitignore index 9ef1b57..feb5040 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -4,2 +4,6 @@ grecs-gram.output grecs-lex.c +meta1-gram.c +meta1-gram.h +meta1-gram.output +meta1-lex.c Make-inst.in diff --git a/src/Make.am b/src/Make.am index 915e6c4..60c0f06 100644 --- a/src/Make.am +++ b/src/Make.am @@ -16,3 +16,9 @@ +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\ @@ -22,2 +28,3 @@ GRECS_SRC = \ join.c\ + lineacc.c\ list.c\ @@ -25,2 +32,3 @@ GRECS_SRC = \ mem.c\ + parser.c\ preproc.c\ @@ -30,8 +38,10 @@ GRECS_SRC = \ 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 @@ -43 +53,4 @@ 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 @@ -20,3 +20,2 @@ #endif -#include "yygrecs.h" #include <grecs.h> @@ -28,7 +27,6 @@ -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; %} @@ -241,70 +239,9 @@ yyerror(char *s) -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; @@ -321,7 +258,2 @@ grecs_parse(const char *name) -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 @@ -5,3 +5,2 @@ #endif -#include "yygrecs.h" } @@ -50,4 +49,2 @@ static size_t xlines; -static struct grecs_list *line_acc; - static void multiline_begin(char *); @@ -55,3 +52,2 @@ 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); @@ -115,5 +111,6 @@ P [1-9][0-9]* 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); @@ -172,16 +169,8 @@ yywrap() -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(); @@ -213,3 +202,3 @@ grecs_lex_end(int err) { - grecs_list_clear(line_acc); + grecs_line_acc_free(); } @@ -241,70 +230,2 @@ multiline_strip_tabs(char *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 @@ -315,6 +236,6 @@ multiline_add(char *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); } @@ -324,8 +245,2 @@ multiline_add(char *s) -void -grecs_line_begin() -{ - /* FIXME: nothing so far. Maybe prepare stk by calling obstack_finish? */ -} - static int @@ -376,26 +291,2 @@ multiline_begin(char *p) -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 @@ -418,8 +309,2 @@ ident() -void -grecs_lex_trace(int n) -{ - yy_flex_debug = -n; -} - grecs_value_t * diff --git a/src/grecs.h b/src/grecs.h index 61a915b..ff47698 100644 --- a/src/grecs.h +++ b/src/grecs.h @@ -200,9 +200,23 @@ 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); @@ -210,4 +224,11 @@ 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); @@ -297,2 +318,9 @@ 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; @@ -365,2 +393,5 @@ 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); 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; } |