From a0826b4c7cd66c4862d9b61bb7e14d73fcba28f6 Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Sun, 15 May 2011 13:51:03 +0300 Subject: 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. --- Makefile.am | 2 +- am/grecs.m4 | 7 +- build-aux/yyrename | 92 +++++++++++++++++++++++ configure.ac | 2 +- doc/GRECS_SETUP.3 | 8 +- doc/grecs_parse.3 | 8 +- src/.gitignore | 4 + src/Make.am | 19 ++++- src/asprintf.c | 85 +++++++++++++++++++++ src/grecs-gram.y | 80 ++------------------ src/grecs-lex.l | 133 +++----------------------------- src/grecs.h | 35 ++++++++- src/lineacc.c | 82 ++++++++++++++++++++ src/meta1-gram.y | 217 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/meta1-lex.l | 114 ++++++++++++++++++++++++++++ src/parser.c | 60 +++++++++++++++ src/txtacc.c | 187 +++++++++++++++++++++++++++++++++++++++++++++ src/wordsplit.c | 2 +- src/yygrecs.h | 28 ------- src/yytrans | 20 +++++ 20 files changed, 944 insertions(+), 241 deletions(-) create mode 100755 build-aux/yyrename create mode 100644 src/asprintf.c create mode 100644 src/lineacc.c create mode 100644 src/meta1-gram.y create mode 100644 src/meta1-lex.l create mode 100644 src/parser.c create mode 100644 src/txtacc.c delete mode 100644 src/yygrecs.h create mode 100644 src/yytrans 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 . + +# 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 . .\" -.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 . +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 . */ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include +#include +#include +#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 #endif -#include "yygrecs.h" #include #include #include @@ -26,11 +25,10 @@ #include #include -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 #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); } [^\\"\n]*\\. | -\"[^\\"\n]*\\\n { line_add_unescape_last(yytext, yyleng); } +\"[^\\"\n]*\\\n { grecs_line_acc_grow_unescape_last(yytext, yyleng); } [^\\"\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 . */ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include +#include + +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 . */ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include +#include +#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 META1_IDENT +%token META1_STRING +%type stmt simple block +%type stmtlist +%type tag value +%type string slist +%type slist0 +%type 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 . */ + +/* This file implements a lexical analyzer for MeTA1 main configuration file. + */ + +#ifndef HAVE_CONFIG_H +# include +#endif +#include "grecs.h" +#include "meta1-gram.h" +#include +} + +%{ +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); +[^*\n]* /* eat anything that's not a '*' */ +"*"+[^*/\n]* /* eat up '*'s not followed by '/'s */ +\n ++grecs_current_locus.line; +"*"+"/" 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); } +[^\\"\n]*\\x{X}{1,2} { meta1_line_add_unescape_hex(yytext, yyleng); } +[^\\"\n]*\\. { grecs_line_acc_grow_unescape_last(yytext, yyleng); } +[^\\"\n]*\" { BEGIN(INITIAL); + if (yyleng > 1) + grecs_line_add(yytext, yyleng - 1); + yylval.string = grecs_line_finish(); + return META1_STRING; } +[^\\"\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 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 "grecs.h" + +int grecs_error_count; +int grecs_default_port = 0; + +int grecs_trace_flags = 0; + +#ifndef GRECS_DEFAULT_PARSER +# define GRECS_DEFAULT_PARSER grecs_grecs_parser +#endif + +struct grecs_node *(*grecs_parser_fun)(const char *name, int trace) = + GRECS_DEFAULT_PARSER; + +void +grecs_gram_trace(int n) +{ + if (n) + grecs_trace_flags |= GRECS_TRACE_GRAM; + else + grecs_trace_flags &= ~GRECS_TRACE_GRAM; +} + +void +grecs_lex_trace(int n) +{ + if (n) + grecs_trace_flags |= GRECS_TRACE_LEX; + else + grecs_trace_flags &= ~GRECS_TRACE_LEX; +} + +struct grecs_node * +grecs_parse(const char *name) +{ + grecs_error_count = 0; + return grecs_parser_fun(name, grecs_trace_flags); +} diff --git a/src/txtacc.c b/src/txtacc.c new file mode 100644 index 0000000..ff13e42 --- /dev/null +++ b/src/txtacc.c @@ -0,0 +1,187 @@ +/* wydawca - automatic release submission daemon + Copyright (C) 2007, 2009-2011 Sergey Poznyakoff + + Wydawca 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. + + Wydawca 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 wydawca. If not, see . */ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include +#include "grecs.h" + +struct grecs_txtacc_entry +{ + char *buf; /* Text buffer */ + size_t size; /* Buffer size */ + size_t len; /* Actual number of bytes in buffer */ +}; +#define TXTACC_BUFSIZE 1024 +#define grecs_txtacc_entry_freesize(e) ((e)->size - (e)->len) + +struct grecs_txtacc +{ + struct grecs_list *cur; /* Current build list */ + struct grecs_list *mem; /* List of already allocated elements */ +}; + +static struct grecs_txtacc_entry * +grecs_txtacc_alloc_entry(struct grecs_list *list, size_t size) +{ + struct grecs_txtacc_entry *p = grecs_malloc(sizeof (*p)); + p->buf = grecs_malloc(size); + p->size = size; + p->len = 0; + grecs_list_append(list, p); + return p; +} + +static struct grecs_txtacc_entry * +grecs_txtacc_cur_entry(struct grecs_txtacc *acc) +{ + struct grecs_txtacc_entry *ent; + + if (grecs_list_size(acc->cur) == 0) + return grecs_txtacc_alloc_entry(acc->cur, + GRECS_TXTACC_BUFSIZE); + ent = acc->cur->tail->data; + if (grecs_txtacc_entry_freesize(ent) == 0) + ent = grecs_txtacc_alloc_entry(acc->cur, + GRECS_TXTACC_BUFSIZE); + return ent; +} + +static void +grecs_txtacc_entry_append(struct grecs_txtacc_entry *ent, + const char *p, size_t size) +{ + memcpy(ent->buf + ent->len, p, size); + ent->len += size; +} + +static void +grecs_txtacc_entry_tailor(struct grecs_txtacc_entry *ent) +{ + if (ent->size > ent->len) { + char *p = realloc(ent->buf, ent->len); + if (!p) + return; + ent->buf = p; + ent->size = ent->len; + } +} + +static void +grecs_txtacc_entry_free(void *p) +{ + if (p) { + struct grecs_txtacc_entry *ent = p; + free(ent->buf); + free(ent); + } +} + +struct grecs_txtacc * +grecs_txtacc_create() +{ + struct grecs_txtacc *acc = grecs_malloc(sizeof (*acc)); + acc->cur = grecs_list_create(); + acc->cur->free_entry = grecs_txtacc_entry_free; + acc->mem = grecs_list_create(); + acc->mem->free_entry = grecs_txtacc_entry_free; + return acc; +} + +void +grecs_txtacc_free(struct grecs_txtacc *acc) +{ + grecs_list_free (acc->cur); + grecs_list_free (acc->mem); + free (acc); +} + +void +grecs_txtacc_grow(struct grecs_txtacc *acc, const char *buf, size_t size) +{ + while (size) { + struct grecs_txtacc_entry *ent = grecs_txtacc_cur_entry(acc); + size_t rest = grecs_txtacc_entry_freesize(ent); + if (rest > size) + rest = size; + grecs_txtacc_entry_append(ent, buf, rest); + buf += rest; + size -= rest; + } +} + +char * +grecs_txtacc_finish(struct grecs_txtacc *acc, int steal) +{ + struct grecs_list_entry *ep; + struct grecs_txtacc_entry *txtent; + size_t size; + char *p; + + switch (grecs_list_size(acc->cur)) { + case 0: + return NULL; + + case 1: + txtent = acc->cur->head->data; + acc->cur->head->data = NULL; + grecs_txtacc_entry_tailor(txtent); + grecs_list_append(acc->mem, txtent); + break; + + default: + size = 0; + for (ep = acc->cur->head; ep; ep = ep->next) { + txtent = ep->data; + size += txtent->len; + } + + txtent = grecs_txtacc_alloc_entry(acc->mem, size); + for (ep = acc->cur->head; ep; ep = ep->next) { + struct grecs_txtacc_entry *tp = ep->data; + grecs_txtacc_entry_append(txtent, tp->buf, tp->len); + } + } + + grecs_list_clear(acc->cur); + p = txtent->buf; + if (steal) { + grecs_list_remove_tail(acc->mem); + free(txtent); + } + return p; +} + +void +grecs_txtacc_free_string(struct grecs_txtacc *acc, char *str) +{ + struct grecs_list_entry *ep; + for (ep = acc->mem->head; ep; ep = ep->next) { + struct grecs_txtacc_entry *tp = ep->data; + if (tp->buf == str) { + grecs_list_remove_entry(acc->mem, ep); + grecs_free(tp->buf); + return; + } + } +} + +void +grecs_txtacc_clear(struct grecs_txtacc *acc) +{ + grecs_list_clear(acc->cur); +} diff --git a/src/wordsplit.c b/src/wordsplit.c index 662a1e1..c51be0a 100644 --- a/src/wordsplit.c +++ b/src/wordsplit.c @@ -1139,7 +1139,7 @@ scan_word (struct wordsplit *wsp, size_t start) return _WRDS_OK; } -static char quote_transtab[] = "\\\\a\ab\bf\fn\nr\rt\tv\v"; +static char quote_transtab[] = "\\\\\"\"a\ab\bf\fn\nr\rt\tv\v"; int wordsplit_c_unquote_char (int c) diff --git a/src/yygrecs.h b/src/yygrecs.h deleted file mode 100644 index 1b3bf31..0000000 --- a/src/yygrecs.h +++ /dev/null @@ -1,28 +0,0 @@ -/* 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 . */ - -#define yyparse grecs_yyparse -#define yylex grecs_yylex -#define yyerror grecs_yyerror -#define yynerrs grecs_yynerrs -#define yylval grecs_yylval -#define yylloc grecs_yylloc -#define yychar grecs_yychar -#define yydebug grecs_yydebug -#define yy_flex_debug grecs_yy_flex_debug - -int yylex(void); -int yyerror(char *s); diff --git a/src/yytrans b/src/yytrans new file mode 100644 index 0000000..d78fe26 --- /dev/null +++ b/src/yytrans @@ -0,0 +1,20 @@ +# Identifier translation table for 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 . +# +# See build-aux/yyrename for a description of this file. + +grecs grecs_yy +meta1 grecs_meta1_yy -- cgit v1.2.1