Add 'parent' keyword to parser/lexer #5

Also major parser/lexer rework to support being called multiple
times.  Switch to reentrant and parameterizable parser.
This commit is contained in:
Thomas Jensen 2021-03-02 22:03:12 +01:00
parent 425d354bc0
commit 9315e62528
No known key found for this signature in database
GPG Key ID: A4ACEE270D0FB7DB
10 changed files with 742 additions and 492 deletions

View File

@ -26,13 +26,13 @@ OUT_DIR = ../out
SRC_DIR = ../src
VPATH = $(SRC_DIR):$(SRC_DIR)/misc
GEN_HDR = parser.h boxes.h
GEN_HDR = parser.h boxes.h lex.yy.h
GEN_SRC = parser.c lex.yy.c
GEN_FILES = $(GEN_SRC) $(GEN_HDR)
ORIG_HDRCL = boxes.in.h config.h
ORIG_HDR = $(ORIG_HDRCL) discovery.h generate.h lexer.h regulex.h remove.h shape.h tools.h unicode.h
ORIG_HDR = $(ORIG_HDRCL) discovery.h generate.h lexer.h parsing.h regulex.h remove.h shape.h tools.h unicode.h
ORIG_GEN = lexer.l parser.y
ORIG_NORM = boxes.c discovery.c generate.c regulex.c remove.c shape.c tools.c unicode.c
ORIG_NORM = boxes.c discovery.c generate.c parsing.c regulex.c remove.c shape.c tools.c unicode.c
ORIG_SRC = $(ORIG_GEN) $(ORIG_NORM)
ORIG_FILES = $(ORIG_SRC) $(ORIG_HDR)
@ -81,14 +81,12 @@ flags_:
@echo Please call make from the top level directory.
exit 1
parser.c parser.h: parser.y boxes.h | check_dir
$(YACC) -o parser.c -d $<
parser.c parser.h: parser.y lex.yy.h | check_dir
$(YACC) --warnings=all --verbose --defines=parser.h --output=parser.c $<
cpp -I. -I$(SRC_DIR) -P parser.c parser.p
lex.yy.c: lexer.l boxes.h | check_dir
$(LEX) -t $< > lexer.tmp.c
echo '#include "config.h"' > lex.yy.c
cat lexer.tmp.c >> lex.yy.c
rm lexer.tmp.c
lex.yy.c lex.yy.h: lexer.l | check_dir
$(LEX) --header-file=lex.yy.h $<
boxes.o: boxes.c boxes.h discovery.h regulex.h shape.h tools.h unicode.h generate.h remove.h config.h | check_dir
discovery.o: discovery.c discovery.h boxes.h tools.h config.h | check_dir
@ -96,11 +94,12 @@ tools.o: tools.c tools.h boxes.h shape.h unicode.h config.h | check_dir
unicode.o: unicode.c unicode.h boxes.h tools.h config.h | check_dir
shape.o: shape.c shape.h boxes.h tools.h config.h | check_dir
generate.o: generate.c generate.h boxes.h shape.h tools.h unicode.h config.h | check_dir
parsing.o: parsing.c parsing.h boxes.h config.h | check_dir
remove.o: remove.c remove.h boxes.h shape.h tools.h unicode.h config.h | check_dir
regulex.o: regulex.c regulex.h boxes.h tools.h unicode.h config.h | check_dir
getopt.o: misc/getopt.c misc/getopt.h | check_dir
parser.o: parser.c parser.h tools.h shape.h lexer.h config.h | check_dir
lex.yy.o: lex.yy.c parser.h tools.h shape.h lexer.h config.h | check_dir
parser.o: parser.c boxes.h lex.yy.h parser.h parsing.h tools.h shape.h discovery.h config.h | check_dir
lex.yy.o: lex.yy.c parser.h boxes.h parsing.h tools.h shape.h config.h | check_dir
package: $(BOXES_EXECUTABLE_NAME)
if [ -z "$(PKG_NAME)" ] ; then exit 1 ; fi

View File

@ -39,30 +39,22 @@
#include "tools.h"
#include "discovery.h"
#include "generate.h"
#include "parsing.h"
#include "regulex.h"
#include "remove.h"
#include "unicode.h"
extern char *optarg; /* for getopt() */
extern int optind, opterr, optopt; /* for getopt() */
/* _\|/_
(o o)
+----oOO-{_}-OOo------------------------------------------------------------+
| G l o b a l V a r i a b l e s |
+--------------------------------------------------------------------------*/
extern int yyparse();
extern FILE *yyin; /* lex input file */
char *yyfilename = NULL; /* file name of config file used */
extern char *optarg; /* for getopt() */
extern int optind, opterr, optopt; /* for getopt() */
design_t *designs = NULL; /* available box designs */
int design_idx = 0; /* anz_designs-1 */
int anz_designs = 0; /* no of designs after parsing */
opt_t opt; /* command line options */
@ -77,29 +69,6 @@ input_t input = INPUT_INITIALIZER; /* input lines */
| F u n c t i o n s |
+--------------------------------------------------------------------------*/
static int open_yy_config_file(const char *config_file_name)
/*
* Set yyin and yyfilename to the config file to be used.
*
* RETURNS: == 0 success (yyin and yyfilename are set)
* != 0 error (yyin is unmodified)
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
{
FILE *new_yyin = fopen(config_file_name, "r");
if (new_yyin == NULL) {
fprintf(stderr, "%s: Couldn't open config file '%s' for input\n", PROJECT, config_file_name);
return 1;
}
yyfilename = (char *) config_file_name;
yyin = new_yyin;
return 0;
}
static void usage(FILE *st)
/*
* Print usage information on stream st.
@ -916,9 +885,8 @@ static int list_styles()
else {
design_t **list; /* temp list for sorting */
char buf[42];
list = (design_t **) calloc(design_idx + 1, sizeof(design_t *));
list = (design_t **) calloc(anz_designs, sizeof(design_t *));
if (list == NULL) {
perror(PROJECT);
return 1;
@ -927,19 +895,10 @@ static int list_styles()
for (i = 0; i < anz_designs; ++i) {
list[i] = &(designs[i]);
}
qsort(list, design_idx + 1, sizeof(design_t *), style_sort);
sprintf(buf, "%d", anz_designs);
qsort(list, anz_designs, sizeof(design_t *), style_sort);
if (!opt.q) {
fprintf(opt.outfile, "%d Available Style%s in \"%s\":\n",
anz_designs, anz_designs == 1 ? "" : "s", yyfilename);
fprintf(opt.outfile, "-----------------------%s",
anz_designs == 1 ? "" : "-");
for (i = strlen(yyfilename) + strlen(buf); i > 0; --i) {
fprintf(opt.outfile, "-");
}
fprintf(opt.outfile, "\n\n");
print_design_list_header();
}
for (i = 0; i < anz_designs; ++i) {
if (opt.q) {
@ -1366,27 +1325,19 @@ int main(int argc, char *argv[])
#endif
/*
* Parse config file, then reset design pointer
* Parse config file(s), then reset design pointer
*/
char *config_file = discover_config_file(0);
if (config_file == NULL) {
exit(EXIT_FAILURE);
}
if (opt.cld == NULL) {
yyin = stdin;
rc = open_yy_config_file(config_file);
if (rc) {
exit(EXIT_FAILURE);
}
}
#ifdef DEBUG
fprintf (stderr, "Parsing Config File %s ...\n", config_file);
#endif
if (opt.cld == NULL) {
rc = yyparse();
if (rc) {
size_t r_num_designs = 0;
designs = parse_config_files(config_file, &r_num_designs);
if (designs == NULL) {
exit(EXIT_FAILURE);
}
anz_designs = (int) r_num_designs;
}
else {
rc = build_design(&designs, opt.cld);

View File

@ -115,35 +115,30 @@ typedef struct {
extern design_t *designs;
extern int anz_designs;
extern int design_idx;
extern int tjlineno; /* config file line counter */
extern char *yyfilename; /* name of config file */
typedef struct { /* Command line options: */
int l; /* list available designs */
char *f; /* the string specified as argument to -f ; config file path */
int mend; /* 1 if -m is given, 2 in 2nd loop */
int q; /* special handling of web UI needs */
int r; /* remove box from input */
int tabstop; /* tab stop distance */
char tabexp; /* tab expansion mode (for leading tabs) */
int padding[ANZ_SIDES]; /* in spaces or lines resp. */
design_t *design; /* currently used box design */
int design_choice_by_user; /* true if design was chosen by user */
char *cld; /* commandline design definition, -c */
long reqwidth; /* requested box width (-s) */
long reqheight; /* requested box height (-s) */
char valign; /* text position inside box */
char halign; /* ( h[lcr]v[tcb] ) */
char indentmode; /* 'b', 't', 'n', or '\0' */
char justify; /* 'l', 'c', 'r', or '\0' */
int killblank; /* -1 if not set */
char *encoding; /* character encoding override for input and output text */
FILE *infile; /* where we get our input */
FILE *outfile; /* where we put our output */
int l; /** list available designs */
char *f; /** the string specified as argument to -f ; config file path */
int mend; /** 1 if -m is given, 2 in 2nd loop */
int q; /** special handling of web UI needs */
int r; /** remove box from input */
int tabstop; /** tab stop distance */
char tabexp; /** tab expansion mode (for leading tabs) */
int padding[ANZ_SIDES]; /** in spaces or lines resp. */
design_t *design; /** currently used box design */
int design_choice_by_user; /** true if design was chosen by user */
char *cld; /** commandline design definition, -c */
long reqwidth; /** requested box width (-s) */
long reqheight; /** requested box height (-s) */
char valign; /** text position inside box */
char halign; /** ( h[lcr]v[tcb] ) */
char indentmode; /** 'b', 't', 'n', or '\0' */
char justify; /** 'l', 'c', 'r', or '\0' */
int killblank; /** -1 if not set */
char *encoding; /** character encoding override for input and output text */
FILE *infile; /** where we get our input */
FILE *outfile; /** where we put our output */
} opt_t;
extern opt_t opt;

View File

@ -1,47 +0,0 @@
/*
* boxes - Command line filter to draw/remove ASCII boxes around text
* Copyright (c) 1999-2021 Thomas Jensen and the boxes contributors
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License, version 2, as published
* by the Free Software Foundation.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
/*
* Export symbols used by the parser files only
*/
#ifndef LEXER_H
#define LEXER_H
/*
* Valid characters to be used as string delimiters. Note that the
* following list must correspond to the DELIM definition in lexer.l.
*/
#define LEX_SDELIM "\"~'`!@%&*=:;<>?/|.\\"
int yylex(); /* defined in lex.yy.c */
void begin_speedmode();
void chg_strdelims (const char asdel, const char asesc);
extern int speeding;
#endif /*LEXER_H*/
/*EOF*/ /* vim: set sw=4: */

View File

@ -1,4 +1,4 @@
%{
%top{
/*
* boxes - Command line filter to draw/remove ASCII boxes around text
* Copyright (c) 1999-2021 Thomas Jensen and the boxes contributors
@ -24,6 +24,28 @@
*/
#include "config.h"
void begin_speedmode();
void chg_strdelims (const char asdel, const char asesc);
/**
* User-defined initializations for the lexer.
*
* Since this scanner must use REJECT in order to be able to process the string delimiter commands, it cannot
* dynamically enlarge its input buffer to accomodate larger tokens. Thus, we simply set the buffer size to the
* input file size plus 10 bytes margin-of-error.
*
* @param scanner pointer to the scanner data block
* @param configfile the path to the config file we are reading
*/
void inflate_inbuf(void *scanner, const char *configfile);
}
%{
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
@ -32,59 +54,59 @@
#include "boxes.h"
#undef FILE_LEXER_L
#include "tools.h"
#include "parsing.h"
#include "parser.h"
#include "lexer.h"
#define LEX_MAX_WARN 3 /* number of lex errors per design */
int tjlineno = 1;
static int yyerrcnt = 0;
/* the currently active string delimiter character */
static char sdel = '\"';
/* the currently active string escape character */
static char sesc = '\\';
/*
* User-defined initializations for the lexer
*/
static void inflate_inbuf();
#define YY_USER_INIT inflate_inbuf()
%}
%option 8bit
%option bison-bridge
%option case-insensitive
%option ecs
%option never-interactive
%option nodefault
%option noinput
%option nounput
%option noyywrap
%option never-interactive
%option caseless
%option noinput
%option noyylineno
%option reentrant
%option warn
%option yylineno
%x SAMPLE
%x SPEEDMODE
%x DELWORD
%x PARENT
%s SHAPES
%s ELASTIC
PWORD [a-zA-ZäöüÄÖÜ][a-zA-Z0-9\-_üäöÜÄÖß]*
PWHITE [\n \r\t]
PBOX Box
SDELIM [\"~\'`!@\%\&\*=:;<>\?/|\.\\]
PWORD [a-zA-ZäöüÄÖÜ][a-zA-Z0-9\-_üäöÜÄÖß]*
PWHITE [\n \r\t]
PBOX Box
SDELIM [\"~\'`!@\%\&\*=:;<>\?/|\.\\]
PPARENT parent
PFILENAME [^\r\n]+
%%
<DELWORD,SHAPES,ELASTIC,INITIAL>[ \r\t] /* ignore whitespace */
<DELWORD,SHAPES,ELASTIC,INITIAL>\n ++tjlineno;
<DELWORD,SHAPES,ELASTIC,INITIAL>\n {}
<DELWORD>[^ \t\r\n]+ {
@ -94,8 +116,8 @@ SDELIM [\"~\'`!@\%\&\*=:;<>\?/|\.\\]
#ifdef LEXER_DEBUG
fprintf (stderr, "\nYDELWOR: %s -- STATE INITIAL", yytext);
#endif
yylval.s = (char *) strdup (yytext);
if (yylval.s == NULL) {
yylval->s = (char *) strdup (yytext);
if (yylval->s == NULL) {
perror (PROJECT);
exit (EXIT_FAILURE);
}
@ -118,12 +140,12 @@ SDELIM [\"~\'`!@\%\&\*=:;<>\?/|\.\\]
REJECT; /* that was not our delimiter */
}
yylval.s = (char *) strdup (yytext + 1);
if (yylval.s == NULL) {
yylval->s = (char *) strdup (yytext + 1);
if (yylval->s == NULL) {
perror (PROJECT);
exit (EXIT_FAILURE);
}
p = yylval.s;
p = yylval->s;
while (*p) {
if (*p == sesc) {
@ -135,9 +157,9 @@ SDELIM [\"~\'`!@\%\&\*=:;<>\?/|\.\\]
}
else if (*p == sdel) {
*p = '\0';
yyless ((p-yylval.s)+2+qcnt); /* string plus quotes */
yyless ((p-yylval->s)+2+qcnt); /* string plus quotes */
#ifdef LEXER_DEBUG
fprintf (stderr, "\n STRING: \"%s\"", yylval.s);
fprintf (stderr, "\n STRING: \"%s\"", yylval->s);
#endif
return STRING;
}
@ -145,11 +167,40 @@ SDELIM [\"~\'`!@\%\&\*=:;<>\?/|\.\\]
++p;
}
if (yyerrcnt++ < 5)
yyerror ("Unterminated String -- %s", yytext);
yyerror(NULL, "Unterminated String -- %s", yytext);
return YUNREC;
}
{PPARENT} {
#ifdef LEXER_DEBUG
fprintf (stderr, "\nYPARENT: %s", yytext);
#endif
BEGIN PARENT;
return YPARENT;
}
<PARENT>{PFILENAME} {
char *p = yytext;
while (*p == ' ' || *p == '\t') {
++p;
}
yylval->s = (char *) strdup (p);
p = yylval->s + strlen(yylval->s) - 1;
while ((*p == ' ' || *p == '\t') && p >= yylval->s) {
*p-- = '\0';
}
#ifdef LEXER_DEBUG
fprintf (stderr, "\n STRING: \"%s\"", yylval->s);
#endif
BEGIN INITIAL;
return STRING;
}
<PARENT>\n {
BEGIN INITIAL;
}
Sample {
#ifdef LEXER_DEBUG
@ -161,7 +212,6 @@ Sample {
<SAMPLE>\n {
++tjlineno;
if (yyleng > 1)
yymore();
}
@ -175,8 +225,8 @@ Sample {
--p; /* skip trailing whitespace */
p -= 2; /* almost skip "ends" statement */
*p = '\0'; /* p now points to 'n' */
yylval.s = (char *) strdup (yytext);
if (yylval.s == NULL) {
yylval->s = (char *) strdup (yytext);
if (yylval->s == NULL) {
perror (PROJECT);
exit (EXIT_FAILURE);
}
@ -185,20 +235,20 @@ Sample {
len = p - yytext; /* yyless(n): push back all but the first n */
yyless (len); /* allow him to return YENDSAMPLE */
yylval.s[len] = '\n'; /* replace 'e' with newline */
btrim (yylval.s, &len);
yylval->s[len] = '\n'; /* replace 'e' with newline */
btrim (yylval->s, &len);
if (len > 0) {
strcat (yylval.s, "\n"); /* memory was allocated with strdup */
strcat (yylval->s, "\n"); /* memory was allocated with strdup */
#ifdef LEXER_DEBUG
fprintf (stderr, "\n STRING: \"%s\" -- STATE INITIAL", yylval.s);
fprintf (stderr, "\n STRING: \"%s\" -- STATE INITIAL", yylval->s);
#endif
BEGIN INITIAL;
return STRING;
}
else {
if (yyerrcnt++ < 5)
yyerror ("SAMPLE block must not be empty");
BFREE (yylval.s);
yyerror(NULL, "SAMPLE block must not be empty");
BFREE (yylval->s);
return YUNREC;
}
}
@ -246,26 +296,26 @@ Padding { return YPADDING; }
End { return YEND; }
To { return YTO; }
With { return YWITH; }
Global { yylval.c = 'g'; return YRXPFLAG; }
Once { yylval.c = 'o'; return YRXPFLAG; }
Global { yylval->c = 'g'; return YRXPFLAG; }
Once { yylval->c = 'o'; return YRXPFLAG; }
<SHAPES,ELASTIC>nw { yylval.shape = NW; return SHAPE; }
<SHAPES,ELASTIC>nnw { yylval.shape = NNW; return SHAPE; }
<SHAPES,ELASTIC>n { yylval.shape = N; return SHAPE; }
<SHAPES,ELASTIC>nne { yylval.shape = NNE; return SHAPE; }
<SHAPES,ELASTIC>ne { yylval.shape = NE; return SHAPE; }
<SHAPES,ELASTIC>ene { yylval.shape = ENE; return SHAPE; }
<SHAPES,ELASTIC>e { yylval.shape = E; return SHAPE; }
<SHAPES,ELASTIC>ese { yylval.shape = ESE; return SHAPE; }
<SHAPES,ELASTIC>se { yylval.shape = SE; return SHAPE; }
<SHAPES,ELASTIC>sse { yylval.shape = SSE; return SHAPE; }
<SHAPES,ELASTIC>s { yylval.shape = S; return SHAPE; }
<SHAPES,ELASTIC>ssw { yylval.shape = SSW; return SHAPE; }
<SHAPES,ELASTIC>sw { yylval.shape = SW; return SHAPE; }
<SHAPES,ELASTIC>wsw { yylval.shape = WSW; return SHAPE; }
<SHAPES,ELASTIC>w { yylval.shape = W; return SHAPE; }
<SHAPES,ELASTIC>wnw { yylval.shape = WNW; return SHAPE; }
<SHAPES,ELASTIC>nw { yylval->shape = NW; return SHAPE; }
<SHAPES,ELASTIC>nnw { yylval->shape = NNW; return SHAPE; }
<SHAPES,ELASTIC>n { yylval->shape = N; return SHAPE; }
<SHAPES,ELASTIC>nne { yylval->shape = NNE; return SHAPE; }
<SHAPES,ELASTIC>ne { yylval->shape = NE; return SHAPE; }
<SHAPES,ELASTIC>ene { yylval->shape = ENE; return SHAPE; }
<SHAPES,ELASTIC>e { yylval->shape = E; return SHAPE; }
<SHAPES,ELASTIC>ese { yylval->shape = ESE; return SHAPE; }
<SHAPES,ELASTIC>se { yylval->shape = SE; return SHAPE; }
<SHAPES,ELASTIC>sse { yylval->shape = SSE; return SHAPE; }
<SHAPES,ELASTIC>s { yylval->shape = S; return SHAPE; }
<SHAPES,ELASTIC>ssw { yylval->shape = SSW; return SHAPE; }
<SHAPES,ELASTIC>sw { yylval->shape = SW; return SHAPE; }
<SHAPES,ELASTIC>wsw { yylval->shape = WSW; return SHAPE; }
<SHAPES,ELASTIC>w { yylval->shape = W; return SHAPE; }
<SHAPES,ELASTIC>wnw { yylval->shape = WNW; return SHAPE; }
<ELASTIC>\) {
#ifdef LEXER_DEBUG
@ -291,8 +341,8 @@ author|designer|tags|created|revision|revdate|indent {
#ifdef LEXER_DEBUG
fprintf (stderr, "\nKEYWORD: %s", yytext);
#endif
yylval.s = (char *) strdup (yytext);
if (yylval.s == NULL) {
yylval->s = (char *) strdup (yytext);
if (yylval->s == NULL) {
perror (PROJECT);
exit (EXIT_FAILURE);
}
@ -316,8 +366,8 @@ Delimiter|Delim {
#ifdef LEXER_DEBUG
fprintf (stderr, "\n WORD: %s", yytext);
#endif
yylval.s = (char *) strdup (yytext);
if (yylval.s == NULL) {
yylval->s = (char *) strdup (yytext);
if (yylval->s == NULL) {
perror (PROJECT);
exit (EXIT_FAILURE);
}
@ -329,7 +379,7 @@ Delimiter|Delim {
#ifdef LEXER_DEBUG
fprintf (stderr, "\nYNUMBER: %s", yytext);
#endif
yylval.num = atoi (yytext);
yylval->num = atoi (yytext);
return YNUMBER;
}
@ -352,7 +402,7 @@ Delimiter|Delim {
. {
if (yyerrcnt++ < LEX_MAX_WARN)
yyerror ("Unrecognized input char \'%s\'", yytext);
yyerror(NULL, "Unrecognized input char \'%s\'", yytext);
return YUNREC;
}
@ -366,7 +416,7 @@ Delimiter|Delim {
BEGIN INITIAL;
}
<SPEEDMODE>\n ++tjlineno;
<SPEEDMODE>\n {}
<SPEEDMODE>. /* ignore anything else */
@ -374,35 +424,31 @@ Delimiter|Delim {
%%
static void inflate_inbuf()
/*
* User-defined initializations for the lexer.
*
* Since this scanner must use REJECT in order to be able to process the
* string delimiter commands, it cannot dynamically enlarge its input
* buffer to accomodate larger tokens. Thus, we simply set the buffer size
* to the input file size plus 10 bytes margin-of-error.
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
/* TODO These functions should go away. The cast to (struct yyguts_t *) is a hack.
* And they use global variables which is not reentrant at all. Use extra data instead.
* Maybe inflate_inbuf() can be moved to parsing.c? */
void inflate_inbuf(void *scanner, const char *configfile)
{
struct stat sinf;
if (stat(yyfilename, &sinf)) {
if (stat(configfile, &sinf)) {
perror (PROJECT);
exit (EXIT_FAILURE);
}
yy_delete_buffer (YY_CURRENT_BUFFER);
yy_switch_to_buffer (yy_create_buffer (yyin, sinf.st_size+10));
struct yyguts_t *yyg = (struct yyguts_t *) scanner;
yy_delete_buffer(YY_CURRENT_BUFFER, scanner);
yy_switch_to_buffer (yy_create_buffer(yyin, sinf.st_size+10, scanner), scanner);
}
void begin_speedmode()
void begin_speedmode(void *scanner)
{
#ifdef LEXER_DEBUG
fprintf (stderr, "\n STATUS: begin_speedmode() -- STATE SPEEDMODE");
#endif
struct yyguts_t *yyg = (struct yyguts_t *) scanner;
BEGIN SPEEDMODE;
}
@ -418,5 +464,4 @@ void chg_strdelims (const char asesc, const char asdel)
}
/*EOF*/ /* vim: set cindent sw=4: */

File diff suppressed because it is too large Load Diff

166
src/parsing.c Normal file
View File

@ -0,0 +1,166 @@
/*
* boxes - Command line filter to draw/remove ASCII boxes around text
* Copyright (c) 1999-2021 Thomas Jensen and the boxes contributors
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License, version 2, as published
* by the Free Software Foundation.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
/*
* Configuration file parsing
*/
#include "config.h"
#include <stdarg.h>
#include <string.h>
#include "boxes.h"
#include "parsing.h"
#include "parser.h"
#include "lex.yy.h"
/* file name of the config file currently being parsed */
char *current_config_file_name = NULL;
/** file handle of the config file currently being parsed */
static FILE *current_config_handle = NULL;
/** names of config files specified via "parent" */
char **parent_configs = NULL;
/** number of parent config files (size of parent_configs array) */
size_t num_parent_configs = 0;
/** the arguments passed to our current invocation of the parser */
static pass_to_bison *current_bison_args = NULL;
/**
* Set yyin and current_config_file_name to the config file to be used.
*
* @param bison_args the bison args that we set up in the calling function
* @param config_file_path the new file name to set
* @return 0 if successful (yyin and current_config_file_name are set)
* != 0 on error (yyin is unmodified)
*/
static int open_yy_config_file(pass_to_bison *bison_args, const char *config_file_path)
{
current_config_handle = fopen(config_file_path, "r");
if (current_config_handle == NULL) {
fprintf(stderr, "%s: Couldn't open config file '%s' for input\n", PROJECT, config_file_path);
return 1;
}
current_config_file_name = (char *) config_file_path;
yyset_in(current_config_handle, bison_args->lexer_state);
// TODO to reset parser, YY_FLUSH_BUFFER and BEGIN INITIAL after each change to yyin.
// --> should be ok, because we delete the whole buffer at the beginning, but BEGIN INITIAL may make sense
return 0;
}
void print_design_list_header()
{
char buf[42];
sprintf(buf, "%d", anz_designs);
fprintf(opt.outfile, "%d Available Style%s in \"%s\":\n",
anz_designs, anz_designs == 1 ? "" : "s", current_config_file_name);
fprintf(opt.outfile, "-----------------------%s",
anz_designs == 1 ? "" : "-");
for (int i = strlen(current_config_file_name) + strlen(buf); i > 0; --i) {
fprintf(opt.outfile, "-");
}
fprintf(opt.outfile, "\n\n");
}
/**
* Print configuration file parser errors.
* @param bison_args pointer to the parser arguments
* @param fmt a format string for `vfprintf()`, followed by the arguments
* @return 0
*/
int yyerror(pass_to_bison *bison_args, const char *fmt, ...)
{
va_list ap;
va_start (ap, fmt);
pass_to_bison *bargs = bison_args ? bison_args : current_bison_args;
fprintf(stderr, "%s: %s: line %d: ", PROJECT,
current_config_file_name ? current_config_file_name : "(null)",
yyget_lineno(bargs->lexer_state));
vfprintf(stderr, fmt, ap);
fputc('\n', stderr);
va_end (ap);
return 0;
}
static design_t *parse_config_file(const char *config_file, size_t *r_num_designs)
{
#ifdef DEBUG
fprintf (stderr, "Parsing Config File %s ...\n", config_file);
#endif
// TODO the parser reallocs the global designs array, do this with module-local vars
pass_to_bison bison_args;
bison_args.designs = NULL;
bison_args.num_designs = 0;
bison_args.design_idx = 0;
bison_args.lexer_state = NULL;
current_bison_args = &bison_args;
yylex_init (&(bison_args.lexer_state));
int rc = open_yy_config_file(&bison_args, config_file);
if (rc) {
return NULL;
}
inflate_inbuf(bison_args.lexer_state, config_file);
rc = yyparse(&bison_args);
yylex_destroy(bison_args.lexer_state);
if (current_config_handle != NULL) {
fclose(current_config_handle);
current_config_handle = NULL;
}
if (rc) {
return NULL;
}
*r_num_designs = bison_args.num_designs;
return bison_args.designs;
}
design_t *parse_config_files(const char *first_config_file, size_t *r_num_designs)
{
// TODO much code
return parse_config_file(first_config_file, r_num_designs);
}
/*EOF*/ /* vim: set sw=4: */

70
src/parsing.h Normal file
View File

@ -0,0 +1,70 @@
/*
* boxes - Command line filter to draw/remove ASCII boxes around text
* Copyright (c) 1999-2021 Thomas Jensen and the boxes contributors
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License, version 2, as published
* by the Free Software Foundation.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
/*
* Configuration file parsing
*/
#ifndef PARSING_H
#define PARSING_H 1
#include "parser.h"
/** file name of config file used */
extern char *config_file_name;
/** names of config files specified via "parent" */
extern char **parent_configs;
/** number of parent config files (size of parent_configs array) */
extern size_t num_parent_configs;
/**
* Print the header for the design list output, which includes the file name.
* TODO what if more file names used?
*/
void print_design_list_header();
/**
* Print configuration file parser errors.
* @param bison_args pointer to the parser arguments, not used
* @param fmt a format string for `vfprintf()`, followed by the arguments
* @return 0
*/
int yyerror(pass_to_bison *bison_args, const char *fmt, ...);
/**
* Parse the given config file and all parents.
* @param first_config_file the path to the config file (relative or absolute)
* @param r_num_designs a return argument that takes the number of design definitions returned from the function.
* Will be set to 0 on error
* @return the consolidated list of designs parsed, or `NULL` on error (then an error message was alread printed)
*/
design_t *parse_config_files(const char *first_config_file, size_t *r_num_designs);
#endif
/*EOF*/ /* vim: set cindent sw=4: */

View File

@ -42,45 +42,6 @@
int yyerror(const char *fmt, ...)
/*
* Print configuration file parser errors.
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
{
va_list ap;
va_start (ap, fmt);
fprintf(stderr, "%s: %s: line %d: ", PROJECT,
yyfilename ? yyfilename : "(null)", tjlineno);
vfprintf(stderr, fmt, ap);
fputc('\n', stderr);
va_end (ap);
return 0;
}
void regerror(char *msg)
/*
* Print regular expression andling error messages
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
{
fprintf(stderr, "%s: %s: line %d: %s\n",
PROJECT, yyfilename ? yyfilename : "(null)",
opt.design->current_rule ? opt.design->current_rule->line : 0,
msg);
errno = EINVAL;
}
int strisyes(const char *s)
/*
* Determine if the string s has a contents indicating "yes".

View File

@ -45,10 +45,6 @@
}
int yyerror(const char *fmt, ...);
void regerror(char *msg);
int empty_line(const line_t *line);
size_t expand_tabs_into(const uint32_t *input_buffer, const int tabstop, uint32_t **text,