mirror of
https://github.com/ascii-boxes/boxes.git
synced 2025-06-06 18:06:44 +02:00
Refactor lexer and parser for maintainability
- Speedmode is now purely a parser thing - Lexer start condition SPEEDMODE is no longer needed - Added lexer start condition BOX to distinguish box def content - DELIMs are now purely a lexer thing - No shared state between lexer and parser for DELIMs anymore - Fix bug where speedmode could terminate in a SAMPLE block - All lexer start conditions now exclusive - Improved debug output - Renamed parser token YDELWORD to YDELIMSPEC
This commit is contained in:
parent
00153f8068
commit
2d3a842728
206
src/lexer.l
206
src/lexer.l
@ -29,16 +29,20 @@
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
int yyerrcnt;
|
int yyerrcnt;
|
||||||
|
|
||||||
/** pointer to the currently active string delimiter character in bison_args */
|
/** the currently active string delimiter character */
|
||||||
char *sdel_ptr;
|
char sdel;
|
||||||
|
|
||||||
/** pointer to the currently active string escape character in bison_args */
|
/** the currently active string escape character */
|
||||||
char *sesc_ptr;
|
char sesc;
|
||||||
} pass_to_flex;
|
} pass_to_flex;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
void begin_speedmode(void *yyscanner);
|
* Valid characters to be used as string delimiters. Note that the
|
||||||
|
* following list must correspond to the SDELIM definition below.
|
||||||
|
*/
|
||||||
|
#define LEX_SDELIM "\"~'`!@%&*=:;<>?/|.\\"
|
||||||
|
#define LEX_SDELIM_RECOMMENDED "\"~'!|"
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -73,6 +77,8 @@ static void report_state_char(char *symbol, char c, char *expected_state_str);
|
|||||||
|
|
||||||
static void report_state(char *symbol, char *text, char *expected_state_str);
|
static void report_state(char *symbol, char *text, char *expected_state_str);
|
||||||
|
|
||||||
|
static int change_string_delimiters(pass_to_flex *extra, char *delim_expr);
|
||||||
|
|
||||||
%}
|
%}
|
||||||
|
|
||||||
|
|
||||||
@ -91,17 +97,16 @@ static void report_state(char *symbol, char *text, char *expected_state_str);
|
|||||||
%option yylineno
|
%option yylineno
|
||||||
|
|
||||||
|
|
||||||
%x DELWORD
|
%x BOX
|
||||||
%x PARENT
|
|
||||||
%x SAMPLE
|
%x SAMPLE
|
||||||
%x SPEEDMODE
|
%x SHAPES
|
||||||
%s ELASTIC
|
%x ELASTIC
|
||||||
%s SHAPES
|
%x DELIMSPEC
|
||||||
|
%x PARENT
|
||||||
|
|
||||||
|
|
||||||
PWORD [a-zA-ZäöüÄÖÜ][a-zA-Z0-9\-_üäöÜÄÖß]*
|
PWORD [a-zA-ZäöüÄÖÜ][a-zA-Z0-9\-_üäöÜÄÖß]*
|
||||||
PWHITE [\n \r\t]
|
PWHITE [ \t\r\n]
|
||||||
PBOX Box
|
|
||||||
SDELIM [\"~\'`!@\%\&\*=:;<>\?/|\.\\]
|
SDELIM [\"~\'`!@\%\&\*=:;<>\?/|\.\\]
|
||||||
PPARENT parent
|
PPARENT parent
|
||||||
PFILENAME [^\r\n]+
|
PFILENAME [^\r\n]+
|
||||||
@ -109,38 +114,39 @@ PFILENAME [^\r\n]+
|
|||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
|
/*
|
||||||
<INITIAL,DELWORD,ELASTIC,SHAPES>[ \r\t] /* ignore whitespace */
|
* Precedence of rules:
|
||||||
|
* - The rule that matches the most text wins.
|
||||||
<INITIAL,DELWORD,ELASTIC,SHAPES>\n {}
|
* - If two rules match the same amount of text, the one defined first (further up) wins.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
<DELWORD>[^ \t\r\n]+ {
|
<INITIAL,BOX,DELIMSPEC,ELASTIC,SHAPES>{PWHITE} /* ignore whitespace */
|
||||||
|
|
||||||
|
<DELIMSPEC>[^ \t\r\n]+ {
|
||||||
/*
|
/*
|
||||||
* String delimiter spec - like WORD, but allow any character
|
* String delimiter spec - like WORD, but allow any character
|
||||||
*/
|
*/
|
||||||
yylval->s = (char *) strdup (yytext);
|
yylval->s = (char *) strdup (yytext);
|
||||||
if (yylval->s == NULL) {
|
BEGIN(BOX);
|
||||||
perror (PROJECT);
|
report_state("YDELIMS", yytext, "INITIAL");
|
||||||
exit (EXIT_FAILURE);
|
if (change_string_delimiters(yyextra, yylval->s) != 0) {
|
||||||
|
return YUNREC;
|
||||||
}
|
}
|
||||||
BEGIN(INITIAL);
|
return YDELIMSPEC;
|
||||||
report_state("YDELWOR", yytext, "INITIAL");
|
|
||||||
return YDELWORD;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
{SDELIM}.*$ {
|
<BOX,SHAPES>{SDELIM}.*$ {
|
||||||
/*
|
/*
|
||||||
* Strings -- first match everything starting from a potential
|
* Strings -- first match everything starting from a potential string delimiter until the end of the line. We
|
||||||
* string delimiter until the end of the line. We will give back what
|
* will give back what we don't need and also detect unterminated strings. Strings always end on the same line.
|
||||||
* we don't need and also detect unterminated strings.
|
|
||||||
*/
|
*/
|
||||||
char *p;
|
char *p;
|
||||||
int rest_len = yyleng - 1; /* length of string pointed to by p */
|
int rest_len = yyleng - 1; /* length of string pointed to by p */
|
||||||
int qcnt = 0; /* esc char count in current string */
|
int qcnt = 0; /* esc char count in current string */
|
||||||
|
|
||||||
if (yytext[0] != *(yyextra->sdel_ptr)) {
|
if (yytext[0] != yyextra->sdel) {
|
||||||
REJECT; /* that was not our delimiter */
|
REJECT; /* that was not our delimiter */
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,14 +158,14 @@ PFILENAME [^\r\n]+
|
|||||||
p = yylval->s;
|
p = yylval->s;
|
||||||
|
|
||||||
while (*p) {
|
while (*p) {
|
||||||
if (*p == *(yyextra->sesc_ptr)) {
|
if (*p == yyextra->sesc) {
|
||||||
memmove (p, p+1, rest_len); /* incl. '\0' */
|
memmove (p, p+1, rest_len); /* incl. '\0' */
|
||||||
++qcnt;
|
++qcnt;
|
||||||
--rest_len;
|
--rest_len;
|
||||||
if (*p == '\0')
|
if (*p == '\0')
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if (*p == *(yyextra->sdel_ptr)) {
|
else if (*p == yyextra->sdel) {
|
||||||
*p = '\0';
|
*p = '\0';
|
||||||
yyless ((p-yylval->s)+2+qcnt); /* string plus quotes */
|
yyless ((p-yylval->s)+2+qcnt); /* string plus quotes */
|
||||||
#ifdef LEXER_DEBUG
|
#ifdef LEXER_DEBUG
|
||||||
@ -177,7 +183,7 @@ PFILENAME [^\r\n]+
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
{PPARENT} {
|
<INITIAL>{PPARENT} {
|
||||||
BEGIN(PARENT);
|
BEGIN(PARENT);
|
||||||
report_state("YPARENT", yytext, "PARENT");
|
report_state("YPARENT", yytext, "PARENT");
|
||||||
return YPARENT;
|
return YPARENT;
|
||||||
@ -204,19 +210,18 @@ PFILENAME [^\r\n]+
|
|||||||
report_state(" NL", "", "INITIAL");
|
report_state(" NL", "", "INITIAL");
|
||||||
}
|
}
|
||||||
|
|
||||||
Sample {
|
|
||||||
|
<BOX>Sample {
|
||||||
BEGIN(SAMPLE);
|
BEGIN(SAMPLE);
|
||||||
report_state("YSAMPLE", yytext, "SAMPLE");
|
report_state("YSAMPLE", yytext, "SAMPLE");
|
||||||
return YSAMPLE;
|
return YSAMPLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
<SAMPLE>\n {
|
<SAMPLE>\n {
|
||||||
if (yyleng > 1)
|
if (yyleng > 1)
|
||||||
yymore();
|
yymore();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
<SAMPLE>^[ \t]*ends[ \t\r]*$ {
|
<SAMPLE>^[ \t]*ends[ \t\r]*$ {
|
||||||
char *p = yytext + yyleng -1;
|
char *p = yytext + yyleng -1;
|
||||||
size_t len; /* length of sample */
|
size_t len; /* length of sample */
|
||||||
@ -239,8 +244,6 @@ Sample {
|
|||||||
btrim (yylval->s, &len);
|
btrim (yylval->s, &len);
|
||||||
if (len > 0) {
|
if (len > 0) {
|
||||||
strcat (yylval->s, "\n"); /* memory was allocated with strdup */
|
strcat (yylval->s, "\n"); /* memory was allocated with strdup */
|
||||||
BEGIN(INITIAL);
|
|
||||||
report_state(" STRING", yylval->s, "INITIAL");
|
|
||||||
return STRING;
|
return STRING;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -252,53 +255,56 @@ Sample {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
<SAMPLE>. yymore();
|
<SAMPLE>. yymore();
|
||||||
|
|
||||||
|
<SAMPLE>ends[ \t\r]*$ {
|
||||||
ends[ \t\r]*$ {
|
/* reached because the other rule pushes it back so a proper end token can be returned */
|
||||||
#ifdef LEXER_DEBUG
|
BEGIN(BOX);
|
||||||
fprintf (stderr, "YENDSAM: %s\n", yytext);
|
report_state("YENDSAM", yytext, "BOX");
|
||||||
#endif
|
|
||||||
return YENDSAMPLE;
|
return YENDSAMPLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Tags {
|
<BOX>Tags {
|
||||||
#ifdef LEXER_DEBUG
|
#ifdef LEXER_DEBUG
|
||||||
fprintf (stderr, " YTAGS: %s\n", yytext);
|
fprintf (stderr, " YTAGS: %s\n", yytext);
|
||||||
#endif
|
#endif
|
||||||
return YTAGS;
|
return YTAGS;
|
||||||
}
|
}
|
||||||
|
|
||||||
Elastic {
|
<BOX>Elastic {
|
||||||
BEGIN(ELASTIC);
|
BEGIN(ELASTIC);
|
||||||
report_state("YELASTC", yytext, "ELASTIC");
|
report_state("YELASTC", yytext, "ELASTIC");
|
||||||
return YELASTIC;
|
return YELASTIC;
|
||||||
}
|
}
|
||||||
|
|
||||||
Shapes {
|
<BOX>Shapes {
|
||||||
BEGIN(SHAPES);
|
BEGIN(SHAPES);
|
||||||
report_state("YSHAPES", yytext, "SHAPES");
|
report_state("YSHAPES", yytext, "SHAPES");
|
||||||
return YSHAPES;
|
return YSHAPES;
|
||||||
}
|
}
|
||||||
|
|
||||||
{PBOX} {
|
<INITIAL>Box {
|
||||||
#ifdef LEXER_DEBUG
|
BEGIN(BOX);
|
||||||
fprintf (stderr, " YBOX: %s\n", yytext);
|
report_state(" YBOX", yytext, "BOX");
|
||||||
#endif
|
|
||||||
yyextra->yyerrcnt = 0;
|
yyextra->yyerrcnt = 0;
|
||||||
|
change_string_delimiters(yyextra, "\\\"");
|
||||||
return YBOX;
|
return YBOX;
|
||||||
}
|
}
|
||||||
|
|
||||||
Replace { return YREPLACE; }
|
<BOX>Replace { return YREPLACE; }
|
||||||
Reverse { return YREVERSE; }
|
<BOX>Reverse { return YREVERSE; }
|
||||||
Padding { return YPADDING; }
|
<BOX>Padding { return YPADDING; }
|
||||||
End { return YEND; }
|
<BOX>To { return YTO; }
|
||||||
To { return YTO; }
|
<BOX>With { return YWITH; }
|
||||||
With { return YWITH; }
|
<BOX>Global { yylval->c = 'g'; return YRXPFLAG; }
|
||||||
Global { yylval->c = 'g'; return YRXPFLAG; }
|
<BOX>Once { yylval->c = 'o'; return YRXPFLAG; }
|
||||||
Once { yylval->c = 'o'; return YRXPFLAG; }
|
<BOX>End {
|
||||||
|
BEGIN(INITIAL);
|
||||||
|
report_state(" YEND", yytext, "INITIAL");
|
||||||
|
change_string_delimiters(yyextra, "\\\"");
|
||||||
|
return YEND;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
<SHAPES,ELASTIC>nw { yylval->shape = NW; return SHAPE; }
|
<SHAPES,ELASTIC>nw { yylval->shape = NW; return SHAPE; }
|
||||||
@ -319,19 +325,19 @@ Once { yylval->c = 'o'; return YRXPFLAG; }
|
|||||||
<SHAPES,ELASTIC>wnw { yylval->shape = WNW; return SHAPE; }
|
<SHAPES,ELASTIC>wnw { yylval->shape = WNW; return SHAPE; }
|
||||||
|
|
||||||
<ELASTIC>\) {
|
<ELASTIC>\) {
|
||||||
BEGIN(INITIAL);
|
BEGIN(BOX);
|
||||||
report_state_char("SYMBOL", yytext[0], "INITIAL");
|
report_state_char("SYMBOL", yytext[0], "BOX");
|
||||||
return yytext[0];
|
return yytext[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
<SHAPES>\} {
|
<SHAPES>\} {
|
||||||
BEGIN(INITIAL);
|
BEGIN(BOX);
|
||||||
report_state_char("SYMBOL", yytext[0], "INITIAL");
|
report_state_char("SYMBOL", yytext[0], "BOX");
|
||||||
return yytext[0];
|
return yytext[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
author|designer|created|revision|revdate|indent {
|
<BOX>author|designer|created|revision|revdate|indent {
|
||||||
/*
|
/*
|
||||||
* general key words
|
* general key words
|
||||||
*/
|
*/
|
||||||
@ -347,17 +353,20 @@ author|designer|created|revision|revdate|indent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Delimiter|Delim {
|
<BOX>Delimiter|Delim {
|
||||||
/*
|
/*
|
||||||
* Change string delimiting characters
|
* Change string delimiting characters
|
||||||
*/
|
*/
|
||||||
BEGIN(DELWORD);
|
BEGIN(DELIMSPEC);
|
||||||
report_state("YCHGDEL", yytext, "DELWORD");
|
report_state("YCHGDEL", yytext, "DELIMSPEC");
|
||||||
return YCHGDEL;
|
return YCHGDEL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
{PWORD} {
|
<INITIAL,BOX>{PWORD} {
|
||||||
|
/*
|
||||||
|
* a free-floating word which is not a string, i.e. it does not have delimiting characters
|
||||||
|
*/
|
||||||
#ifdef LEXER_DEBUG
|
#ifdef LEXER_DEBUG
|
||||||
fprintf (stderr, " WORD: %s\n", yytext);
|
fprintf (stderr, " WORD: %s\n", yytext);
|
||||||
#endif
|
#endif
|
||||||
@ -370,7 +379,7 @@ Delimiter|Delim {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[\+-]?[0-9]+ {
|
<BOX>[\+-]?[0-9]+ {
|
||||||
#ifdef LEXER_DEBUG
|
#ifdef LEXER_DEBUG
|
||||||
fprintf (stderr, "YNUMBER: %s\n", yytext);
|
fprintf (stderr, "YNUMBER: %s\n", yytext);
|
||||||
#endif
|
#endif
|
||||||
@ -379,7 +388,7 @@ Delimiter|Delim {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[,(){}] {
|
<BOX,SHAPES,ELASTIC>[,(){}] {
|
||||||
#ifdef LEXER_DEBUG
|
#ifdef LEXER_DEBUG
|
||||||
fprintf (stderr, " SYMBOL: \'%c\'\n", yytext[0]);
|
fprintf (stderr, " SYMBOL: \'%c\'\n", yytext[0]);
|
||||||
#endif
|
#endif
|
||||||
@ -387,7 +396,7 @@ Delimiter|Delim {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#.*$ {
|
<INITIAL,BOX,SHAPES,ELASTIC>#.*$ {
|
||||||
/* ignore comments */
|
/* ignore comments */
|
||||||
#ifdef LEXER_DEBUG
|
#ifdef LEXER_DEBUG
|
||||||
fprintf (stderr, "COMMENT: %s\n", yytext+1);
|
fprintf (stderr, "COMMENT: %s\n", yytext+1);
|
||||||
@ -395,25 +404,15 @@ Delimiter|Delim {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
. {
|
<INITIAL,BOX,SHAPES,ELASTIC>. {
|
||||||
if ((yyextra->yyerrcnt)++ < LEX_MAX_WARN)
|
/* a character that made no sense where it was encountered. Let the parser handle it. */
|
||||||
yyerror(NULL, "Unrecognized input char \'%s\'", yytext);
|
#ifdef LEXER_DEBUG
|
||||||
|
fprintf (stderr, " YUNREC: \'%c\'\n", yytext[0]);
|
||||||
|
#endif
|
||||||
return YUNREC;
|
return YUNREC;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
<SPEEDMODE>{PBOX}{PWHITE}+{PWORD}|{PPARENT} {
|
|
||||||
/* end speedmode, but then give back the whole match so BOX or PARENT can be started properly */
|
|
||||||
yyless(0);
|
|
||||||
BEGIN(INITIAL);
|
|
||||||
report_state(" STATUS", "", "INITIAL");
|
|
||||||
}
|
|
||||||
|
|
||||||
<SPEEDMODE>\n {}
|
|
||||||
|
|
||||||
<SPEEDMODE>. /* ignore anything else */
|
|
||||||
|
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
|
|
||||||
@ -433,19 +432,10 @@ void inflate_inbuf(void *yyscanner, const char *configfile)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
void begin_speedmode(void *yyscanner)
|
|
||||||
{
|
|
||||||
struct yyguts_t *yyg = (struct yyguts_t *) yyscanner;
|
|
||||||
BEGIN(SPEEDMODE);
|
|
||||||
report_state(" STATUS", "begin_speedmode()", "SPEEDMODE");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void report_state_char(char *symbol, char c, char *expected_state_str)
|
static void report_state_char(char *symbol, char c, char *expected_state_str)
|
||||||
{
|
{
|
||||||
char *s = (char *) malloc(4);
|
char *s = (char *) malloc(4);
|
||||||
sprintf(s, "'%c'", c);
|
sprintf(s, "'%c'", c >= ' ' && c <= 126 ? c : '?');
|
||||||
report_state(symbol, s, expected_state_str);
|
report_state(symbol, s, expected_state_str);
|
||||||
BFREE(s);
|
BFREE(s);
|
||||||
}
|
}
|
||||||
@ -464,4 +454,30 @@ static void report_state(char *symbol, char *text, char *expected_state_str)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static int change_string_delimiters(pass_to_flex *extra, char *delim_expr)
|
||||||
|
{
|
||||||
|
if (strlen(delim_expr) != 2) {
|
||||||
|
yyerror(NULL, "invalid string delimiter specification -- %s", delim_expr);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (delim_expr[0] == delim_expr[1]) {
|
||||||
|
yyerror(NULL, "string delimiter and escape char may not be the same");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (strchr (LEX_SDELIM, delim_expr[1]) == NULL) {
|
||||||
|
yyerror(NULL, "invalid string delimiter -- %c (try one of %s)", delim_expr[1], LEX_SDELIM_RECOMMENDED);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef LEXER_DEBUG
|
||||||
|
fprintf(stderr, "YDELIMS: change_string_delimiters('%c', '%c')\n", delim_expr[0], delim_expr[1]);
|
||||||
|
#endif
|
||||||
|
extra->sesc = delim_expr[0];
|
||||||
|
extra->sdel = delim_expr[1];
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*EOF*/ /* vim: set cindent sw=4: */
|
/*EOF*/ /* vim: set cindent sw=4: */
|
||||||
|
@ -39,48 +39,11 @@
|
|||||||
#include "lex.yy.h"
|
#include "lex.yy.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 "\"~'`!@%&*=:;<>?/|.\\"
|
|
||||||
|
|
||||||
static pcre2_code *eol_pattern = NULL;
|
static pcre2_code *eol_pattern = NULL;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void chg_strdelims (pass_to_bison *bison_args, const char asesc, const char asdel)
|
|
||||||
{
|
|
||||||
#ifdef PARSER_DEBUG
|
|
||||||
fprintf (stderr, " STATUS: chg_strdelims ('%c', '%c') - This changes lexer behavior!\n", asesc, asdel);
|
|
||||||
#endif
|
|
||||||
bison_args->sesc = asesc;
|
|
||||||
bison_args->sdel = asdel;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int action_chg_delim(pass_to_bison *bison_args, char *delim_expr)
|
|
||||||
{
|
|
||||||
if (strlen(delim_expr) != 2) {
|
|
||||||
yyerror(bison_args, "invalid string delimiter specification -- %s", delim_expr);
|
|
||||||
return RC_ERROR;
|
|
||||||
}
|
|
||||||
if (delim_expr[0] == delim_expr[1]) {
|
|
||||||
yyerror(bison_args, "string delimiter and escape char may not be the same");
|
|
||||||
return RC_ERROR;
|
|
||||||
}
|
|
||||||
if (strchr (LEX_SDELIM, delim_expr[1]) == NULL) {
|
|
||||||
yyerror(bison_args, "invalid string delimiter -- %c (try one of %s)",
|
|
||||||
delim_expr[1], LEX_SDELIM);
|
|
||||||
return RC_ERROR;
|
|
||||||
}
|
|
||||||
chg_strdelims(bison_args, delim_expr[0], delim_expr[1]);
|
|
||||||
return RC_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static int check_sizes(pass_to_bison *bison_args)
|
static int check_sizes(pass_to_bison *bison_args)
|
||||||
/*
|
/*
|
||||||
* For the author's convenience, it is required that shapes on one side
|
* For the author's convenience, it is required that shapes on one side
|
||||||
@ -95,7 +58,7 @@ static int check_sizes(pass_to_bison *bison_args)
|
|||||||
int i, j, k;
|
int i, j, k;
|
||||||
|
|
||||||
#ifdef PARSER_DEBUG
|
#ifdef PARSER_DEBUG
|
||||||
fprintf (stderr, "check_sizes()\n");
|
fprintf (stderr, " Parser: check_sizes()\n");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
for (i=0; i<NUM_SIDES; ++i) {
|
for (i=0; i<NUM_SIDES; ++i) {
|
||||||
@ -163,7 +126,7 @@ static int corner_check(pass_to_bison *bison_args)
|
|||||||
int c;
|
int c;
|
||||||
|
|
||||||
#ifdef PARSER_DEBUG
|
#ifdef PARSER_DEBUG
|
||||||
fprintf (stderr, "corner_check()\n");
|
fprintf (stderr, " Parser: corner_check()\n");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
for (c=0; c<NUM_CORNERS; ++c) {
|
for (c=0; c<NUM_CORNERS; ++c) {
|
||||||
@ -183,7 +146,7 @@ static shape_t non_existent_elastics(pass_to_bison *bison_args)
|
|||||||
shape_t i;
|
shape_t i;
|
||||||
|
|
||||||
#ifdef PARSER_DEBUG
|
#ifdef PARSER_DEBUG
|
||||||
fprintf (stderr, "non_existent_elastics()\n");
|
fprintf (stderr, " Parser: non_existent_elastics()\n");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
for (i=0; i<NUM_SHAPES; ++i) {
|
for (i=0; i<NUM_SHAPES; ++i) {
|
||||||
@ -202,7 +165,7 @@ static int insufficient_elasticity(pass_to_bison *bison_args)
|
|||||||
int i, j, ef;
|
int i, j, ef;
|
||||||
|
|
||||||
#ifdef PARSER_DEBUG
|
#ifdef PARSER_DEBUG
|
||||||
fprintf (stderr, "insufficient_elasticity()\n");
|
fprintf (stderr, " Parser: insufficient_elasticity()\n");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
for (i=0; i<NUM_SIDES; ++i) {
|
for (i=0; i<NUM_SIDES; ++i) {
|
||||||
@ -226,7 +189,7 @@ static int adjoining_elastics(pass_to_bison *bison_args)
|
|||||||
int i, j, ef;
|
int i, j, ef;
|
||||||
|
|
||||||
#ifdef PARSER_DEBUG
|
#ifdef PARSER_DEBUG
|
||||||
fprintf (stderr, "adjoining_elastics()\n");
|
fprintf (stderr, " Parser: adjoining_elastics()\n");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
for (i=0; i<NUM_SIDES; ++i) {
|
for (i=0; i<NUM_SIDES; ++i) {
|
||||||
@ -304,7 +267,6 @@ void recover(pass_to_bison *bison_args)
|
|||||||
bison_args->num_mandatory = 0;
|
bison_args->num_mandatory = 0;
|
||||||
bison_args->time_for_se_check = 0;
|
bison_args->time_for_se_check = 0;
|
||||||
bison_args->num_shapespec = 0;
|
bison_args->num_shapespec = 0;
|
||||||
chg_strdelims(bison_args, '\\', '\"');
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Clear current design
|
* Clear current design
|
||||||
@ -358,7 +320,7 @@ static int full_parse_required()
|
|||||||
result = opt.r || opt.l || (opt.query != NULL && !query_is_undoc());
|
result = opt.r || opt.l || (opt.query != NULL && !query_is_undoc());
|
||||||
}
|
}
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
fprintf(stderr, "full_parse_required() -> %s\n", result ? "true" : "false");
|
fprintf(stderr, " Parser: full_parse_required() -> %s\n", result ? "true" : "false");
|
||||||
#endif
|
#endif
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -642,9 +604,9 @@ int action_finalize_shapes(pass_to_bison *bison_args)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#ifdef PARSER_DEBUG
|
#ifdef PARSER_DEBUG
|
||||||
fprintf (stderr, "Minimum box dimensions: width %d height %d\n",
|
fprintf (stderr, " Parser: Minimum box dimensions: width %d height %d\n",
|
||||||
(int) curdes.minwidth, (int) curdes.minheight);
|
(int) curdes.minwidth, (int) curdes.minheight);
|
||||||
fprintf (stderr, "Maximum shape height: %d\n",
|
fprintf (stderr, " Parser: Maximum shape height: %d\n",
|
||||||
(int) curdes.maxshapeheight);
|
(int) curdes.maxshapeheight);
|
||||||
#endif
|
#endif
|
||||||
return RC_SUCCESS;
|
return RC_SUCCESS;
|
||||||
@ -662,7 +624,6 @@ int action_finalize_shapes(pass_to_bison *bison_args)
|
|||||||
*/
|
*/
|
||||||
int action_start_parsing_design(pass_to_bison *bison_args, char *design_name)
|
int action_start_parsing_design(pass_to_bison *bison_args, char *design_name)
|
||||||
{
|
{
|
||||||
chg_strdelims(bison_args, '\\', '\"');
|
|
||||||
bison_args->speeding = 0;
|
bison_args->speeding = 0;
|
||||||
bison_args->skipping = 0;
|
bison_args->skipping = 0;
|
||||||
|
|
||||||
@ -674,9 +635,10 @@ int action_start_parsing_design(pass_to_bison *bison_args, char *design_name)
|
|||||||
|
|
||||||
if (!design_needed(bison_args)) {
|
if (!design_needed(bison_args)) {
|
||||||
bison_args->speeding = 1;
|
bison_args->speeding = 1;
|
||||||
begin_speedmode(bison_args->lexer_state);
|
#ifdef PARSER_DEBUG
|
||||||
init_design(bison_args, &(curdes));
|
fprintf (stderr, " Parser: Skipping to next design (lexer doesn't know!)\n");
|
||||||
return RC_ERROR;
|
#endif
|
||||||
|
return RC_ERROR; /* trigger the parser's `error` rule, which will skip to the next design */
|
||||||
}
|
}
|
||||||
return RC_SUCCESS;
|
return RC_SUCCESS;
|
||||||
}
|
}
|
||||||
@ -693,7 +655,7 @@ int action_start_parsing_design(pass_to_bison *bison_args, char *design_name)
|
|||||||
int action_parent_config(pass_to_bison *bison_args, char *filepath)
|
int action_parent_config(pass_to_bison *bison_args, char *filepath)
|
||||||
{
|
{
|
||||||
#ifdef PARSER_DEBUG
|
#ifdef PARSER_DEBUG
|
||||||
fprintf (stderr, "parent config file specified: [%s]\n", filepath);
|
fprintf (stderr, " Parser: parent config file specified: [%s]\n", filepath);
|
||||||
#endif
|
#endif
|
||||||
if (filepath == NULL || filepath[0] == '\0') {
|
if (filepath == NULL || filepath[0] == '\0') {
|
||||||
bison_args->skipping = 1;
|
bison_args->skipping = 1;
|
||||||
@ -720,13 +682,13 @@ int action_parent_config(pass_to_bison *bison_args, char *filepath)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#ifdef PARSER_DEBUG
|
#ifdef PARSER_DEBUG
|
||||||
fprintf (stderr, "parent config file path resolved: [%s]\n", filepath);
|
fprintf (stderr, " Parser: parent config file path resolved: [%s]\n", filepath);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int is_new = !array_contains(bison_args->parent_configs, bison_args->num_parent_configs, filepath);
|
int is_new = !array_contains(bison_args->parent_configs, bison_args->num_parent_configs, filepath);
|
||||||
if (!is_new) {
|
if (!is_new) {
|
||||||
#ifdef PARSER_DEBUG
|
#ifdef PARSER_DEBUG
|
||||||
fprintf (stderr, "duplicate parent / cycle: [%s]\n", filepath);
|
fprintf (stderr, " Parser: duplicate parent / cycle: [%s]\n", filepath);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -822,7 +784,7 @@ int action_add_design(pass_to_bison *bison_args, char *design_primary_name, char
|
|||||||
int action_record_keyword(pass_to_bison *bison_args, char *keyword, char *value)
|
int action_record_keyword(pass_to_bison *bison_args, char *keyword, char *value)
|
||||||
{
|
{
|
||||||
#ifdef PARSER_DEBUG
|
#ifdef PARSER_DEBUG
|
||||||
fprintf (stderr, "entry rule fulfilled [%s = %s]\n", keyword, value);
|
fprintf (stderr, " Parser: entry rule fulfilled [%s = %s]\n", keyword, value);
|
||||||
#endif
|
#endif
|
||||||
if (strcasecmp (keyword, "author") == 0) {
|
if (strcasecmp (keyword, "author") == 0) {
|
||||||
curdes.author = (char *) strdup (value);
|
curdes.author = (char *) strdup (value);
|
||||||
@ -946,7 +908,7 @@ int action_add_alias(pass_to_bison *bison_args, char *alias_name)
|
|||||||
}
|
}
|
||||||
if (alias_exists_in_child_configs(bison_args, alias_name)) {
|
if (alias_exists_in_child_configs(bison_args, alias_name)) {
|
||||||
#ifdef PARSER_DEBUG
|
#ifdef PARSER_DEBUG
|
||||||
fprintf (stderr, "alias already used by child config, dropping: %s\n", alias_name);
|
fprintf (stderr, " Parser: alias already used by child config, dropping: %s\n", alias_name);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -978,7 +940,7 @@ static char *adjust_eols(char *sample)
|
|||||||
int action_sample_block(pass_to_bison *bison_args, char *sample)
|
int action_sample_block(pass_to_bison *bison_args, char *sample)
|
||||||
{
|
{
|
||||||
#ifdef PARSER_DEBUG
|
#ifdef PARSER_DEBUG
|
||||||
fprintf(stderr, "SAMPLE block rule satisfied\n");
|
fprintf(stderr, " Parser: SAMPLE block rule satisfied\n");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (curdes.sample) {
|
if (curdes.sample) {
|
||||||
|
@ -48,9 +48,6 @@
|
|||||||
int action_init_parser(pass_to_bison *bison_args);
|
int action_init_parser(pass_to_bison *bison_args);
|
||||||
|
|
||||||
|
|
||||||
int action_chg_delim(pass_to_bison *bison_args, char *delim_expr);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rule action called when a shape list was fully parsed.
|
* Rule action called when a shape list was fully parsed.
|
||||||
* @param bison_args the parser state
|
* @param bison_args the parser state
|
||||||
|
28
src/parser.y
28
src/parser.y
@ -47,12 +47,6 @@ typedef struct {
|
|||||||
/** the path to the config file we are parsing */
|
/** the path to the config file we are parsing */
|
||||||
char *config_file;
|
char *config_file;
|
||||||
|
|
||||||
/** the currently active string delimiter character */
|
|
||||||
char sdel;
|
|
||||||
|
|
||||||
/** the currently active string escape character */
|
|
||||||
char sesc;
|
|
||||||
|
|
||||||
int num_mandatory;
|
int num_mandatory;
|
||||||
|
|
||||||
int time_for_se_check;
|
int time_for_se_check;
|
||||||
@ -140,7 +134,7 @@ typedef struct {
|
|||||||
%token <shape> SHAPE
|
%token <shape> SHAPE
|
||||||
%token <num> YNUMBER
|
%token <num> YNUMBER
|
||||||
%token <c> YRXPFLAG
|
%token <c> YRXPFLAG
|
||||||
%token <s> YDELWORD
|
%token <s> YDELIMSPEC
|
||||||
|
|
||||||
%type <sentry> shape_def
|
%type <sentry> shape_def
|
||||||
%type <sentry> shape_lines
|
%type <sentry> shape_lines
|
||||||
@ -203,7 +197,16 @@ config_file: config_file design_or_error | design_or_error | config_file parent_
|
|||||||
|
|
||||||
design_or_error: design | error
|
design_or_error: design | error
|
||||||
{
|
{
|
||||||
if (!(bison_args->speeding) && !(bison_args->skipping)) {
|
/* reset alias list, as those are collected even when speedmode is on */
|
||||||
|
#ifdef PARSER_DEBUG
|
||||||
|
fprintf (stderr, " Parser: Discarding token [skipping=%s, speeding=%s]\n",
|
||||||
|
bison_args->skipping ? "true" : "false", bison_args->speeding ? "true" : "false");
|
||||||
|
#endif
|
||||||
|
if (curdes.aliases[0] != NULL) {
|
||||||
|
BFREE(curdes.aliases);
|
||||||
|
curdes.aliases = (char **) calloc(1, sizeof(char *));
|
||||||
|
}
|
||||||
|
if (!bison_args->speeding && !bison_args->skipping) {
|
||||||
recover(bison_args);
|
recover(bison_args);
|
||||||
yyerror(bison_args, "skipping to next design");
|
yyerror(bison_args, "skipping to next design");
|
||||||
bison_args->skipping = 1;
|
bison_args->skipping = 1;
|
||||||
@ -260,14 +263,14 @@ entry: KEYWORD STRING
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
#ifdef PARSER_DEBUG
|
#ifdef PARSER_DEBUG
|
||||||
fprintf (stderr, "%s: Discarding entry [%s = %s].\n", PROJECT, "parent", filename);
|
fprintf (stderr, " Parser: Discarding entry [%s = %s].\n", "parent", filename);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
| YCHGDEL YDELWORD
|
| YCHGDEL YDELIMSPEC
|
||||||
{
|
{
|
||||||
invoke_action(action_chg_delim(bison_args, $2));
|
/* string delimiters were changed - this is a lexer thing. ignore here. */
|
||||||
}
|
}
|
||||||
|
|
||||||
| YTAGS '(' tag_list ')' | YTAGS tag_entry
|
| YTAGS '(' tag_list ')' | YTAGS tag_entry
|
||||||
@ -275,7 +278,7 @@ entry: KEYWORD STRING
|
|||||||
| WORD STRING
|
| WORD STRING
|
||||||
{
|
{
|
||||||
#ifdef PARSER_DEBUG
|
#ifdef PARSER_DEBUG
|
||||||
fprintf (stderr, "%s: Discarding entry [%s = %s].\n", PROJECT, $1, $2);
|
fprintf (stderr, " Parser: Discarding entry [%s = %s].\n", $1, $2);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
@ -492,4 +495,5 @@ wlist_entry: WORD YNUMBER
|
|||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
|
|
||||||
/*EOF*/ /* vim: set sw=4 cindent: */
|
/*EOF*/ /* vim: set sw=4 cindent: */
|
||||||
|
@ -135,8 +135,6 @@ static pass_to_bison new_bison_args(const char *config_file)
|
|||||||
bison_args.num_designs = 0;
|
bison_args.num_designs = 0;
|
||||||
bison_args.design_idx = 0;
|
bison_args.design_idx = 0;
|
||||||
bison_args.config_file = (char *) config_file;
|
bison_args.config_file = (char *) config_file;
|
||||||
bison_args.sdel = '\"'; /* sdel is shared by flex and bison */
|
|
||||||
bison_args.sesc = '\\'; /* sesc is shared by flex and bison */
|
|
||||||
bison_args.num_mandatory = 0;
|
bison_args.num_mandatory = 0;
|
||||||
bison_args.time_for_se_check = 0;
|
bison_args.time_for_se_check = 0;
|
||||||
bison_args.num_shapespec = 0;
|
bison_args.num_shapespec = 0;
|
||||||
@ -150,12 +148,12 @@ static pass_to_bison new_bison_args(const char *config_file)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
static pass_to_flex new_flex_extra_data(pass_to_bison *bison_args)
|
static pass_to_flex new_flex_extra_data()
|
||||||
{
|
{
|
||||||
pass_to_flex flex_extra_data;
|
pass_to_flex flex_extra_data;
|
||||||
flex_extra_data.yyerrcnt = 0;
|
flex_extra_data.yyerrcnt = 0;
|
||||||
flex_extra_data.sdel_ptr = &(bison_args->sdel);
|
flex_extra_data.sdel = '\"';
|
||||||
flex_extra_data.sesc_ptr = &(bison_args->sesc);
|
flex_extra_data.sesc = '\\';
|
||||||
return flex_extra_data;
|
return flex_extra_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,7 +168,7 @@ static pass_to_bison parse_config_file(const char *config_file, design_t *child_
|
|||||||
pass_to_bison bison_args = new_bison_args(config_file);
|
pass_to_bison bison_args = new_bison_args(config_file);
|
||||||
bison_args.child_configs = child_configs;
|
bison_args.child_configs = child_configs;
|
||||||
bison_args.num_child_configs = num_child_configs;
|
bison_args.num_child_configs = num_child_configs;
|
||||||
pass_to_flex flex_extra_data = new_flex_extra_data(&bison_args);
|
pass_to_flex flex_extra_data = new_flex_extra_data();
|
||||||
current_bison_args = &bison_args;
|
current_bison_args = &bison_args;
|
||||||
|
|
||||||
yylex_init (&(bison_args.lexer_state));
|
yylex_init (&(bison_args.lexer_state));
|
||||||
|
@ -11,7 +11,7 @@ that speedmode is properly terminated.)
|
|||||||
:EXPECTED
|
:EXPECTED
|
||||||
boxes: 131_data/B.cfg: line 32: entries SAMPLE, SHAPES, and ELASTIC are mandatory
|
boxes: 131_data/B.cfg: line 32: entries SAMPLE, SHAPES, and ELASTIC are mandatory
|
||||||
boxes: 131_data/B.cfg: line 32: skipping to next design
|
boxes: 131_data/B.cfg: line 32: skipping to next design
|
||||||
boxes: 131_data/A.cfg: line 29: string expected
|
boxes: 131_data/A.cfg: line 29: syntax error
|
||||||
boxes: 131_data/A.cfg: line 29: skipping to next design
|
boxes: 131_data/A.cfg: line 29: skipping to next design
|
||||||
3 Available Styles:
|
3 Available Styles:
|
||||||
-------------------
|
-------------------
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
:DESC
|
:DESC
|
||||||
Test that the list of aliases is properly reset when lexer is in speedmode.
|
Test that the list of aliases is properly reset when parser is in speedmode.
|
||||||
|
|
||||||
:ARGS
|
:ARGS
|
||||||
-f 137_design_alias_no_accumulation.cfg -d alias3b -l
|
-f 137_design_alias_no_accumulation.cfg -d alias3b -l
|
||||||
|
33
test/168_parent_keyword_in_sample_ok.cfg
Normal file
33
test/168_parent_keyword_in_sample_ok.cfg
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
|
||||||
|
BOX designA
|
||||||
|
|
||||||
|
sample
|
||||||
|
A mentions the word parent THIS_IS_NOT_A_CONFIG_FILE
|
||||||
|
ends
|
||||||
|
|
||||||
|
shapes {
|
||||||
|
w ("A")
|
||||||
|
}
|
||||||
|
|
||||||
|
elastic (
|
||||||
|
w
|
||||||
|
)
|
||||||
|
|
||||||
|
END designA
|
||||||
|
|
||||||
|
|
||||||
|
BOX designB
|
||||||
|
|
||||||
|
sample
|
||||||
|
B is the one we select
|
||||||
|
ends
|
||||||
|
|
||||||
|
shapes {
|
||||||
|
w ("B")
|
||||||
|
}
|
||||||
|
|
||||||
|
elastic (
|
||||||
|
w
|
||||||
|
)
|
||||||
|
|
||||||
|
END designB
|
27
test/168_parent_keyword_in_sample_ok.txt
Normal file
27
test/168_parent_keyword_in_sample_ok.txt
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
:DESC
|
||||||
|
Test that the keyword 'parent' may occur as a regular string in a sample block, without triggering parent config file
|
||||||
|
resolution.
|
||||||
|
|
||||||
|
:ARGS
|
||||||
|
-f 168_parent_keyword_in_sample_ok.cfg -d designB -l
|
||||||
|
:INPUT
|
||||||
|
:OUTPUT-FILTER
|
||||||
|
:EXPECTED
|
||||||
|
Complete Design Information for "designB":
|
||||||
|
------------------------------------------
|
||||||
|
Alias Names: none
|
||||||
|
Author: (unknown author)
|
||||||
|
Original Designer: (unknown artist)
|
||||||
|
Creation Date: (unknown)
|
||||||
|
Current Revision: (unknown)
|
||||||
|
Configuration File: 168_parent_keyword_in_sample_ok.cfg
|
||||||
|
Indentation Mode: box (indent box)
|
||||||
|
Replacement Rules: none
|
||||||
|
Reversion Rules: none
|
||||||
|
Minimum Box Dimensions: 3 x 3 (width x height)
|
||||||
|
Default Padding: none
|
||||||
|
Default Killblank: no
|
||||||
|
Tags: none
|
||||||
|
Elastic Shapes: N, E, S, W
|
||||||
|
Defined Shapes: W: "B"
|
||||||
|
:EOF
|
Loading…
x
Reference in New Issue
Block a user