mirror of
https://github.com/ascii-boxes/boxes.git
synced 2024-12-04 22:11:07 +01:00
Enable lexer and parser to handle UTF-8 config file #72
This commit is contained in:
parent
4ff37eb5ba
commit
8a7bb8039f
1
.gitattributes
vendored
1
.gitattributes
vendored
@ -25,5 +25,4 @@ tools/VERIFICATION.txt text eol=crlf
|
||||
/test/*.txt text eol=lf
|
||||
|
||||
# some files which are currently in ISO-8859-15 encoding
|
||||
/src/lexer.l text working-tree-encoding=ISO_8859-15
|
||||
/test/111_manual_encoding_iso.txt text working-tree-encoding=ISO_8859-15
|
||||
|
133
src/lexer.l
133
src/lexer.l
@ -19,7 +19,7 @@
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "bxstring.h"
|
||||
|
||||
typedef struct {
|
||||
int yyerrcnt;
|
||||
@ -33,8 +33,8 @@ typedef struct {
|
||||
|
||||
|
||||
/*
|
||||
* Valid characters to be used as string delimiters. Note that the
|
||||
* following list must correspond to the SDELIM definition below.
|
||||
* Valid characters to be used as string delimiters.
|
||||
* The following list must correspond to the SDELIM definition below.
|
||||
*/
|
||||
#define LEX_SDELIM "\"~'`!@%&*=:;<>?/|.\\"
|
||||
#define LEX_SDELIM_RECOMMENDED "\"~'!|"
|
||||
@ -50,7 +50,7 @@ typedef struct {
|
||||
* @param yyscanner pointer to the scanner data block
|
||||
* @param configfile the path to the config file we are reading
|
||||
*/
|
||||
void inflate_inbuf(void *yyscanner, const char *configfile);
|
||||
void inflate_inbuf(void *yyscanner, const bxstr_t *configfile);
|
||||
|
||||
}
|
||||
|
||||
@ -58,12 +58,14 @@ void inflate_inbuf(void *yyscanner, const char *configfile);
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <unitypes.h>
|
||||
|
||||
#include "boxes.h"
|
||||
#include "shape.h"
|
||||
#include "tools.h"
|
||||
#include "parsing.h"
|
||||
#include "parser.h"
|
||||
#include "unicode.h"
|
||||
|
||||
|
||||
#define LEX_MAX_WARN 3 /* number of lex errors per design */
|
||||
@ -99,8 +101,23 @@ static int change_string_delimiters(pass_to_flex *extra, char *delim_expr);
|
||||
%x DELIMSPEC
|
||||
%x PARENT
|
||||
|
||||
/*
|
||||
* The following paragraph contains patterns to recognize UTF-8 characters from a byte stream, based on
|
||||
* - https://stackoverflow.com/a/10253320/1005481 by Zack Weinberg (under CC-BY-SA 3.0 license)
|
||||
* - https://www.w3.org/2005/03/23-lex-U by Eric Prud'hommeaux, W3C (under the W3C Document License)
|
||||
*/
|
||||
PBOM \xEF\xBB\xBF
|
||||
U2A [\xC2-\xDF][\x80-\xBF]
|
||||
U2B \xE0[\xA0-\xBF][\x80-\xBF]
|
||||
U3A [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}
|
||||
U3B \xED[\x80-\x9F][\x80-\xBF]
|
||||
U4A \xF0[\x90-\xBF][\x80-\xBF]{2}
|
||||
U4B [\xF1-\xF3][\x80-\xBF]{3}
|
||||
U4C \xF4[\x80-\x8F][\x80-\xBF]{2}
|
||||
UTF_8 {U2A}|{U2B}|{U3A}|{U3B}|{U4A}|{U4B}|{U4C}
|
||||
|
||||
PWORD [a-zA-ZäöüÄÖÜ][a-zA-Z0-9\-_üäöÜÄÖß]*
|
||||
PWORD (?:[a-zA-Z]|{UTF_8})(?:[a-zA-Z0-9_-]|{UTF_8})*
|
||||
PASCII_ID [a-zA-Z][a-zA-Z0-9_-]*
|
||||
PWHITE [ \t\r\n]
|
||||
SDELIM [\"~\'`!@\%\&\*=:;<>\?/|\.\\]
|
||||
PPARENT parent
|
||||
@ -116,16 +133,17 @@ PFILENAME [^\r\n]+
|
||||
*/
|
||||
|
||||
|
||||
<INITIAL,BOX,DELIMSPEC,ELASTIC,SHAPES>{PWHITE} /* ignore whitespace */
|
||||
<INITIAL,BOX,DELIMSPEC,ELASTIC,SHAPES>{PWHITE}|{PBOM} /* ignore whitespace and a byte order mark */
|
||||
|
||||
<DELIMSPEC>[^ \t\r\n]+ {
|
||||
/*
|
||||
* String delimiter spec - like WORD, but allow any character
|
||||
*/
|
||||
yylval->s = (char *) strdup (yytext);
|
||||
yylval->s = bxs_from_ascii("IGNORED");
|
||||
char *str = (char *) strdup(yytext);
|
||||
BEGIN(BOX);
|
||||
report_state("YDELIMS", yytext, "INITIAL");
|
||||
if (change_string_delimiters(yyextra, yylval->s) != 0) {
|
||||
report_state("YDELIMS", str, "INITIAL");
|
||||
if (change_string_delimiters(yyextra, str) != 0) {
|
||||
return YUNREC;
|
||||
}
|
||||
return YDELIMSPEC;
|
||||
@ -137,7 +155,6 @@ PFILENAME [^\r\n]+
|
||||
* Strings -- first match everything starting from a potential string delimiter until the end of the line. We
|
||||
* will give back what we don't need and also detect unterminated strings. Strings always end on the same line.
|
||||
*/
|
||||
char *p;
|
||||
int rest_len = yyleng - 1; /* length of string pointed to by p */
|
||||
int qcnt = 0; /* esc char count in current string */
|
||||
|
||||
@ -145,27 +162,33 @@ PFILENAME [^\r\n]+
|
||||
REJECT; /* that was not our delimiter */
|
||||
}
|
||||
|
||||
yylval->s = (char *) strdup (yytext + 1);
|
||||
if (yylval->s == NULL) {
|
||||
char *str = (char *) strdup(yytext + 1);
|
||||
if (str == NULL) {
|
||||
perror (PROJECT);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
p = yylval->s;
|
||||
char *p = str;
|
||||
|
||||
while (*p) {
|
||||
if (*p == yyextra->sesc) {
|
||||
memmove (p, p+1, rest_len); /* incl. '\0' */
|
||||
++qcnt;
|
||||
--rest_len;
|
||||
if (*p == '\0')
|
||||
if (*p == '\0') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (*p == yyextra->sdel) {
|
||||
*p = '\0';
|
||||
yyless ((p-yylval->s)+2+qcnt); /* string plus quotes */
|
||||
yyless ((p - str) + 2 + qcnt); /* string plus quotes */
|
||||
#ifdef LEXER_DEBUG
|
||||
fprintf (stderr, " STRING: \"%s\"\n", yylval->s);
|
||||
fprintf (stderr, " STRING: \"%s\"\n", str);
|
||||
#endif
|
||||
|
||||
uint32_t *utf8 = u32_strconv_from_arg(str, CONFIG_FILE_ENCODING);
|
||||
yylval->s = bxs_from_unicode(utf8);
|
||||
BFREE(utf8);
|
||||
BFREE(str);
|
||||
return STRING;
|
||||
}
|
||||
--rest_len;
|
||||
@ -174,6 +197,7 @@ PFILENAME [^\r\n]+
|
||||
if ((yyextra->yyerrcnt)++ < 5) {
|
||||
yyerror(NULL, "Unterminated String -- %s", yytext);
|
||||
}
|
||||
BFREE(str);
|
||||
return YUNREC;
|
||||
}
|
||||
|
||||
@ -185,17 +209,15 @@ PFILENAME [^\r\n]+
|
||||
}
|
||||
|
||||
<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';
|
||||
}
|
||||
uint32_t *utf8 = u32_strconv_from_arg(yytext, CONFIG_FILE_ENCODING);
|
||||
bxstr_t *bxstr = bxs_from_unicode(utf8);
|
||||
yylval->s = bxs_trim(bxstr);
|
||||
|
||||
BFREE(utf8);
|
||||
bxs_free(bxstr);
|
||||
|
||||
BEGIN(INITIAL);
|
||||
report_state("FILENAM", yylval->s, "INITIAL");
|
||||
report_state("FILENAM", bxs_to_output(yylval->s), "INITIAL");
|
||||
return FILENAME;
|
||||
}
|
||||
|
||||
@ -225,27 +247,37 @@ PFILENAME [^\r\n]+
|
||||
--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) {
|
||||
char *sample = (char *) strdup(yytext);
|
||||
if (sample == NULL) {
|
||||
perror (PROJECT);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
*p-- = 'n';
|
||||
|
||||
len = p - yytext; /* yyless(n): push back all but the first n */
|
||||
yyless (len); /* allow him to return YENDSAMPLE */
|
||||
yyless (len); /* allow the lexer to return YENDSAMPLE */
|
||||
|
||||
yylval->s[len] = '\n'; /* replace 'e' with newline */
|
||||
btrim (yylval->s, &len);
|
||||
sample[len] = '\n'; /* replace 'e' with newline */
|
||||
btrim(sample, &len);
|
||||
if (len > 0) {
|
||||
strcat (yylval->s, "\n"); /* memory was allocated with strdup */
|
||||
uint32_t *utf8 = u32_strconv_from_arg(sample, CONFIG_FILE_ENCODING);
|
||||
uint32_t *nl = u32_strconv_from_arg("\n", CONFIG_FILE_ENCODING);
|
||||
bxstr_t *bxstr = bxs_from_unicode(utf8);
|
||||
bxstr_t *bxstr2 = bxs_rtrim(bxstr);
|
||||
bxs_free(bxstr);
|
||||
bxstr = bxs_strcat(bxstr2, nl);
|
||||
BFREE(nl);
|
||||
BFREE(utf8);
|
||||
BFREE(sample);
|
||||
bxs_free(bxstr2);
|
||||
yylval->s = bxstr;
|
||||
return STRING;
|
||||
}
|
||||
else {
|
||||
if ((yyextra->yyerrcnt)++ < 5) {
|
||||
yyerror(NULL, "SAMPLE block must not be empty");
|
||||
}
|
||||
BFREE (yylval->s);
|
||||
BFREE(sample);
|
||||
return YUNREC;
|
||||
}
|
||||
}
|
||||
@ -339,8 +371,8 @@ PFILENAME [^\r\n]+
|
||||
#ifdef LEXER_DEBUG
|
||||
fprintf (stderr, "KEYWORD: %s\n", yytext);
|
||||
#endif
|
||||
yylval->s = (char *) strdup (yytext);
|
||||
if (yylval->s == NULL) {
|
||||
yylval->ascii = strdup(yytext);
|
||||
if (yylval->ascii == NULL) {
|
||||
perror (PROJECT);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
@ -357,19 +389,35 @@ PFILENAME [^\r\n]+
|
||||
return YCHGDEL;
|
||||
}
|
||||
|
||||
<INITIAL,BOX>{PASCII_ID} {
|
||||
/*
|
||||
* a free-floating word which is not a string, i.e. it does not have delimiting characters (ASCII version)
|
||||
*/
|
||||
yylval->ascii = strdup(yytext);
|
||||
if (yylval->ascii == NULL) {
|
||||
perror (PROJECT);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
#ifdef LEXER_DEBUG
|
||||
fprintf (stderr, "ASCIIID: %s\n", yylval->ascii);
|
||||
#endif
|
||||
return ASCII_ID;
|
||||
}
|
||||
|
||||
<INITIAL,BOX>{PWORD} {
|
||||
/*
|
||||
* a free-floating word which is not a string, i.e. it does not have delimiting characters
|
||||
*/
|
||||
#ifdef LEXER_DEBUG
|
||||
fprintf (stderr, " WORD: %s\n", yytext);
|
||||
#endif
|
||||
yylval->s = (char *) strdup (yytext);
|
||||
uint32_t *utf8 = u32_strconv_from_arg(yytext, CONFIG_FILE_ENCODING);
|
||||
yylval->s = bxs_from_unicode(utf8);
|
||||
if (yylval->s == NULL) {
|
||||
perror (PROJECT);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
#ifdef LEXER_DEBUG
|
||||
fprintf (stderr, " WORD: %s\n", u32_strconv_to_output(utf8));
|
||||
#endif
|
||||
BFREE(utf8);
|
||||
return WORD;
|
||||
}
|
||||
|
||||
@ -411,14 +459,17 @@ PFILENAME [^\r\n]+
|
||||
%%
|
||||
|
||||
|
||||
void inflate_inbuf(void *yyscanner, const char *configfile)
|
||||
void inflate_inbuf(void *yyscanner, const bxstr_t *configfile)
|
||||
{
|
||||
struct stat sinf;
|
||||
|
||||
if (stat(configfile, &sinf)) {
|
||||
char *utf8 = u32_strconv_to_arg(configfile->memory, "UTF-8");
|
||||
if (stat(utf8, &sinf)) {
|
||||
perror (PROJECT);
|
||||
BFREE(utf8);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
BFREE(utf8);
|
||||
struct yyguts_t *yyg = (struct yyguts_t *) yyscanner;
|
||||
yy_delete_buffer(YY_CURRENT_BUFFER, yyscanner);
|
||||
yy_switch_to_buffer (yy_create_buffer(yyin, sinf.st_size+10, yyscanner), yyscanner);
|
||||
|
468
src/parsecode.c
468
src/parsecode.c
@ -18,21 +18,25 @@
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <unictype.h>
|
||||
#include <unistr.h>
|
||||
|
||||
#include "discovery.h"
|
||||
#include "tools.h"
|
||||
#include "regulex.h"
|
||||
#include "unicode.h"
|
||||
#include "query.h"
|
||||
#include "parsecode.h"
|
||||
#include "parsing.h"
|
||||
#include "parser.h"
|
||||
#include "lex.yy.h"
|
||||
#include "query.h"
|
||||
#include "regulex.h"
|
||||
#include "tools.h"
|
||||
#include "unicode.h"
|
||||
|
||||
#include "parser.h"
|
||||
|
||||
#include "lex.yy.h"
|
||||
|
||||
|
||||
static pcre2_code *eol_pattern = NULL;
|
||||
@ -53,19 +57,19 @@ static int check_sizes(pass_to_bison *bison_args)
|
||||
int i, j, k;
|
||||
|
||||
#ifdef PARSER_DEBUG
|
||||
fprintf (stderr, " Parser: check_sizes()\n");
|
||||
fprintf(stderr, " Parser: check_sizes()\n");
|
||||
#endif
|
||||
|
||||
for (i=0; i<NUM_SIDES; ++i) {
|
||||
for (i = 0; i < NUM_SIDES; ++i) {
|
||||
if (i == 0 || i == 2) {
|
||||
/*
|
||||
* horizontal
|
||||
*/
|
||||
for (j=0; j<SHAPES_PER_SIDE-1; ++j) {
|
||||
for (j = 0; j < SHAPES_PER_SIDE - 1; ++j) {
|
||||
if (curdes.shape[sides[i][j]].height == 0) {
|
||||
continue;
|
||||
}
|
||||
for (k=j+1; k<SHAPES_PER_SIDE; ++k) {
|
||||
for (k = j + 1; k < SHAPES_PER_SIDE; ++k) {
|
||||
if (curdes.shape[sides[i][k]].height == 0) {
|
||||
continue;
|
||||
}
|
||||
@ -83,11 +87,11 @@ static int check_sizes(pass_to_bison *bison_args)
|
||||
/*
|
||||
* vertical
|
||||
*/
|
||||
for (j=0; j<SHAPES_PER_SIDE-1; ++j) {
|
||||
for (j = 0; j < SHAPES_PER_SIDE - 1; ++j) {
|
||||
if (curdes.shape[sides[i][j]].width == 0) {
|
||||
continue;
|
||||
}
|
||||
for (k=j+1; k<SHAPES_PER_SIDE; ++k) {
|
||||
for (k = j + 1; k < SHAPES_PER_SIDE; ++k) {
|
||||
if (curdes.shape[sides[i][k]].width == 0) {
|
||||
continue;
|
||||
}
|
||||
@ -103,7 +107,7 @@ static int check_sizes(pass_to_bison *bison_args)
|
||||
}
|
||||
}
|
||||
|
||||
return 0; /* all clear */
|
||||
return 0; /* all clear */
|
||||
}
|
||||
|
||||
|
||||
@ -121,17 +125,17 @@ static int corner_check(pass_to_bison *bison_args)
|
||||
int c;
|
||||
|
||||
#ifdef PARSER_DEBUG
|
||||
fprintf (stderr, " Parser: corner_check()\n");
|
||||
fprintf(stderr, " Parser: corner_check()\n");
|
||||
#endif
|
||||
|
||||
for (c=0; c<NUM_CORNERS; ++c) {
|
||||
for (c = 0; c < NUM_CORNERS; ++c) {
|
||||
if (curdes.shape[corners[c]].elastic) {
|
||||
yyerror(bison_args, "Corners may not be elastic (%s)", shape_name[corners[c]]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0; /* all clear */
|
||||
return 0; /* all clear */
|
||||
}
|
||||
|
||||
|
||||
@ -141,16 +145,16 @@ static shape_t non_existent_elastics(pass_to_bison *bison_args)
|
||||
shape_t i;
|
||||
|
||||
#ifdef PARSER_DEBUG
|
||||
fprintf (stderr, " Parser: non_existent_elastics()\n");
|
||||
fprintf(stderr, " Parser: non_existent_elastics()\n");
|
||||
#endif
|
||||
|
||||
for (i=0; i<NUM_SHAPES; ++i) {
|
||||
if (curdes.shape[i].elastic && isempty(curdes.shape+i)) {
|
||||
for (i = 0; i < NUM_SHAPES; ++i) {
|
||||
if (curdes.shape[i].elastic && isempty(curdes.shape + i)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return (shape_t) NUM_SHAPES; /* all elastic shapes exist */
|
||||
return (shape_t) NUM_SHAPES; /* all elastic shapes exist */
|
||||
}
|
||||
|
||||
|
||||
@ -160,21 +164,21 @@ static int insufficient_elasticity(pass_to_bison *bison_args)
|
||||
int i, j, ef;
|
||||
|
||||
#ifdef PARSER_DEBUG
|
||||
fprintf (stderr, " Parser: insufficient_elasticity()\n");
|
||||
fprintf(stderr, " Parser: insufficient_elasticity()\n");
|
||||
#endif
|
||||
|
||||
for (i=0; i<NUM_SIDES; ++i) {
|
||||
for (j=1,ef=0; j<4; ++j) {
|
||||
for (i = 0; i < NUM_SIDES; ++i) {
|
||||
for (j = 1, ef = 0; j < 4; ++j) {
|
||||
if (curdes.shape[sides[i][j]].elastic) {
|
||||
++ef;
|
||||
}
|
||||
}
|
||||
if (ef != 1 && ef != 2) {
|
||||
return 1; /* error */
|
||||
return 1; /* error */
|
||||
}
|
||||
}
|
||||
|
||||
return 0; /* all clear */
|
||||
return 0; /* all clear */
|
||||
}
|
||||
|
||||
|
||||
@ -184,19 +188,20 @@ static int adjoining_elastics(pass_to_bison *bison_args)
|
||||
int i, j, ef;
|
||||
|
||||
#ifdef PARSER_DEBUG
|
||||
fprintf (stderr, " Parser: adjoining_elastics()\n");
|
||||
fprintf(stderr, " Parser: adjoining_elastics()\n");
|
||||
#endif
|
||||
|
||||
for (i=0; i<NUM_SIDES; ++i) {
|
||||
for (i = 0; i < NUM_SIDES; ++i) {
|
||||
ef = 0;
|
||||
for (j=1; j<4; ++j) {
|
||||
if (isempty(curdes.shape+sides[i][j])) {
|
||||
for (j = 1; j < 4; ++j) {
|
||||
if (isempty(curdes.shape + sides[i][j])) {
|
||||
continue;
|
||||
}
|
||||
if (curdes.shape[sides[i][j]].elastic) {
|
||||
if (ef) {
|
||||
return 1; /* error detected */
|
||||
} else {
|
||||
return 1; /* error detected */
|
||||
}
|
||||
else {
|
||||
ef = 1;
|
||||
}
|
||||
}
|
||||
@ -206,7 +211,7 @@ static int adjoining_elastics(pass_to_bison *bison_args)
|
||||
}
|
||||
}
|
||||
|
||||
return 0; /* all clear */
|
||||
return 0; /* all clear */
|
||||
}
|
||||
|
||||
|
||||
@ -217,8 +222,7 @@ int perform_se_check(pass_to_bison *bison_args)
|
||||
|
||||
s_rc = non_existent_elastics(bison_args);
|
||||
if (s_rc != NUM_SHAPES) {
|
||||
yyerror(bison_args, "Shape %s has been specified as elastic but doesn't exist",
|
||||
shape_name[s_rc]);
|
||||
yyerror(bison_args, "Shape %s has been specified as elastic but doesn't exist", shape_name[s_rc]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -244,7 +248,7 @@ int perform_se_check(pass_to_bison *bison_args)
|
||||
|
||||
static void init_design(pass_to_bison *bison_args, design_t *design)
|
||||
{
|
||||
memset (design, 0, sizeof(design_t));
|
||||
memset(design, 0, sizeof(design_t));
|
||||
design->aliases = (char **) calloc(1, sizeof(char *));
|
||||
design->indentmode = DEF_INDENTMODE;
|
||||
design->defined_in = bison_args->config_file;
|
||||
@ -259,23 +263,23 @@ static void init_design(pass_to_bison *bison_args, design_t *design)
|
||||
*/
|
||||
void recover(pass_to_bison *bison_args)
|
||||
{
|
||||
bison_args->num_mandatory = 0;
|
||||
bison_args->time_for_se_check = 0;
|
||||
bison_args->num_shapespec = 0;
|
||||
bison_args->num_mandatory = 0;
|
||||
bison_args->time_for_se_check = 0;
|
||||
bison_args->num_shapespec = 0;
|
||||
|
||||
/*
|
||||
* Clear current design
|
||||
*/
|
||||
BFREE (curdes.name);
|
||||
BFREE (curdes.author);
|
||||
BFREE (curdes.aliases);
|
||||
BFREE (curdes.designer);
|
||||
BFREE (curdes.created);
|
||||
BFREE (curdes.revision);
|
||||
BFREE (curdes.revdate);
|
||||
BFREE (curdes.sample);
|
||||
BFREE (curdes.tags);
|
||||
init_design(bison_args, &(curdes));
|
||||
/*
|
||||
* Clear current design
|
||||
*/
|
||||
BFREE(curdes.name);
|
||||
BFREE(curdes.author);
|
||||
BFREE(curdes.aliases);
|
||||
BFREE(curdes.designer);
|
||||
BFREE(curdes.created);
|
||||
BFREE(curdes.revision);
|
||||
BFREE(curdes.revdate);
|
||||
BFREE(curdes.sample);
|
||||
BFREE(curdes.tags);
|
||||
init_design(bison_args, &(curdes));
|
||||
}
|
||||
|
||||
|
||||
@ -370,23 +374,23 @@ static int alias_exists_in_child_configs(pass_to_bison *bison_args, char *alias)
|
||||
|
||||
|
||||
|
||||
static int tag_add(pass_to_bison *bison_args, char *tag)
|
||||
static int tag_add(pass_to_bison *bison_args, bxstr_t *tag)
|
||||
{
|
||||
int rc = RC_SUCCESS;
|
||||
if (tag_is_valid(tag)) {
|
||||
if (!array_contains0(curdes.tags, tag)) {
|
||||
if (is_ascii_id(tag, 1)) {
|
||||
if (!array_contains0(curdes.tags, tag->ascii)) {
|
||||
size_t num_tags = array_count0(curdes.tags);
|
||||
curdes.tags = (char **) realloc(curdes.tags, (num_tags + 2) * sizeof(char *));
|
||||
curdes.tags[num_tags] = tag;
|
||||
curdes.tags[num_tags] = (char *) strdup(tag->ascii);
|
||||
curdes.tags[num_tags + 1] = NULL;
|
||||
}
|
||||
else {
|
||||
yyerror(bison_args, "duplicate tag -- %s", tag);
|
||||
yyerror(bison_args, "duplicate tag -- %s", bxs_to_output(tag));
|
||||
rc = RC_ERROR;
|
||||
}
|
||||
}
|
||||
else {
|
||||
yyerror(bison_args, "invalid tag -- %s", tag);
|
||||
yyerror(bison_args, "invalid tag -- %s", bxs_to_output(tag));
|
||||
rc = RC_ERROR;
|
||||
}
|
||||
return rc;
|
||||
@ -394,51 +398,41 @@ static int tag_add(pass_to_bison *bison_args, char *tag)
|
||||
|
||||
|
||||
|
||||
static char *tag_next_comma(char *s)
|
||||
{
|
||||
char *comma = strchr(s, ',');
|
||||
if (comma == NULL) {
|
||||
comma = s + strlen(s);
|
||||
}
|
||||
return comma;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int tag_split_add(pass_to_bison *bison_args, char *tag)
|
||||
static int tag_split_add(pass_to_bison *bison_args, bxstr_t *tag)
|
||||
{
|
||||
int rc = RC_SUCCESS;
|
||||
char *s = tag;
|
||||
char *c = NULL;
|
||||
uint32_t *c = NULL;
|
||||
int vis_start = 0;
|
||||
int cursor = -1;
|
||||
do {
|
||||
c = tag_next_comma(s);
|
||||
char *single_tag = trimdup(s, c - 1);
|
||||
c = bxs_strchr(tag, to_utf32(','), &cursor);
|
||||
bxstr_t *single_tag = bxs_trimdup(tag, vis_start, c != NULL ? (size_t) cursor : tag->num_chars_visible);
|
||||
int rc_add = tag_add(bison_args, single_tag);
|
||||
bxs_free(single_tag);
|
||||
if (rc_add != 0) {
|
||||
rc = rc_add;
|
||||
}
|
||||
s = c + 1;
|
||||
} while (*c != '\0');
|
||||
vis_start = cursor + 1;
|
||||
} while (c != NULL);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Add tag to list of current design's tag after validity checking.
|
||||
* @param bison_args the parser state
|
||||
* @param tag a single tag, or a comma-separated list of tags
|
||||
* @return error code, 0 on success, anything else on failure
|
||||
*/
|
||||
int tag_record(pass_to_bison *bison_args, char *tag)
|
||||
int tag_record(pass_to_bison *bison_args, bxstr_t *tag)
|
||||
{
|
||||
int rc = RC_SUCCESS;
|
||||
if (strchr(tag, ',') != NULL) {
|
||||
if (tag->num_chars_invisible > 0) {
|
||||
yyerror(bison_args, "invalid tag");
|
||||
return RC_ERROR;
|
||||
}
|
||||
if (bxs_strchr(tag, to_utf32(','), NULL) != NULL) {
|
||||
rc = tag_split_add(bison_args, tag);
|
||||
}
|
||||
else {
|
||||
char *trimmed = trimdup(tag, tag + strlen(tag) - 1);
|
||||
bxstr_t *trimmed = bxs_trimdup(tag, 0, tag->num_chars_visible);
|
||||
rc = tag_add(bison_args, trimmed);
|
||||
bxs_free(trimmed);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@ -454,12 +448,12 @@ int tag_record(pass_to_bison *bison_args, char *tag)
|
||||
*/
|
||||
int action_finalize_shapes(pass_to_bison *bison_args)
|
||||
{
|
||||
int i,j;
|
||||
shape_t fshape; /* found shape */
|
||||
int fside; /* first side */
|
||||
int sc; /* side counter */
|
||||
int side; /* effective side */
|
||||
int rc; /* received return code */
|
||||
int i, j;
|
||||
shape_t fshape; /* found shape */
|
||||
int fside; /* first side */
|
||||
int sc; /* side counter */
|
||||
int side; /* effective side */
|
||||
int rc; /* received return code */
|
||||
|
||||
/*
|
||||
* At least one shape must be specified
|
||||
@ -474,28 +468,29 @@ int action_finalize_shapes(pass_to_bison *bison_args)
|
||||
* as necessary, starting at any side which already includes at
|
||||
* least one shape in order to ensure correct measurements.
|
||||
*/
|
||||
fshape = findshape (curdes.shape, NUM_SHAPES);
|
||||
fshape = findshape(curdes.shape, NUM_SHAPES);
|
||||
if (fshape == NUM_SHAPES) {
|
||||
yyerror(bison_args, "internal error");
|
||||
return RC_ABORT;
|
||||
}
|
||||
fside = on_side (fshape, 0);
|
||||
fside = on_side(fshape, 0);
|
||||
if (fside == NUM_SIDES) {
|
||||
yyerror(bison_args, "internal error");
|
||||
return RC_ABORT;
|
||||
}
|
||||
|
||||
for (sc=0,side=fside; sc<NUM_SIDES; ++sc,side=(side+1)%NUM_SIDES) {
|
||||
shape_t nshape; /* next shape */
|
||||
sentry_t *c; /* corner to be processed */
|
||||
c = curdes.shape + sides[side][SHAPES_PER_SIDE-1];
|
||||
for (sc = 0, side = fside; sc < NUM_SIDES; ++sc, side = (side + 1) % NUM_SIDES) {
|
||||
shape_t nshape; /* next shape */
|
||||
sentry_t *c; /* corner to be processed */
|
||||
c = curdes.shape + sides[side][SHAPES_PER_SIDE - 1];
|
||||
|
||||
if (isempty(c)) {
|
||||
nshape = findshape (c, SHAPES_PER_SIDE);
|
||||
nshape = findshape(c, SHAPES_PER_SIDE);
|
||||
if (side == BLEF || side == BRIG) {
|
||||
if (nshape == SHAPES_PER_SIDE) {
|
||||
c->height = 1;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
c->height = c[nshape].height;
|
||||
}
|
||||
c->width = curdes.shape[fshape].width;
|
||||
@ -503,36 +498,38 @@ int action_finalize_shapes(pass_to_bison *bison_args)
|
||||
else {
|
||||
if (nshape == SHAPES_PER_SIDE) {
|
||||
c->width = 1;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
c->width = c[nshape].width;
|
||||
}
|
||||
c->height = curdes.shape[fshape].height;
|
||||
}
|
||||
c->elastic = 0;
|
||||
rc = genshape (c->width, c->height, &(c->chars));
|
||||
rc = genshape(c->width, c->height, &(c->chars), &(c->mbcs));
|
||||
if (rc) {
|
||||
return RC_ABORT;
|
||||
}
|
||||
}
|
||||
|
||||
fshape = sides[side][SHAPES_PER_SIDE-1];
|
||||
fshape = sides[side][SHAPES_PER_SIDE - 1];
|
||||
}
|
||||
|
||||
/*
|
||||
* For all sides whose side shapes have not been defined, generate
|
||||
* an elastic middle side shape.
|
||||
*/
|
||||
for (side=0; side<NUM_SIDES; ++side) {
|
||||
for (side = 0; side < NUM_SIDES; ++side) {
|
||||
int found = 0;
|
||||
for (i=1; i<SHAPES_PER_SIDE-1; ++i) {
|
||||
if (isempty (curdes.shape + sides[side][i])) {
|
||||
for (i = 1; i < SHAPES_PER_SIDE - 1; ++i) {
|
||||
if (isempty(curdes.shape + sides[side][i])) {
|
||||
continue;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
found = 1;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
sentry_t *c = curdes.shape + sides[side][SHAPES_PER_SIDE/2];
|
||||
sentry_t *c = curdes.shape + sides[side][SHAPES_PER_SIDE / 2];
|
||||
if (side == BLEF || side == BRIG) {
|
||||
c->width = curdes.shape[sides[side][0]].width;
|
||||
c->height = 1;
|
||||
@ -542,7 +539,7 @@ int action_finalize_shapes(pass_to_bison *bison_args)
|
||||
c->height = curdes.shape[sides[side][0]].height;
|
||||
}
|
||||
c->elastic = 1;
|
||||
rc = genshape (c->width, c->height, &(c->chars));
|
||||
rc = genshape(c->width, c->height, &(c->chars), &(c->mbcs));
|
||||
if (rc) {
|
||||
return RC_ABORT;
|
||||
}
|
||||
@ -563,10 +560,10 @@ int action_finalize_shapes(pass_to_bison *bison_args)
|
||||
/*
|
||||
* Compute minimum height/width of a box of current design
|
||||
*/
|
||||
for (i=0; i<NUM_SIDES; ++i) {
|
||||
for (i = 0; i < NUM_SIDES; ++i) {
|
||||
size_t c = 0;
|
||||
if (i % 2) { /* vertical sides */
|
||||
for (j=0; j<SHAPES_PER_SIDE; ++j) {
|
||||
if (i % 2) { /* vertical sides */
|
||||
for (j = 0; j < SHAPES_PER_SIDE; ++j) {
|
||||
if (!isempty(curdes.shape + sides[i][j])) {
|
||||
c += curdes.shape[sides[i][j]].height;
|
||||
}
|
||||
@ -575,8 +572,8 @@ int action_finalize_shapes(pass_to_bison *bison_args)
|
||||
curdes.minheight = c;
|
||||
}
|
||||
}
|
||||
else { /* horizontal sides */
|
||||
for (j=0; j<SHAPES_PER_SIDE; ++j) {
|
||||
else { /* horizontal sides */
|
||||
for (j = 0; j < SHAPES_PER_SIDE; ++j) {
|
||||
if (!isempty(curdes.shape + sides[i][j])) {
|
||||
c += curdes.shape[sides[i][j]].width;
|
||||
}
|
||||
@ -590,7 +587,7 @@ int action_finalize_shapes(pass_to_bison *bison_args)
|
||||
/*
|
||||
* Compute height of highest shape in design
|
||||
*/
|
||||
for (i=0; i<NUM_SHAPES; ++i) {
|
||||
for (i = 0; i < NUM_SHAPES; ++i) {
|
||||
if (isempty(curdes.shape + i)) {
|
||||
continue;
|
||||
}
|
||||
@ -599,10 +596,9 @@ int action_finalize_shapes(pass_to_bison *bison_args)
|
||||
}
|
||||
}
|
||||
#ifdef PARSER_DEBUG
|
||||
fprintf (stderr, " Parser: 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);
|
||||
fprintf (stderr, " Parser: Maximum shape height: %d\n",
|
||||
(int) curdes.maxshapeheight);
|
||||
fprintf(stderr, " Parser: Maximum shape height: %d\n", (int) curdes.maxshapeheight);
|
||||
#endif
|
||||
return RC_SUCCESS;
|
||||
}
|
||||
@ -631,45 +627,42 @@ int action_start_parsing_design(pass_to_bison *bison_args, char *design_name)
|
||||
if (!design_needed(bison_args)) {
|
||||
bison_args->speeding = 1;
|
||||
#ifdef PARSER_DEBUG
|
||||
fprintf (stderr, " Parser: Skipping to next design (lexer doesn't know!)\n");
|
||||
fprintf(stderr, " Parser: Skipping to next design (lexer doesn't know!)\n");
|
||||
#endif
|
||||
return RC_ERROR; /* trigger the parser's `error` rule, which will skip to the next design */
|
||||
return RC_ERROR; /* trigger the parser's `error` rule, which will skip to the next design */
|
||||
}
|
||||
return RC_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Rule action called when a parent config file specification is encountered.
|
||||
* @param bison_args the parser state
|
||||
* @param filepath the path to the parent config file
|
||||
* @return 0: success;
|
||||
* 1: YYERROR must be invoked
|
||||
*/
|
||||
int action_parent_config(pass_to_bison *bison_args, char *filepath)
|
||||
int action_parent_config(pass_to_bison *bison_args, bxstr_t *filepath)
|
||||
{
|
||||
#ifdef PARSER_DEBUG
|
||||
fprintf (stderr, " Parser: parent config file specified: [%s]\n", filepath);
|
||||
fprintf(stderr, " Parser: parent config file specified: [%s]\n", bxs_to_output(filepath));
|
||||
#endif
|
||||
if (filepath == NULL || filepath[0] == '\0') {
|
||||
if (bxs_is_empty(filepath)) {
|
||||
bison_args->skipping = 1;
|
||||
yyerror(bison_args, "parent reference is empty");
|
||||
return RC_ERROR;
|
||||
}
|
||||
else if (strcasecmp(filepath, ":global:") == 0) { /* special token */
|
||||
else if (strcasecmp(filepath->ascii, ":global:") == 0) { /* special token */
|
||||
filepath = discover_config_file(1);
|
||||
if (filepath == NULL) {
|
||||
bison_args->skipping = 1; /* prevent redundant "skipping to next design" message */
|
||||
bison_args->skipping = 1; /* prevent redundant "skipping to next design" message */
|
||||
yyerror(bison_args, "parent reference to global config which cannot be found");
|
||||
return RC_ERROR;
|
||||
}
|
||||
}
|
||||
else if (!bxs_valid_in_filename(filepath, NULL)) {
|
||||
yyerror(bison_args, "parent reference contains invalid characters: %s", bxs_to_output(filepath));
|
||||
return RC_ERROR;
|
||||
}
|
||||
else {
|
||||
FILE *f = fopen(filepath, "r");
|
||||
FILE *f = bx_fopens(filepath, "r");
|
||||
if (f == NULL) {
|
||||
bison_args->skipping = 1;
|
||||
yyerror(bison_args, "parent config file not found: %s", filepath);
|
||||
yyerror(bison_args, "parent config file not found: %s", bxs_to_output(filepath));
|
||||
return RC_ERROR;
|
||||
}
|
||||
else {
|
||||
@ -677,46 +670,36 @@ int action_parent_config(pass_to_bison *bison_args, char *filepath)
|
||||
}
|
||||
}
|
||||
#ifdef PARSER_DEBUG
|
||||
fprintf (stderr, " Parser: parent config file path resolved: [%s]\n", filepath);
|
||||
fprintf(stderr, " Parser: parent config file path resolved: [%s]\n", bxs_to_output(filepath));
|
||||
#endif
|
||||
|
||||
int is_new = !array_contains(bison_args->parent_configs, bison_args->num_parent_configs, filepath);
|
||||
if (!is_new) {
|
||||
#ifdef PARSER_DEBUG
|
||||
fprintf (stderr, " Parser: duplicate parent / cycle: [%s]\n", filepath);
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
bison_args->parent_configs = realloc(bison_args->parent_configs,
|
||||
(bison_args->num_parent_configs + 1) * sizeof(char *));
|
||||
|
||||
int is_new = !array_contains_bxs(bison_args->parent_configs, bison_args->num_parent_configs, filepath);
|
||||
if (is_new) {
|
||||
bison_args->parent_configs
|
||||
= realloc(bison_args->parent_configs, (bison_args->num_parent_configs + 1) * sizeof(bxstr_t *));
|
||||
bison_args->parent_configs[bison_args->num_parent_configs] = filepath;
|
||||
++(bison_args->num_parent_configs);
|
||||
}
|
||||
else {
|
||||
#ifdef PARSER_DEBUG
|
||||
fprintf(stderr, " Parser: duplicate parent / cycle: [%s]\n", bxs_to_output(filepath));
|
||||
#endif
|
||||
}
|
||||
return RC_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Rule action called when a design has been parsed completely and can be checked and added.
|
||||
* @param bison_args the parser state
|
||||
* @param design_primary_name the primary name of the design as specified at BOX statement
|
||||
* @param name_at_end the primary name of the design as specified at END statement
|
||||
* @return 0: success;
|
||||
* 1: YYERROR must be invoked
|
||||
* 2: YYABORT must be invoked
|
||||
* 3: YYACCEPT must be invoked
|
||||
*/
|
||||
int action_add_design(pass_to_bison *bison_args, char *design_primary_name, char *name_at_end)
|
||||
{
|
||||
design_t *tmp;
|
||||
char *p;
|
||||
|
||||
#ifdef PARSER_DEBUG
|
||||
fprintf (stderr, "--------- ADDING DESIGN \"%s\".\n", design_primary_name);
|
||||
fprintf(stderr, "--------- ADDING DESIGN \"%s\".\n", design_primary_name);
|
||||
#endif
|
||||
|
||||
if (strcasecmp (design_primary_name, name_at_end)) {
|
||||
if (strcasecmp(design_primary_name, name_at_end)) {
|
||||
yyerror(bison_args, "box design name differs at BOX and END");
|
||||
return RC_ERROR;
|
||||
}
|
||||
@ -731,7 +714,7 @@ int action_add_design(pass_to_bison *bison_args, char *design_primary_name, char
|
||||
|
||||
p = design_primary_name;
|
||||
while (*p) {
|
||||
if (*p < 32 || *p > 126) {
|
||||
if (*p < 32 || *p > 126) { // CHECK this check may be unnecessary due to lexer's ASCII_ID
|
||||
yyerror(bison_args, "box design name must consist of printable standard ASCII characters.");
|
||||
return RC_ERROR;
|
||||
}
|
||||
@ -756,7 +739,7 @@ int action_add_design(pass_to_bison *bison_args, char *design_primary_name, char
|
||||
++(bison_args->design_idx);
|
||||
tmp = (design_t *) realloc(bison_args->designs, (bison_args->design_idx + 1) * sizeof(design_t));
|
||||
if (tmp == NULL) {
|
||||
perror (PROJECT);
|
||||
perror(PROJECT);
|
||||
return RC_ABORT;
|
||||
}
|
||||
bison_args->designs = tmp;
|
||||
@ -767,63 +750,77 @@ int action_add_design(pass_to_bison *bison_args, char *design_primary_name, char
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Rule action called when a keyword entry is encountered in a box design.
|
||||
* @param bison_args the parser state
|
||||
* @param keyword the keyword
|
||||
* @param value the associated value
|
||||
* @return 0: success;
|
||||
* 1: YYERROR must be invoked
|
||||
* 2: YYABORT must be invoked
|
||||
*/
|
||||
int action_record_keyword(pass_to_bison *bison_args, char *keyword, char *value)
|
||||
static int is_semantic_version(char *version)
|
||||
{
|
||||
pcre2_code *version_pattern /* CHECK cache compiled pattern to speed up config file parsing */
|
||||
= compile_pattern("^(0|[1-9]\\d*)(?:\\.(0|[1-9]\\d*))?(?:\\.(0|[1-9]\\d*))?(?:[+-][a-zA-Z0-9\\.+-]+)?$");
|
||||
int result = regex_match(version_pattern, version);
|
||||
pcre2_code_free(version_pattern);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int action_record_keyword(pass_to_bison *bison_args, char *keyword, bxstr_t *value)
|
||||
{
|
||||
#ifdef PARSER_DEBUG
|
||||
fprintf (stderr, " Parser: entry rule fulfilled [%s = %s]\n", keyword, value);
|
||||
fprintf(stderr, " Parser: entry rule fulfilled [%s = %s]\n", keyword, bxs_to_output(value));
|
||||
#endif
|
||||
if (strcasecmp (keyword, "author") == 0) {
|
||||
curdes.author = (char *) strdup (value);
|
||||
|
||||
size_t error_pos = 0;
|
||||
if (!bxs_valid_in_kv_string(value, &error_pos)) {
|
||||
yyerror(bison_args, "invalid character in string value at position %d", (int) error_pos);
|
||||
return RC_ERROR;
|
||||
}
|
||||
|
||||
if (strcasecmp(keyword, "author") == 0) {
|
||||
curdes.author = bxs_strdup(value);
|
||||
if (curdes.author == NULL) {
|
||||
perror (PROJECT);
|
||||
perror(PROJECT);
|
||||
return RC_ABORT;
|
||||
}
|
||||
}
|
||||
else if (strcasecmp (keyword, "designer") == 0) {
|
||||
curdes.designer = (char *) strdup (value);
|
||||
else if (strcasecmp(keyword, "designer") == 0) {
|
||||
curdes.designer = bxs_strdup(value);
|
||||
if (curdes.designer == NULL) {
|
||||
perror (PROJECT);
|
||||
perror(PROJECT);
|
||||
return RC_ABORT;
|
||||
}
|
||||
}
|
||||
else if (strcasecmp (keyword, "revision") == 0) {
|
||||
curdes.revision = (char *) strdup (value);
|
||||
if (curdes.revision == NULL) {
|
||||
perror (PROJECT);
|
||||
return RC_ABORT;
|
||||
else if (strcasecmp(keyword, "revision") == 0) {
|
||||
if (is_semantic_version(value->ascii)) {
|
||||
curdes.revision = (char *) strdup(value->ascii);
|
||||
if (curdes.revision == NULL) {
|
||||
perror(PROJECT);
|
||||
return RC_ABORT;
|
||||
}
|
||||
}
|
||||
else {
|
||||
yyerror(bison_args, "revision is not a version number in line %d of %s", __LINE__, __FILE__);
|
||||
return RC_ERROR;
|
||||
}
|
||||
}
|
||||
else if (strcasecmp (keyword, "created") == 0) {
|
||||
curdes.created = (char *) strdup (value);
|
||||
else if (strcasecmp(keyword, "created") == 0) {
|
||||
curdes.created = bxs_strdup(value);
|
||||
if (curdes.created == NULL) {
|
||||
perror (PROJECT);
|
||||
perror(PROJECT);
|
||||
return RC_ABORT;
|
||||
}
|
||||
}
|
||||
else if (strcasecmp (keyword, "revdate") == 0) {
|
||||
curdes.revdate = (char *) strdup (value);
|
||||
else if (strcasecmp(keyword, "revdate") == 0) {
|
||||
curdes.revdate = bxs_strdup(value);
|
||||
if (curdes.revdate == NULL) {
|
||||
perror (PROJECT);
|
||||
perror(PROJECT);
|
||||
return RC_ABORT;
|
||||
}
|
||||
}
|
||||
else if (strcasecmp (keyword, "tags") == 0) {
|
||||
tag_record(bison_args, value); /* discard return code (we print warnings, but tolerate the problem) */
|
||||
else if (strcasecmp(keyword, "tags") == 0) {
|
||||
tag_record(bison_args, value); /* discard return code (we print warnings, but tolerate the problem) */
|
||||
}
|
||||
else if (strcasecmp (keyword, "indent") == 0) {
|
||||
if (strcasecmp (value, "text") == 0 ||
|
||||
strcasecmp (value, "box") == 0 ||
|
||||
strcasecmp (value, "none") == 0) {
|
||||
curdes.indentmode = value[0];
|
||||
else if (strcasecmp(keyword, "indent") == 0) {
|
||||
char *val = value->ascii;
|
||||
if (strcasecmp(val, "text") == 0 || strcasecmp(val, "box") == 0 || strcasecmp(val, "none") == 0) {
|
||||
curdes.indentmode = val[0];
|
||||
}
|
||||
else {
|
||||
yyerror(bison_args, "indent keyword must be followed by \"text\", \"box\", or \"none\"");
|
||||
@ -883,9 +880,9 @@ int action_padding_entry(pass_to_bison *bison_args, char *area, int value)
|
||||
|
||||
int action_init_parser(pass_to_bison *bison_args)
|
||||
{
|
||||
bison_args->designs = (design_t *) calloc (1, sizeof(design_t));
|
||||
bison_args->designs = (design_t *) calloc(1, sizeof(design_t));
|
||||
if (bison_args->designs == NULL) {
|
||||
perror (PROJECT);
|
||||
perror(PROJECT);
|
||||
return RC_ABORT;
|
||||
}
|
||||
bison_args->num_designs = 1;
|
||||
@ -903,7 +900,7 @@ int action_add_alias(pass_to_bison *bison_args, char *alias_name)
|
||||
}
|
||||
if (alias_exists_in_child_configs(bison_args, alias_name)) {
|
||||
#ifdef PARSER_DEBUG
|
||||
fprintf (stderr, " Parser: 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
|
||||
}
|
||||
else {
|
||||
@ -917,22 +914,40 @@ int action_add_alias(pass_to_bison *bison_args, char *alias_name)
|
||||
|
||||
|
||||
|
||||
static char *adjust_eols(char *sample)
|
||||
static bxstr_t *adjust_eols(uint32_t *sample)
|
||||
{
|
||||
if (eol_pattern == NULL) {
|
||||
eol_pattern = compile_pattern("(?(?=\r)(\r\n?)|(\n))");
|
||||
}
|
||||
uint32_t *u32_sample = u32_strconv_from_input(sample);
|
||||
uint32_t *replaced = regex_replace(eol_pattern, opt.eol, u32_sample, strlen(sample), 1);
|
||||
char *result = u32_strconv_to_output(replaced);
|
||||
uint32_t *replaced = regex_replace(eol_pattern, opt.eol, sample, u32_strlen(sample), 1);
|
||||
bxstr_t *result = bxs_from_unicode(replaced);
|
||||
BFREE(replaced);
|
||||
BFREE(u32_sample);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int action_sample_block(pass_to_bison *bison_args, char *sample)
|
||||
static uint32_t *find_first_nonblank_line(bxstr_t *sample)
|
||||
{
|
||||
uint32_t *result = sample->memory;
|
||||
uint32_t *p = sample->memory;
|
||||
while (*p != char_nul) {
|
||||
while (uc_is_blank(*p) || *p == char_cr) {
|
||||
p++;
|
||||
}
|
||||
if (*p == char_newline) {
|
||||
result = ++p;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int action_sample_block(pass_to_bison *bison_args, bxstr_t *sample)
|
||||
{
|
||||
#ifdef PARSER_DEBUG
|
||||
fprintf(stderr, " Parser: SAMPLE block rule satisfied\n");
|
||||
@ -943,20 +958,59 @@ int action_sample_block(pass_to_bison *bison_args, char *sample)
|
||||
return RC_ERROR;
|
||||
}
|
||||
|
||||
char *p = sample;
|
||||
while ((*p == '\r' || *p == '\n') && *p != '\0') {
|
||||
p++;
|
||||
}
|
||||
char *line = adjust_eols(p);
|
||||
uint32_t *p = find_first_nonblank_line(sample);
|
||||
bxstr_t *line = adjust_eols(p);
|
||||
if (line == NULL) {
|
||||
perror(PROJECT);
|
||||
return RC_ABORT;
|
||||
}
|
||||
|
||||
if (bxs_is_empty(line)) {
|
||||
yyerror(bison_args, "SAMPLE block must not be empty");
|
||||
return RC_ERROR;
|
||||
}
|
||||
size_t error_pos = 0;
|
||||
if (!bxs_valid_in_sample(line, &error_pos)) {
|
||||
yyerror(bison_args, "invalid character in SAMPLE block at position %d", (int) error_pos);
|
||||
return RC_ERROR;
|
||||
}
|
||||
|
||||
curdes.sample = line;
|
||||
++(bison_args->num_mandatory);
|
||||
return RC_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/*EOF*/ /* vim: set cindent sw=4: */
|
||||
|
||||
int action_add_regex_rule(pass_to_bison *bison_args, char *type, reprule_t **rule_list, size_t *rule_list_len,
|
||||
bxstr_t *search, bxstr_t *replace, char mode)
|
||||
{
|
||||
size_t n = *rule_list_len;
|
||||
|
||||
UNUSED(type); /* used only in PARSER_DEBUG mode */
|
||||
#ifdef PARSER_DEBUG
|
||||
fprintf(stderr, "Adding %s rule: \"%s\" with \"%s\" (%c)\n",
|
||||
strcmp(type, "rep") == 0 ? "replacement" : "reversion",
|
||||
bxs_to_output(search), bxs_to_output(replace), mode);
|
||||
#endif
|
||||
|
||||
*rule_list = (reprule_t *) realloc(*rule_list, (n + 1) * sizeof(reprule_t));
|
||||
if (*rule_list == NULL) {
|
||||
perror(PROJECT);
|
||||
return RC_ABORT;
|
||||
}
|
||||
memset((*rule_list) + n, 0, sizeof(reprule_t));
|
||||
(*rule_list)[n].search = bxs_strdup(search);
|
||||
(*rule_list)[n].repstr = bxs_strdup(replace);
|
||||
if ((*rule_list)[n].search == NULL || (*rule_list)[n].repstr == NULL) {
|
||||
perror(PROJECT);
|
||||
return RC_ABORT;
|
||||
}
|
||||
(*rule_list)[n].line = yyget_lineno(bison_args->lexer_state);
|
||||
(*rule_list)[n].mode = mode;
|
||||
*rule_list_len = n + 1;
|
||||
return RC_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/* vim: set cindent sw=4: */
|
||||
|
@ -71,7 +71,7 @@ int action_start_parsing_design(pass_to_bison *bison_args, char *design_name);
|
||||
* @return 0: success;
|
||||
* 1: YYERROR must be invoked
|
||||
*/
|
||||
int action_parent_config(pass_to_bison *bison_args, char *filepath);
|
||||
int action_parent_config(pass_to_bison *bison_args, bxstr_t *filepath);
|
||||
|
||||
|
||||
/**
|
||||
@ -80,12 +80,13 @@ int action_parent_config(pass_to_bison *bison_args, char *filepath);
|
||||
* @param design_primary_name the primary name of the design as specified at BOX statement
|
||||
* @param name_at_end the primary name of the design as specified at END statement
|
||||
* @return 0: success;
|
||||
* 1: YYERROR must be invoked
|
||||
* 2: YYABORT must be invoked
|
||||
* 1: YYERROR must be invoked;
|
||||
* 2: YYABORT must be invoked;
|
||||
* 3: YYACCEPT must be invoked
|
||||
*/
|
||||
int action_add_design(pass_to_bison *bison_args, char *design_primary_name, char *name_at_end);
|
||||
|
||||
|
||||
/**
|
||||
* Rule action called when a keyword entry is encountered in a box design.
|
||||
* @param bison_args the parser state
|
||||
@ -95,7 +96,7 @@ int action_add_design(pass_to_bison *bison_args, char *design_primary_name, char
|
||||
* 1: YYERROR must be invoked
|
||||
* 2: YYABORT must be invoked
|
||||
*/
|
||||
int action_record_keyword(pass_to_bison *bison_args, char *keyword, char *value);
|
||||
int action_record_keyword(pass_to_bison *bison_args, char *keyword, bxstr_t *value);
|
||||
|
||||
|
||||
/**
|
||||
@ -118,6 +119,23 @@ int action_padding_entry(pass_to_bison *bison_args, char *area, int value);
|
||||
int action_add_alias(pass_to_bison *bison_args, char *alias_name);
|
||||
|
||||
|
||||
/**
|
||||
* Rule action to add a `replace` or `reverse` rule to the current design definition.
|
||||
* @param bison_args the parser state
|
||||
* @param type the rule type (either `rep` or `rev`)
|
||||
* @param rule_list pointer to the list to add to
|
||||
* @param rule_list_len pointer to the number of elements in `rule_list`
|
||||
* @param search the search pattern of the rule
|
||||
* @param replace the replacement text of the rule
|
||||
* @param mode the replacement mode (`g` or `o` for `global` or `once`)
|
||||
* @return 0: success;
|
||||
* 1: YYERROR must be invoked
|
||||
* 2: YYABORT must be invoked
|
||||
*/
|
||||
int action_add_regex_rule(pass_to_bison *bison_args, char *type, reprule_t **rule_list, size_t *rule_list_len,
|
||||
bxstr_t *search, bxstr_t *replace, char mode);
|
||||
|
||||
|
||||
/**
|
||||
* (shape-elastic check)
|
||||
*/
|
||||
@ -132,12 +150,12 @@ void recover(pass_to_bison *bison_args);
|
||||
|
||||
|
||||
/**
|
||||
* Add tag to list of current design's tag after validity checking.
|
||||
* Add tag to list of current design's tags after validity checking.
|
||||
* @param bison_args the parser state
|
||||
* @param tag a single tag, or a comma-separated list of tags
|
||||
* @return error code, 0 on success, anything else on failure
|
||||
*/
|
||||
int tag_record(pass_to_bison *bison_args, char *tag);
|
||||
int tag_record(pass_to_bison *bison_args, bxstr_t *tag);
|
||||
|
||||
|
||||
/**
|
||||
@ -148,7 +166,7 @@ int tag_record(pass_to_bison *bison_args, char *tag);
|
||||
* 1: YYERROR must be invoked
|
||||
* 2: YYABORT must be invoked
|
||||
*/
|
||||
int action_sample_block(pass_to_bison *bison_args, char *sample);
|
||||
int action_sample_block(pass_to_bison *bison_args, bxstr_t *sample);
|
||||
|
||||
|
||||
#endif
|
||||
|
143
src/parser.y
143
src/parser.y
@ -20,6 +20,7 @@
|
||||
|
||||
#include "config.h"
|
||||
#include "boxes.h"
|
||||
#include "bxstring.h"
|
||||
|
||||
|
||||
/** all the arguments which we pass to the bison parser */
|
||||
@ -40,7 +41,7 @@ typedef struct {
|
||||
size_t num_child_configs;
|
||||
|
||||
/** the path to the config file we are parsing */
|
||||
char *config_file;
|
||||
bxstr_t *config_file;
|
||||
|
||||
int num_mandatory;
|
||||
|
||||
@ -56,7 +57,7 @@ typedef struct {
|
||||
int speeding;
|
||||
|
||||
/** names of config files specified via "parent" */
|
||||
char **parent_configs;
|
||||
bxstr_t **parent_configs;
|
||||
|
||||
/** number of parent config files (size of parent_configs array) */
|
||||
size_t num_parent_configs;
|
||||
@ -72,6 +73,7 @@ typedef struct {
|
||||
#include "config.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unictype.h>
|
||||
|
||||
#include "shape.h"
|
||||
#include "tools.h"
|
||||
@ -79,6 +81,7 @@ typedef struct {
|
||||
#include "parser.h"
|
||||
#include "lex.yy.h"
|
||||
#include "parsecode.h"
|
||||
#include "unicode.h"
|
||||
|
||||
|
||||
/** required for bison-flex bridge */
|
||||
@ -113,7 +116,8 @@ typedef struct {
|
||||
%parse-param {pass_to_bison *bison_args}
|
||||
|
||||
%union {
|
||||
char *s;
|
||||
bxstr_t *s;
|
||||
char *ascii;
|
||||
char c;
|
||||
shape_t shape;
|
||||
sentry_t sentry;
|
||||
@ -122,8 +126,9 @@ typedef struct {
|
||||
|
||||
%token YPARENT YSHAPES YELASTIC YPADDING YSAMPLE YENDSAMPLE YBOX YEND YUNREC
|
||||
%token YREPLACE YREVERSE YTO YWITH YCHGDEL YTAGS
|
||||
%token <s> KEYWORD
|
||||
%token <ascii> KEYWORD
|
||||
%token <s> WORD
|
||||
%token <ascii> ASCII_ID
|
||||
%token <s> STRING
|
||||
%token <s> FILENAME
|
||||
%token <shape> SHAPE
|
||||
@ -166,7 +171,8 @@ config_file
|
||||
BFREE (bison_args->designs);
|
||||
bison_args->num_designs = 0;
|
||||
if (!opt.design_choice_by_user && bison_args->num_parent_configs == 0) {
|
||||
fprintf (stderr, "%s: no valid data in config file -- %s\n", PROJECT, bison_args->config_file);
|
||||
fprintf(stderr, "%s: no valid data in config file -- %s\n", PROJECT,
|
||||
bxs_to_output(bison_args->config_file));
|
||||
YYABORT;
|
||||
}
|
||||
YYACCEPT;
|
||||
@ -209,22 +215,22 @@ design_or_error: design | error
|
||||
}
|
||||
|
||||
|
||||
alias: WORD
|
||||
alias: ASCII_ID
|
||||
{
|
||||
invoke_action(action_add_alias(bison_args, $1));
|
||||
}
|
||||
|
||||
alias_list: alias | alias_list ',' alias;
|
||||
|
||||
design_id: WORD | WORD ',' alias_list;
|
||||
design_id: ASCII_ID | ASCII_ID ',' alias_list;
|
||||
|
||||
design: YBOX design_id
|
||||
{
|
||||
invoke_action(action_start_parsing_design(bison_args, $<s>2));
|
||||
invoke_action(action_start_parsing_design(bison_args, $<ascii>2));
|
||||
}
|
||||
layout YEND WORD
|
||||
layout YEND ASCII_ID
|
||||
{
|
||||
invoke_action(action_add_design(bison_args, $<s>2, $6));
|
||||
invoke_action(action_add_design(bison_args, $<ascii>2, $6));
|
||||
}
|
||||
;
|
||||
|
||||
@ -247,18 +253,17 @@ entry: KEYWORD STRING
|
||||
|
||||
| YPARENT FILENAME
|
||||
{
|
||||
char *filename = $2;
|
||||
if (filename[0] != filename[strlen(filename) - 1]
|
||||
|| (filename[0] >= 'a' && filename[0] <= 'z')
|
||||
|| (filename[0] >= 'A' && filename[0] <= 'Z')
|
||||
|| (filename[0] >= '0' && filename[0] <= '9'))
|
||||
{
|
||||
yyerror(bison_args, "string expected", filename);
|
||||
/*
|
||||
* Called when PARENT appears as a key inside a box design. That's a user mistake, but not an error.
|
||||
*/
|
||||
bxstr_t *filename = $2;
|
||||
if (filename->memory[0] != filename->memory[filename->num_chars - 1] || uc_is_alnum(filename->memory[0])) {
|
||||
yyerror(bison_args, "string expected");
|
||||
YYERROR;
|
||||
}
|
||||
else {
|
||||
#ifdef PARSER_DEBUG
|
||||
fprintf (stderr, " Parser: Discarding entry [%s = %s].\n", "parent", filename);
|
||||
fprintf (stderr, " Parser: Discarding entry [%s = %s].\n", "parent", bxs_to_output(filename));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@ -270,10 +275,10 @@ entry: KEYWORD STRING
|
||||
|
||||
| YTAGS '(' tag_list ')' | YTAGS tag_entry
|
||||
|
||||
| WORD STRING
|
||||
| WORD STRING | ASCII_ID STRING
|
||||
{
|
||||
#ifdef PARSER_DEBUG
|
||||
fprintf (stderr, " Parser: Discarding entry [%s = %s].\n", $1, $2);
|
||||
fprintf (stderr, " Parser: Discarding entry [%s = %s].\n", $1, bxs_to_output($2));
|
||||
#endif
|
||||
}
|
||||
;
|
||||
@ -301,53 +306,12 @@ block: YSAMPLE STRING YENDSAMPLE
|
||||
|
||||
| YREPLACE rflag STRING YWITH STRING
|
||||
{
|
||||
int a = curdes.anz_reprules;
|
||||
|
||||
#ifdef PARSER_DEBUG
|
||||
fprintf (stderr, "Adding replacement rule: \"%s\" with \"%s\" (%c)\n",
|
||||
$3, $5, $2);
|
||||
#endif
|
||||
|
||||
curdes.reprules = (reprule_t *) realloc (curdes.reprules, (a+1) * sizeof(reprule_t));
|
||||
if (curdes.reprules == NULL) {
|
||||
perror (PROJECT);
|
||||
YYABORT;
|
||||
}
|
||||
memset (&(curdes.reprules[a]), 0, sizeof(reprule_t));
|
||||
curdes.reprules[a].search = (char *) strdup ($3);
|
||||
curdes.reprules[a].repstr = (char *) strdup ($5);
|
||||
if (curdes.reprules[a].search == NULL || curdes.reprules[a].repstr == NULL) {
|
||||
perror (PROJECT);
|
||||
YYABORT;
|
||||
}
|
||||
curdes.reprules[a].line = yyget_lineno(scanner);
|
||||
curdes.reprules[a].mode = $2;
|
||||
curdes.anz_reprules = a + 1;
|
||||
invoke_action(action_add_regex_rule(bison_args, "rep", &curdes.reprules, &curdes.anz_reprules, $3, $5, $2));
|
||||
}
|
||||
|
||||
| YREVERSE rflag STRING YTO STRING
|
||||
{
|
||||
int a = curdes.anz_revrules;
|
||||
|
||||
#ifdef PARSER_DEBUG
|
||||
fprintf (stderr, "Adding reversion rule: \"%s\" to \"%s\" (%c)\n", $3, $5, $2);
|
||||
#endif
|
||||
|
||||
curdes.revrules = (reprule_t *) realloc (curdes.revrules, (a+1) * sizeof(reprule_t));
|
||||
if (curdes.revrules == NULL) {
|
||||
perror (PROJECT);
|
||||
YYABORT;
|
||||
}
|
||||
memset (&(curdes.revrules[a]), 0, sizeof(reprule_t));
|
||||
curdes.revrules[a].search = (char *) strdup ($3);
|
||||
curdes.revrules[a].repstr = (char *) strdup ($5);
|
||||
if (curdes.revrules[a].search == NULL || curdes.revrules[a].repstr == NULL) {
|
||||
perror (PROJECT);
|
||||
YYABORT;
|
||||
}
|
||||
curdes.revrules[a].line = yyget_lineno(scanner);
|
||||
curdes.revrules[a].mode = $2;
|
||||
curdes.anz_revrules = a + 1;
|
||||
invoke_action(action_add_regex_rule(bison_args, "rev", &curdes.revrules, &curdes.anz_revrules, $3, $5, $2));
|
||||
}
|
||||
|
||||
| YPADDING '{' wlist '}'
|
||||
@ -426,9 +390,8 @@ shape_def: '(' shape_lines ')'
|
||||
|
||||
shape_lines: shape_lines ',' STRING
|
||||
{
|
||||
sentry_t rval = $1;
|
||||
size_t slen = strlen ($3);
|
||||
char **tmp;
|
||||
sentry_t rval = $1; // TODO move this to parsecode.c
|
||||
size_t slen = $3->num_columns;
|
||||
|
||||
#ifdef PARSER_DEBUG
|
||||
fprintf (stderr, "Extending a shape entry\n");
|
||||
@ -439,18 +402,38 @@ shape_lines: shape_lines ',' STRING
|
||||
YYERROR;
|
||||
}
|
||||
|
||||
size_t error_pos = 0;
|
||||
if (!bxs_valid_in_shape($3, &error_pos)) {
|
||||
yyerror(bison_args, "invalid character in shape line at position %d", (int) error_pos);
|
||||
YYERROR;
|
||||
}
|
||||
|
||||
rval.height++;
|
||||
tmp = (char **) realloc (rval.chars, rval.height*sizeof(char*));
|
||||
|
||||
char **tmp = (char **) realloc(rval.chars, rval.height * sizeof(char *));
|
||||
if (tmp == NULL) {
|
||||
perror (PROJECT": shape_lines11");
|
||||
YYABORT;
|
||||
}
|
||||
rval.chars = tmp;
|
||||
rval.chars[rval.height-1] = (char *) strdup ($3);
|
||||
rval.chars[rval.height - 1] = (char *) strdup ($3->ascii);
|
||||
if (rval.chars[rval.height-1] == NULL) {
|
||||
perror (PROJECT": shape_lines12");
|
||||
YYABORT;
|
||||
}
|
||||
|
||||
bxstr_t **mtmp = (bxstr_t **) realloc(rval.mbcs, rval.height * sizeof(bxstr_t *));
|
||||
if (mtmp == NULL) {
|
||||
perror (PROJECT": shape_lines13");
|
||||
YYABORT;
|
||||
}
|
||||
rval.mbcs = mtmp;
|
||||
rval.mbcs[rval.height - 1] = bxs_strdup($3);
|
||||
if (rval.mbcs[rval.height - 1] == NULL) {
|
||||
perror (PROJECT": shape_lines14");
|
||||
YYABORT;
|
||||
}
|
||||
|
||||
$$ = rval;
|
||||
}
|
||||
|
||||
@ -462,18 +445,36 @@ shape_lines: shape_lines ',' STRING
|
||||
fprintf (stderr, "Initializing a shape entry with first line\n");
|
||||
#endif
|
||||
|
||||
rval.width = strlen ($1);
|
||||
size_t error_pos = 0;
|
||||
if (!bxs_valid_in_shape($1, &error_pos)) {
|
||||
yyerror(bison_args, "invalid character in shape line at position %d", (int) error_pos);
|
||||
YYERROR;
|
||||
}
|
||||
|
||||
rval.width = $1->num_columns;
|
||||
rval.height = 1;
|
||||
|
||||
rval.chars = (char **) malloc (sizeof(char*));
|
||||
if (rval.chars == NULL) {
|
||||
perror (PROJECT": shape_lines21");
|
||||
YYABORT;
|
||||
}
|
||||
rval.chars[0] = (char *) strdup ($1);
|
||||
rval.chars[0] = (char *) strdup ($1->ascii);
|
||||
if (rval.chars[0] == NULL) {
|
||||
perror (PROJECT": shape_lines22");
|
||||
YYABORT;
|
||||
}
|
||||
|
||||
rval.mbcs = (bxstr_t **) malloc(sizeof(bxstr_t *));
|
||||
if (rval.mbcs == NULL) {
|
||||
perror (PROJECT": shape_lines23");
|
||||
YYABORT;
|
||||
}
|
||||
rval.mbcs[0] = bxs_strdup($1);
|
||||
if (rval.mbcs[0] == NULL) {
|
||||
perror (PROJECT": shape_lines24");
|
||||
YYABORT;
|
||||
}
|
||||
$$ = rval;
|
||||
}
|
||||
;
|
||||
@ -481,7 +482,7 @@ shape_lines: shape_lines ',' STRING
|
||||
|
||||
wlist: wlist wlist_entry | wlist_entry;
|
||||
|
||||
wlist_entry: WORD YNUMBER
|
||||
wlist_entry: ASCII_ID YNUMBER
|
||||
{
|
||||
invoke_action(action_padding_entry(bison_args, $1, $2));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user