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 /src | |
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.
Diffstat (limited to 'src')
-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 |
14 files changed, 834 insertions, 232 deletions
diff --git a/src/.gitignore b/src/.gitignore index 9ef1b57..feb5040 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -1,7 +1,11 @@ grecs-gram.c grecs-gram.h grecs-gram.output grecs-lex.c +meta1-gram.c +meta1-gram.h +meta1-gram.output +meta1-lex.c Make-inst.in Make-shared.in Make-static.in diff --git a/src/Make.am b/src/Make.am index 915e6c4..60c0f06 100644 --- a/src/Make.am +++ b/src/Make.am @@ -11,33 +11,46 @@ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Grecs. If not, see <http://www.gnu.org/licenses/>. +if GRECS_COND_META1_PARSER + GRECS_PARSER_META1 = meta1-gram.y meta1-lex.l + GRECS_EXTRA_META1 = meta1-gram.h +endif + GRECS_SRC = \ + asprintf.c\ diag.c\ format.c\ grecs-gram.y\ grecs-lex.l\ join.c\ + lineacc.c\ list.c\ lookup.c\ mem.c\ + parser.c\ preproc.c\ sort.c\ symtab.c\ text.c\ tree.c\ + txtacc.c\ version.c\ - wordsplit.c + wordsplit.c\ + $(GRECS_PARSER_META1) -noinst_HEADERS = yygrecs.h +noinst_HEADERS = -EXTRA_DIST=grecs-gram.h $(PP_SETUP_FILE) Make.am Make-inst.am Make-shared.am Make-static.am +EXTRA_DIST=grecs-gram.h $(GRECS_EXTRA_META1) $(PP_SETUP_FILE) Make.am Make-inst.am Make-shared.am Make-static.am INCLUDES = -I$(srcdir) -I$(top_srcdir)/@GRECS_SUBDIR@ @GRECS_INCLUDES@ @GRECS_HOST_PROJECT_INCLUDES@ AM_YFLAGS = -dtv AM_LFLAGS = -d incdir=$(pkgdatadir)/$(VERSION)/include inc_DATA = $(PP_SETUP_FILE) + +LEXCOMPILE = $(top_srcdir)/@GRECS_SUBDIR@/build-aux/yyrename '$(LEX) $(LFLAGS) $(AM_LFLAGS)' +YACCCOMPILE = $(top_srcdir)/@GRECS_SUBDIR@/build-aux/yyrename '$(YACC) $(YFLAGS) $(AM_YFLAGS)'
\ No newline at end of file diff --git a/src/asprintf.c b/src/asprintf.c new file mode 100644 index 0000000..d493fcb --- /dev/null +++ b/src/asprintf.c @@ -0,0 +1,85 @@ +/* grecs - Gray's Extensible Configuration System + Copyright (C) 2007-2011 Sergey Poznyakoff + + Grecs is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 3 of the License, or (at your + option) any later version. + + Grecs is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Grecs. If not, see <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include "grecs.h" + +int +grecs_vasprintf(char **pbuf, size_t *psize, const char *fmt, va_list ap) +{ + char *buf = *pbuf; + size_t buflen = *psize; + int rc = 0; + + if (!buf) { + if (buflen == 0) + buflen = 512; /* Initial allocation */ + + buf = calloc(1, buflen); + if (buf == NULL) + return ENOMEM; + } + + for (;;) { + ssize_t n = vsnprintf(buf, buflen, fmt, ap); + if (n < 0 || n >= buflen || !memchr(buf, '\0', n + 1)) { + char *newbuf; + size_t newlen = buflen * 2; + if (newlen < buflen) { + rc = ENOMEM; + break; + } + newbuf = realloc(buf, newlen); + if (newbuf == NULL) { + rc = ENOMEM; + break; + } + buflen = newlen; + buf = newbuf; + } else + break; + } + + if (rc) { + if (!*pbuf) { + /* We made first allocation, now free it */ + free(buf); + buf = NULL; + buflen = 0; + } + } + + *pbuf = buf; + *psize = buflen; + return rc; +} + +int +grecs_asprintf(char **pbuf, size_t *psize, const char *fmt, ...) +{ + int rc; + va_list ap; + + va_start(ap, fmt); + rc = grecs_vasprintf(pbuf, psize, fmt, ap); + va_end(ap); + return rc; +} diff --git a/src/grecs-gram.y b/src/grecs-gram.y index 9f8d000..3452074 100644 --- a/src/grecs-gram.y +++ b/src/grecs-gram.y @@ -15,25 +15,23 @@ You should have received a copy of the GNU General Public License along with Grecs. If not, see <http://www.gnu.org/licenses/>. */ #ifdef HAVE_CONFIG_H # include <config.h> #endif -#include "yygrecs.h" #include <grecs.h> #include <grecs-gram.h> #include <stdlib.h> #include <stdarg.h> #include <string.h> #include <errno.h> -static struct grecs_node *parse_tree; -int grecs_error_count; - -int grecs_default_port = 0; +int yylex(void); +int yyerror(char *s); +static struct grecs_node *parse_tree; %} %union { struct { grecs_locus_t locus; char *string; @@ -236,96 +234,30 @@ int yyerror(char *s) { grecs_error(&grecs_current_locus, 0, "%s", s); return 0; } -int -grecs_vasprintf(char **pbuf, size_t *psize, const char *fmt, va_list ap) -{ - char *buf = *pbuf; - size_t buflen = *psize; - int rc = 0; - - if (!buf) { - if (buflen == 0) - buflen = 512; /* Initial allocation */ - - buf = calloc(1, buflen); - if (buf == NULL) - return ENOMEM; - } - - for (;;) { - ssize_t n = vsnprintf(buf, buflen, fmt, ap); - if (n < 0 || n >= buflen || !memchr(buf, '\0', n + 1)) { - char *newbuf; - size_t newlen = buflen * 2; - if (newlen < buflen) { - rc = ENOMEM; - break; - } - newbuf = realloc(buf, newlen); - if (newbuf == NULL) { - rc = ENOMEM; - break; - } - buflen = newlen; - buf = newbuf; - } else - break; - } - - if (rc) { - if (!*pbuf) { - /* We made first allocation, now free it */ - free(buf); - buf = NULL; - buflen = 0; - } - } - - *pbuf = buf; - *psize = buflen; - return rc; -} - -int -grecs_asprintf(char **pbuf, size_t *psize, const char *fmt, ...) -{ - int rc; - va_list ap; - - va_start(ap, fmt); - rc = grecs_vasprintf(pbuf, psize, fmt, ap); - va_end(ap); - return rc; -} - struct grecs_node * -grecs_parse(const char *name) +grecs_grecs_parser(const char *name, int traceflags) { int rc; - if (grecs_lex_begin(name)) + if (grecs_lex_begin(name, traceflags & GRECS_TRACE_LEX)) return NULL; + yydebug = traceflags & GRECS_TRACE_GRAM; parse_tree = NULL; rc = yyparse(); if (grecs_error_count) rc = 1; grecs_lex_end(rc); if (rc) { grecs_tree_free(parse_tree); parse_tree = NULL; } return parse_tree; } -void -grecs_gram_trace(int n) -{ - yydebug = n; -} diff --git a/src/grecs-lex.l b/src/grecs-lex.l index 4d491fe..84ee858 100644 --- a/src/grecs-lex.l +++ b/src/grecs-lex.l @@ -1,12 +1,11 @@ /* grecs - Gray's Extensible Configuration System -*- c -*- */ %top { #ifdef HAVE_CONFIG_H # include <config.h> #endif -#include "yygrecs.h" } %{ /* grecs - Gray's Extensible Configuration System Copyright (C) 2007-2011 Sergey Poznyakoff Grecs is free software; you can redistribute it and/or modify it @@ -45,18 +44,15 @@ grecs_locus_t grecs_current_locus; /* Input file location */ #line directives are offset by the value of XLINES. Uff, running two preprocessors is confusing... */ static size_t xlines; -static struct grecs_list *line_acc; - static void multiline_begin(char *); static void multiline_add(char *); static char *multiline_strip_tabs(char *text); -static void line_add_unescape_last(char *text, size_t len); static int ident(void); static int isemptystr(int off); static void parse_line(char *text, grecs_locus_t *ploc, size_t *pxlines); static void parse_line_cpp(char *text, grecs_locus_t *ploc, size_t *pxlines); @@ -110,15 +106,16 @@ P [1-9][0-9]* grecs_line_add(yytext + 1, yyleng - 2); yylval.string = grecs_line_finish(); return QSTRING; } \"[^\\"\n]*\\. | \"[^\\"\n]*\\\n { BEGIN(STR); grecs_line_begin(); - line_add_unescape_last(yytext + 1, yyleng - 1); } + grecs_line_acc_grow_unescape_last(yytext + 1, + yyleng - 1); } <STR>[^\\"\n]*\\. | -<STR>\"[^\\"\n]*\\\n { line_add_unescape_last(yytext, yyleng); } +<STR>\"[^\\"\n]*\\\n { grecs_line_acc_grow_unescape_last(yytext, yyleng); } <STR>[^\\"\n]*\" { BEGIN(INITIAL); if (yyleng > 1) grecs_line_add(yytext, yyleng - 1); yylval.string = grecs_line_finish(); return QSTRING; } /* Multiline strings */ @@ -167,26 +164,18 @@ yywrap() else grecs_preproc_done(); grecs_current_locus.file = NULL; return 1; } -static void -line_acc_free_entry(void *ptr) -{ - grecs_free(ptr); -} - int -grecs_lex_begin(const char *name) +grecs_lex_begin(const char *name, int trace) { - if (yy_flex_debug > 0) - yy_flex_debug = 0; + yy_flex_debug = trace; - line_acc = grecs_list_create(); - line_acc->free_entry = line_acc_free_entry; + grecs_line_acc_create(); if (grecs_preprocessor) { int fd; fd = open(name, O_RDONLY); if (fd == -1) { @@ -208,13 +197,13 @@ grecs_lex_begin(const char *name) return 0; } void grecs_lex_end(int err) { - grecs_list_clear(line_acc); + grecs_line_acc_free(); } static int isemptystr(int off) { for (; yytext[off] && isspace(yytext[off]); off++) @@ -236,101 +225,27 @@ multiline_strip_tabs(char *text) if (char_to_strip) for (; *text && char_to_strip(*text); text++) ; return text; } -static int -unquote_char(int c) -{ - static char quote_transtab[] = "\\\\\"\"a\ab\bf\fn\nr\rt\tv\v"; - - char *p; - - for (p = quote_transtab; *p; p += 2) { - if (*p == c) - return p[1]; - } - return -1; -} - -struct line_acc_entry -{ - size_t size; -}; -#define line_acc_ptr(entry) (char*)(entry + 1) - -static void -line_acc_add_string(const char *str, size_t len) -{ - struct line_acc_entry *ent = grecs_malloc(sizeof(*ent) + len + 1); - char *p = line_acc_ptr(ent); - memcpy(p, str, len); - p[len] = 0; - ent->size = len; - grecs_list_append(line_acc, ent); -} - -static void -line_acc_add_char(int c) -{ - char t = c; - line_acc_add_string(&t, 1); -} - -static void -list_acc_unescape_char(int c) -{ - if (c != '\n') { - int t = unquote_char(c); - if (t != -1) - line_acc_add_char(t); - else { - grecs_warning(&grecs_current_locus, 0, - _("unknown escape sequence '\\%c'"), - c); - line_acc_add_char(c); - } - } -} - -void -grecs_line_add(const char *text, size_t len) -{ - line_acc_add_string(text, len); -} - -/* Same, but unescapes the last character from yytext */ -static void -line_add_unescape_last(char *text, size_t len) -{ - line_acc_add_string(text, len - 2); - list_acc_unescape_char(text[len - 1]); -} - static void multiline_add(char *s) { if (multiline_unescape) { for (; *s; s++) { if (*s == '\\') { - list_acc_unescape_char(s[1]); + grecs_line_acc_grow_char_unescape(s[1]); ++s; } else - line_acc_add_char(*s); + grecs_line_acc_grow_char(*s); } } else grecs_line_add(s, strlen(s)); } -void -grecs_line_begin() -{ - /* FIXME: nothing so far. Maybe prepare stk by calling obstack_finish? */ -} - static int is_tab(char c) { return c == '\t'; } @@ -371,36 +286,12 @@ multiline_begin(char *p) multiline_delimiter = grecs_malloc(multiline_delimiter_len + 1); memcpy(multiline_delimiter, p, multiline_delimiter_len); multiline_delimiter[multiline_delimiter_len] = 0; grecs_line_begin(); } -char * -grecs_line_finish() -{ - struct grecs_list_entry *ep; - size_t size = 0; - char *str, *p; - - for (ep = line_acc->head; ep; ep = ep->next) { - struct line_acc_entry *ent = ep->data; - size += ent->size; - } - - str = grecs_malloc(size + 1); - for (ep = line_acc->head, p = str; ep; ep = ep->next) { - struct line_acc_entry *ent = ep->data; - char *str = line_acc_ptr(ent); - memcpy(p, str, ent->size); - p += ent->size; - } - *p = 0; - grecs_list_clear(line_acc); - return str; -} - static int ident() { char *p; char *str; size_t len; @@ -413,18 +304,12 @@ ident() strcpy(str, p); yylval.ident.locus = grecs_current_locus; yylval.ident.string = str; return IDENT; } -void -grecs_lex_trace(int n) -{ - yy_flex_debug = -n; -} - grecs_value_t * grecs_value_ptr_from_static(grecs_value_t *input) { grecs_value_t *ptr = grecs_malloc(sizeof(*ptr)); *ptr = *input; return ptr; diff --git a/src/grecs.h b/src/grecs.h index 61a915b..ff47698 100644 --- a/src/grecs.h +++ b/src/grecs.h @@ -195,24 +195,45 @@ grecs_value_t *grecs_value_ptr_from_static(grecs_value_t *input); extern void (*grecs_print_diag_fun)(grecs_locus_t *, int, int, const char*); void grecs_warning(grecs_locus_t *locus, int errcode, const char *fmt, ...) __attribute__ ((__format__ (__printf__, 3, 4))); void grecs_error(grecs_locus_t *locus, int errcode, const char *fmt, ...) __attribute__ ((__format__ (__printf__, 3, 4))); + + +extern int grecs_trace_flags; + +#define GRECS_TRACE_GRAM 0x01 +#define GRECS_TRACE_LEX 0x02 void grecs_gram_trace(int n); void grecs_lex_trace(int n); + -int grecs_lex_begin(const char*); +int grecs_lex_begin(const char*, int); void grecs_lex_end(int err); struct grecs_node *grecs_parse(const char *name); + +extern struct grecs_node *(*grecs_parser_fun)(const char *name, int trace); + +/* Parsers: */ +struct grecs_node *grecs_grecs_parser(const char *name, int traceflags); +struct grecs_node *grecs_meta1_parser(const char *name, int traceflags); + struct grecs_list *_grecs_simple_list_create(int dispose); struct grecs_list *grecs_value_list_create(void); +void grecs_line_acc_create(void); +void grecs_line_acc_free(void); +void grecs_line_acc_grow_char(int c); +void grecs_line_acc_grow_char_unescape(int c); +void grecs_line_acc_grow(const char *text, size_t len); +void grecs_line_acc_grow_unescape_last(char *text, size_t len); + void grecs_line_begin(void); -void grecs_line_add(const char *text, size_t len); +#define grecs_line_add grecs_line_acc_grow char *grecs_line_finish(void); extern int grecs_string_convert(void *target, enum grecs_data_type type, const char *string, grecs_locus_t *locus); extern void grecs_process_ident(struct grecs_keyword *kwp, grecs_value_t *value, @@ -292,12 +313,19 @@ void grecs_list_clear(struct grecs_list *lp); void grecs_list_free(struct grecs_list *lp); void grecs_list_add(struct grecs_list *dst, struct grecs_list *src); int grecs_vasprintf(char **pbuf, size_t *psize, const char *fmt, va_list ap); int grecs_asprintf(char **pbuf, size_t *psize, const char *fmt, ...); +#define GRECS_TXTACC_BUFSIZE 1024 +struct grecs_txtacc *grecs_txtacc_create(void); +void grecs_txtacc_free(struct grecs_txtacc *acc); +void grecs_txtacc_grow(struct grecs_txtacc *acc, const char *buf, size_t size); +char *grecs_txtacc_finish(struct grecs_txtacc *acc, int steal); +void grecs_txtacc_free_string(struct grecs_txtacc *acc, char *str); + struct grecs_symtab; struct grecs_syment { char *name; }; @@ -360,12 +388,15 @@ struct grecs_node *grecs_match_first(struct grecs_node *tree, const char *pattern, grecs_match_buf_t *buf); struct grecs_node *grecs_match_next(struct grecs_match_buf *buf); void grecs_match_buf_free(struct grecs_match_buf *buf); int grecs_value_eq(struct grecs_value *a, struct grecs_value *b); +int grecs_value_match(struct grecs_value *pat, struct grecs_value *b, + int flags); + struct grecs_node *grecs_find_node(struct grecs_node *node, const char *path); struct grecs_node *grecs_node_from_path(const char *path, const char *value); int grecs_tree_reduce(struct grecs_node *node, struct grecs_keyword *kwd, int flags); diff --git a/src/lineacc.c b/src/lineacc.c new file mode 100644 index 0000000..a3f4836 --- /dev/null +++ b/src/lineacc.c @@ -0,0 +1,82 @@ +/* grecs - Gray's Extensible Configuration System + Copyright (C) 2007-2011 Sergey Poznyakoff + + Grecs is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 3 of the License, or (at your + option) any later version. + + Grecs is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Grecs. If not, see <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#include <grecs.h> +#include <wordsplit.h> + +static struct grecs_txtacc *line_acc; + +void +grecs_line_acc_create() +{ + line_acc = grecs_txtacc_create(); +} + +void +grecs_line_acc_free() +{ + grecs_txtacc_free(line_acc); + line_acc = NULL; +} + +void +grecs_line_acc_grow_char(int c) +{ + char t = c; + grecs_txtacc_grow(line_acc, &t, 1); +} + +void +grecs_line_acc_grow_char_unescape(int c) +{ + if (c != '\n') + grecs_line_acc_grow_char(wordsplit_c_unquote_char(c)); +} + +void +grecs_line_acc_grow(const char *text, size_t len) +{ + grecs_txtacc_grow(line_acc, text, len); +} + +/* Same, but unescapes the last character from text */ +void +grecs_line_acc_grow_unescape_last(char *text, size_t len) +{ + grecs_txtacc_grow(line_acc, text, len - 2); + grecs_line_acc_grow_char_unescape(text[len - 1]); +} + +void +grecs_line_begin() +{ + if (!line_acc) + grecs_line_acc_create(); +} + +char * +grecs_line_finish() +{ + grecs_line_acc_grow_char(0); + return grecs_txtacc_finish(line_acc, 1); +} + + + + diff --git a/src/meta1-gram.y b/src/meta1-gram.y new file mode 100644 index 0000000..8ddcbfb --- /dev/null +++ b/src/meta1-gram.y @@ -0,0 +1,217 @@ +%{ +/* MeTA1 configuration parser for Grecs. + Copyright (C) 2007-2011 Sergey Poznyakoff + + Grecs is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 3 of the License, or (at your + option) any later version. + + Grecs is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Grecs. If not, see <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#include <errno.h> +#include <string.h> +#include "grecs.h" + +int yylex(void); +int yyerror(char *s); + +static struct grecs_node *parse_tree; +extern int yy_flex_debug; +%} + +%union { + struct { + grecs_locus_t locus; + char *string; + } ident; + char *string; + grecs_value_t svalue, *pvalue; + struct grecs_list *list; + struct { struct grecs_node *head, *tail; } node_list; + struct grecs_node *node; +} + +%token <ident> META1_IDENT +%token <string> META1_STRING +%type <node> stmt simple block +%type <node_list> stmtlist +%type <pvalue> tag value +%type <string> string slist +%type <list> slist0 +%type <list> values list +%% + +input : stmtlist + { + parse_tree = grecs_node_create(grecs_node_root, + &grecs_current_locus); + parse_tree->v.texttab = grecs_text_table(); + grecs_node_bind(parse_tree, $1.head, 1); + } + ; + +stmtlist: stmt + { + $$.head = $$.tail = $1; + } + | stmtlist stmt + { + grecs_node_bind($1.tail, $2, 0); + } + ; + +stmt : simple + | block + ; + +simple : META1_IDENT '=' value opt_sc + { + $$ = grecs_node_create(grecs_node_stmt, + &$1.locus); + $$->ident = $1.string; + $$->v.value = $3; + } + ; + +block : META1_IDENT tag '{' stmtlist '}' opt_sc + { + $$ = grecs_node_create(grecs_node_block, + &$1.locus); + $$->ident = $1.string; + $$->v.value = $2; + grecs_node_bind($$, $4.head, 1); + } + ; + +tag : /* empty */ + { + $$ = NULL; + } + | META1_IDENT + { + $$ = grecs_malloc(sizeof($$[0])); + $$->type = GRECS_TYPE_STRING; + $$->v.string = $1.string; + } + ; + +value : string + { + $$ = grecs_malloc(sizeof($$[0])); + $$->type = GRECS_TYPE_STRING; + $$->v.string = $1; + } + | list + { + $$ = grecs_malloc(sizeof($$[0])); + $$->type = GRECS_TYPE_LIST; + $$->v.list = $1; + } + ; + +string : META1_IDENT + { + $$ = $1.string; + } + | slist + ; + +slist : slist0 + { + struct grecs_list_entry *ep; + + grecs_line_begin(); + for (ep = $1->head; ep; ep = ep->next) { + grecs_line_add(ep->data, strlen(ep->data)); + free(ep->data); + ep->data = NULL; + } + $$ = grecs_line_finish(); + grecs_list_free($1); + } + +slist0 : META1_STRING + { + $$ = grecs_list_create(); + grecs_list_append($$, $1); + } + | slist0 META1_STRING + { + grecs_list_append($1, $2); + $$ = $1; + } + ; + +list : '{' values '}' + { + $$ = $2; + } + | '{' values ',' '}' + { + $$ = $2; + } + ; + +values : value + { + $$ = grecs_value_list_create(); + grecs_list_append($$, $1); + } + | values ',' value + { + grecs_list_append($1, $3); + $$ = $1; + } + ; + +opt_sc : /* empty */ + | ';' + ; + +%% +int +yyerror(char *s) +{ + grecs_error(&grecs_current_locus, 0, "%s", s); + return 0; +} + +struct grecs_node * +grecs_meta1_parser(const char *name, int traceflags) +{ + int rc; + FILE *fp; + + fp = fopen(name, "r"); + if (!fp) { + grecs_error(NULL, errno, _("Cannot open `%s'"), name); + return NULL; + } + yyset_in(fp); + + yy_flex_debug = traceflags & GRECS_TRACE_LEX; + yydebug = traceflags & GRECS_TRACE_GRAM; + parse_tree = NULL; + grecs_line_acc_create(); + rc = yyparse(); + fclose(fp); + if (grecs_error_count) + rc = 1; + grecs_line_acc_free(); + if (rc) { + grecs_tree_free(parse_tree); + parse_tree = NULL; + } + return parse_tree; +} + diff --git a/src/meta1-lex.l b/src/meta1-lex.l new file mode 100644 index 0000000..cb2931d --- /dev/null +++ b/src/meta1-lex.l @@ -0,0 +1,114 @@ +/* MeTA1 configuration lexer for Grecs. -*- c -*- */ +%top { +/* MeTA1 configuration lexer for Grecs. + Copyright (C) 2007-2011 Sergey Poznyakoff + + Grecs is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 3 of the License, or (at your + option) any later version. + + Grecs is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Grecs. If not, see <http://www.gnu.org/licenses/>. */ + +/* This file implements a lexical analyzer for MeTA1 main configuration file. + */ + +#ifndef HAVE_CONFIG_H +# include <config.h> +#endif +#include "grecs.h" +#include "meta1-gram.h" +#include <ctype.h> +} + +%{ +static int yywrap(void); +static void meta1_line_add_unescape_hex(const char *text, size_t len); +%} + +%x COMMENT STR +X [0-9a-fA-F] +%% + /* C-style comments */ +"/*" BEGIN (COMMENT); +<COMMENT>[^*\n]* /* eat anything that's not a '*' */ +<COMMENT>"*"+[^*/\n]* /* eat up '*'s not followed by '/'s */ +<COMMENT>\n ++grecs_current_locus.line; +<COMMENT>"*"+"/" BEGIN (INITIAL); + /* End-of-line comments */ +#.*\n { grecs_current_locus.line++; } +#.* /* end-of-file comment */; + /* Number */ +0[xX]{X}+ | +0[0-7]+ | +[1-9][0-9]+ { grecs_line_begin(); + grecs_line_add(yytext, yyleng); + yylval.string = grecs_line_finish(); + return META1_STRING; } + /* Identifiers (unquoted strings) */ +[a-zA-Z0-9_\./:\*-]+ { grecs_line_begin(); + grecs_line_add(yytext, yyleng); + yylval.ident.locus = grecs_current_locus; + yylval.ident.string = grecs_line_finish(); + return META1_IDENT; } + /* Quoted strings */ +\"[^\\"\n]*\" { grecs_line_begin(); + grecs_line_add(yytext + 1, yyleng - 2); + yylval.string = grecs_line_finish(); + return META1_STRING; } +\"[^\\"\n]*\\x{X}{1,2} { BEGIN(STR); + grecs_line_begin(); + meta1_line_add_unescape_hex(yytext + 1, yyleng - 1); + } +\"[^\\"\n]*\\. { BEGIN(STR); + grecs_line_begin(); + grecs_line_acc_grow_unescape_last(yytext + 1, + yyleng - 1); } +<STR>[^\\"\n]*\\x{X}{1,2} { meta1_line_add_unescape_hex(yytext, yyleng); } +<STR>[^\\"\n]*\\. { grecs_line_acc_grow_unescape_last(yytext, yyleng); } +<STR>[^\\"\n]*\" { BEGIN(INITIAL); + if (yyleng > 1) + grecs_line_add(yytext, yyleng - 1); + yylval.string = grecs_line_finish(); + return META1_STRING; } +<STR>[^\\"\n]*\n { BEGIN(INITIAL); + grecs_error(&grecs_current_locus, 0, + _("newline in a string")); + grecs_line_add (yytext, yyleng - 1); + yylval.string = grecs_line_finish (); + return META1_STRING; } + /* Other tokens */ +[ \t\f][ \t\f]* ; +\n { grecs_current_locus.line++; } +[,;{}=] return yytext[0]; +. { grecs_error(&grecs_current_locus, 0, + (isascii(yytext[0]) && isprint(yytext[0])) ? + _("stray character %c") : + _("stray character \\%03o"), + (unsigned char) yytext[0]); } +%% + +int +yywrap() +{ + return 1; +} + +static void +meta1_line_add_unescape_hex(const char *text, size_t len) +{ + for (; text[len-1] != 'x' && len > 0; len--) + ; + grecs_line_acc_grow(text, len - 2); + grecs_line_acc_grow_char((char) strtoul (text + len, NULL, 16)); +} + + + + diff --git a/src/parser.c b/src/parser.c new file mode 100644 index 0000000..6f84bd8 --- /dev/null +++ b/src/parser.c @@ -0,0 +1,60 @@ +/* grecs - Gray's Extensible Configuration System + Copyright (C) 2007-2011 Sergey Poznyakoff + + Grecs is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 3 of the License, or (at your + option) any later version. + + Grecs is distributed in th |