mirror of
https://github.com/ascii-boxes/boxes.git
synced 2025-06-20 09:48:12 +02: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
|
/test/*.txt text eol=lf
|
||||||
|
|
||||||
# some files which are currently in ISO-8859-15 encoding
|
# 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
|
/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 "config.h"
|
||||||
|
#include "bxstring.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int yyerrcnt;
|
int yyerrcnt;
|
||||||
@ -33,8 +33,8 @@ typedef struct {
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Valid characters to be used as string delimiters. Note that the
|
* Valid characters to be used as string delimiters.
|
||||||
* following list must correspond to the SDELIM definition below.
|
* The following list must correspond to the SDELIM definition below.
|
||||||
*/
|
*/
|
||||||
#define LEX_SDELIM "\"~'`!@%&*=:;<>?/|.\\"
|
#define LEX_SDELIM "\"~'`!@%&*=:;<>?/|.\\"
|
||||||
#define LEX_SDELIM_RECOMMENDED "\"~'!|"
|
#define LEX_SDELIM_RECOMMENDED "\"~'!|"
|
||||||
@ -50,7 +50,7 @@ typedef struct {
|
|||||||
* @param yyscanner pointer to the scanner data block
|
* @param yyscanner pointer to the scanner data block
|
||||||
* @param configfile the path to the config file we are reading
|
* @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 <string.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <unitypes.h>
|
||||||
|
|
||||||
#include "boxes.h"
|
#include "boxes.h"
|
||||||
#include "shape.h"
|
#include "shape.h"
|
||||||
#include "tools.h"
|
#include "tools.h"
|
||||||
#include "parsing.h"
|
#include "parsing.h"
|
||||||
#include "parser.h"
|
#include "parser.h"
|
||||||
|
#include "unicode.h"
|
||||||
|
|
||||||
|
|
||||||
#define LEX_MAX_WARN 3 /* number of lex errors per design */
|
#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 DELIMSPEC
|
||||||
%x PARENT
|
%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]
|
PWHITE [ \t\r\n]
|
||||||
SDELIM [\"~\'`!@\%\&\*=:;<>\?/|\.\\]
|
SDELIM [\"~\'`!@\%\&\*=:;<>\?/|\.\\]
|
||||||
PPARENT parent
|
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]+ {
|
<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 = bxs_from_ascii("IGNORED");
|
||||||
|
char *str = (char *) strdup(yytext);
|
||||||
BEGIN(BOX);
|
BEGIN(BOX);
|
||||||
report_state("YDELIMS", yytext, "INITIAL");
|
report_state("YDELIMS", str, "INITIAL");
|
||||||
if (change_string_delimiters(yyextra, yylval->s) != 0) {
|
if (change_string_delimiters(yyextra, str) != 0) {
|
||||||
return YUNREC;
|
return YUNREC;
|
||||||
}
|
}
|
||||||
return YDELIMSPEC;
|
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
|
* 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.
|
* 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 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 */
|
||||||
|
|
||||||
@ -145,27 +162,33 @@ PFILENAME [^\r\n]+
|
|||||||
REJECT; /* that was not our delimiter */
|
REJECT; /* that was not our delimiter */
|
||||||
}
|
}
|
||||||
|
|
||||||
yylval->s = (char *) strdup (yytext + 1);
|
char *str = (char *) strdup(yytext + 1);
|
||||||
if (yylval->s == NULL) {
|
if (str == NULL) {
|
||||||
perror (PROJECT);
|
perror (PROJECT);
|
||||||
exit (EXIT_FAILURE);
|
exit (EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
p = yylval->s;
|
char *p = str;
|
||||||
|
|
||||||
while (*p) {
|
while (*p) {
|
||||||
if (*p == yyextra->sesc) {
|
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) {
|
else if (*p == yyextra->sdel) {
|
||||||
*p = '\0';
|
*p = '\0';
|
||||||
yyless ((p-yylval->s)+2+qcnt); /* string plus quotes */
|
yyless ((p - str) + 2 + qcnt); /* string plus quotes */
|
||||||
#ifdef LEXER_DEBUG
|
#ifdef LEXER_DEBUG
|
||||||
fprintf (stderr, " STRING: \"%s\"\n", yylval->s);
|
fprintf (stderr, " STRING: \"%s\"\n", str);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
uint32_t *utf8 = u32_strconv_from_arg(str, CONFIG_FILE_ENCODING);
|
||||||
|
yylval->s = bxs_from_unicode(utf8);
|
||||||
|
BFREE(utf8);
|
||||||
|
BFREE(str);
|
||||||
return STRING;
|
return STRING;
|
||||||
}
|
}
|
||||||
--rest_len;
|
--rest_len;
|
||||||
@ -174,6 +197,7 @@ PFILENAME [^\r\n]+
|
|||||||
if ((yyextra->yyerrcnt)++ < 5) {
|
if ((yyextra->yyerrcnt)++ < 5) {
|
||||||
yyerror(NULL, "Unterminated String -- %s", yytext);
|
yyerror(NULL, "Unterminated String -- %s", yytext);
|
||||||
}
|
}
|
||||||
|
BFREE(str);
|
||||||
return YUNREC;
|
return YUNREC;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,17 +209,15 @@ PFILENAME [^\r\n]+
|
|||||||
}
|
}
|
||||||
|
|
||||||
<PARENT>{PFILENAME} {
|
<PARENT>{PFILENAME} {
|
||||||
char *p = yytext;
|
uint32_t *utf8 = u32_strconv_from_arg(yytext, CONFIG_FILE_ENCODING);
|
||||||
while (*p == ' ' || *p == '\t') {
|
bxstr_t *bxstr = bxs_from_unicode(utf8);
|
||||||
++p;
|
yylval->s = bxs_trim(bxstr);
|
||||||
}
|
|
||||||
yylval->s = (char *) strdup (p);
|
BFREE(utf8);
|
||||||
p = yylval->s + strlen(yylval->s) - 1;
|
bxs_free(bxstr);
|
||||||
while ((*p == ' ' || *p == '\t') && p >= yylval->s) {
|
|
||||||
*p-- = '\0';
|
|
||||||
}
|
|
||||||
BEGIN(INITIAL);
|
BEGIN(INITIAL);
|
||||||
report_state("FILENAM", yylval->s, "INITIAL");
|
report_state("FILENAM", bxs_to_output(yylval->s), "INITIAL");
|
||||||
return FILENAME;
|
return FILENAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -225,27 +247,37 @@ PFILENAME [^\r\n]+
|
|||||||
--p; /* skip trailing whitespace */
|
--p; /* skip trailing whitespace */
|
||||||
p -= 2; /* almost skip "ends" statement */
|
p -= 2; /* almost skip "ends" statement */
|
||||||
*p = '\0'; /* p now points to 'n' */
|
*p = '\0'; /* p now points to 'n' */
|
||||||
yylval->s = (char *) strdup (yytext);
|
char *sample = (char *) strdup(yytext);
|
||||||
if (yylval->s == NULL) {
|
if (sample == NULL) {
|
||||||
perror (PROJECT);
|
perror (PROJECT);
|
||||||
exit (EXIT_FAILURE);
|
exit (EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
*p-- = 'n';
|
*p-- = 'n';
|
||||||
|
|
||||||
len = p - yytext; /* yyless(n): push back all but the first 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 */
|
sample[len] = '\n'; /* replace 'e' with newline */
|
||||||
btrim (yylval->s, &len);
|
btrim(sample, &len);
|
||||||
if (len > 0) {
|
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;
|
return STRING;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if ((yyextra->yyerrcnt)++ < 5) {
|
if ((yyextra->yyerrcnt)++ < 5) {
|
||||||
yyerror(NULL, "SAMPLE block must not be empty");
|
yyerror(NULL, "SAMPLE block must not be empty");
|
||||||
}
|
}
|
||||||
BFREE (yylval->s);
|
BFREE(sample);
|
||||||
return YUNREC;
|
return YUNREC;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -339,8 +371,8 @@ PFILENAME [^\r\n]+
|
|||||||
#ifdef LEXER_DEBUG
|
#ifdef LEXER_DEBUG
|
||||||
fprintf (stderr, "KEYWORD: %s\n", yytext);
|
fprintf (stderr, "KEYWORD: %s\n", yytext);
|
||||||
#endif
|
#endif
|
||||||
yylval->s = (char *) strdup (yytext);
|
yylval->ascii = strdup(yytext);
|
||||||
if (yylval->s == NULL) {
|
if (yylval->ascii == NULL) {
|
||||||
perror (PROJECT);
|
perror (PROJECT);
|
||||||
exit (EXIT_FAILURE);
|
exit (EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
@ -357,19 +389,35 @@ PFILENAME [^\r\n]+
|
|||||||
return YCHGDEL;
|
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} {
|
<INITIAL,BOX>{PWORD} {
|
||||||
/*
|
/*
|
||||||
* a free-floating word which is not a string, i.e. it does not have delimiting characters
|
* a free-floating word which is not a string, i.e. it does not have delimiting characters
|
||||||
*/
|
*/
|
||||||
#ifdef LEXER_DEBUG
|
uint32_t *utf8 = u32_strconv_from_arg(yytext, CONFIG_FILE_ENCODING);
|
||||||
fprintf (stderr, " WORD: %s\n", yytext);
|
yylval->s = bxs_from_unicode(utf8);
|
||||||
#endif
|
|
||||||
yylval->s = (char *) strdup (yytext);
|
|
||||||
if (yylval->s == NULL) {
|
if (yylval->s == NULL) {
|
||||||
perror (PROJECT);
|
perror (PROJECT);
|
||||||
exit (EXIT_FAILURE);
|
exit (EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
#ifdef LEXER_DEBUG
|
||||||
|
fprintf (stderr, " WORD: %s\n", u32_strconv_to_output(utf8));
|
||||||
|
#endif
|
||||||
|
BFREE(utf8);
|
||||||
return WORD;
|
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;
|
struct stat sinf;
|
||||||
|
|
||||||
if (stat(configfile, &sinf)) {
|
char *utf8 = u32_strconv_to_arg(configfile->memory, "UTF-8");
|
||||||
|
if (stat(utf8, &sinf)) {
|
||||||
perror (PROJECT);
|
perror (PROJECT);
|
||||||
|
BFREE(utf8);
|
||||||
exit (EXIT_FAILURE);
|
exit (EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
BFREE(utf8);
|
||||||
struct yyguts_t *yyg = (struct yyguts_t *) yyscanner;
|
struct yyguts_t *yyg = (struct yyguts_t *) yyscanner;
|
||||||
yy_delete_buffer(YY_CURRENT_BUFFER, yyscanner);
|
yy_delete_buffer(YY_CURRENT_BUFFER, yyscanner);
|
||||||
yy_switch_to_buffer (yy_create_buffer(yyin, sinf.st_size+10, yyscanner), yyscanner);
|
yy_switch_to_buffer (yy_create_buffer(yyin, sinf.st_size+10, yyscanner), yyscanner);
|
||||||
|
284
src/parsecode.c
284
src/parsecode.c
@ -18,21 +18,25 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <strings.h>
|
#include <strings.h>
|
||||||
|
#include <unictype.h>
|
||||||
|
#include <unistr.h>
|
||||||
|
|
||||||
#include "discovery.h"
|
#include "discovery.h"
|
||||||
#include "tools.h"
|
|
||||||
#include "regulex.h"
|
|
||||||
#include "unicode.h"
|
|
||||||
#include "query.h"
|
|
||||||
#include "parsecode.h"
|
#include "parsecode.h"
|
||||||
#include "parsing.h"
|
#include "parsing.h"
|
||||||
#include "parser.h"
|
#include "query.h"
|
||||||
#include "lex.yy.h"
|
#include "regulex.h"
|
||||||
|
#include "tools.h"
|
||||||
|
#include "unicode.h"
|
||||||
|
|
||||||
|
#include "parser.h"
|
||||||
|
|
||||||
|
#include "lex.yy.h"
|
||||||
|
|
||||||
|
|
||||||
static pcre2_code *eol_pattern = NULL;
|
static pcre2_code *eol_pattern = NULL;
|
||||||
@ -196,7 +200,8 @@ static int adjoining_elastics(pass_to_bison *bison_args)
|
|||||||
if (curdes.shape[sides[i][j]].elastic) {
|
if (curdes.shape[sides[i][j]].elastic) {
|
||||||
if (ef) {
|
if (ef) {
|
||||||
return 1; /* error detected */
|
return 1; /* error detected */
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
ef = 1;
|
ef = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -217,8 +222,7 @@ int perform_se_check(pass_to_bison *bison_args)
|
|||||||
|
|
||||||
s_rc = non_existent_elastics(bison_args);
|
s_rc = non_existent_elastics(bison_args);
|
||||||
if (s_rc != NUM_SHAPES) {
|
if (s_rc != NUM_SHAPES) {
|
||||||
yyerror(bison_args, "Shape %s has been specified as elastic but doesn't exist",
|
yyerror(bison_args, "Shape %s has been specified as elastic but doesn't exist", shape_name[s_rc]);
|
||||||
shape_name[s_rc]);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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;
|
int rc = RC_SUCCESS;
|
||||||
if (tag_is_valid(tag)) {
|
if (is_ascii_id(tag, 1)) {
|
||||||
if (!array_contains0(curdes.tags, tag)) {
|
if (!array_contains0(curdes.tags, tag->ascii)) {
|
||||||
size_t num_tags = array_count0(curdes.tags);
|
size_t num_tags = array_count0(curdes.tags);
|
||||||
curdes.tags = (char **) realloc(curdes.tags, (num_tags + 2) * sizeof(char *));
|
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;
|
curdes.tags[num_tags + 1] = NULL;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
yyerror(bison_args, "duplicate tag -- %s", tag);
|
yyerror(bison_args, "duplicate tag -- %s", bxs_to_output(tag));
|
||||||
rc = RC_ERROR;
|
rc = RC_ERROR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
yyerror(bison_args, "invalid tag -- %s", tag);
|
yyerror(bison_args, "invalid tag -- %s", bxs_to_output(tag));
|
||||||
rc = RC_ERROR;
|
rc = RC_ERROR;
|
||||||
}
|
}
|
||||||
return rc;
|
return rc;
|
||||||
@ -394,51 +398,41 @@ static int tag_add(pass_to_bison *bison_args, char *tag)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
static char *tag_next_comma(char *s)
|
static int tag_split_add(pass_to_bison *bison_args, bxstr_t *tag)
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
int rc = RC_SUCCESS;
|
int rc = RC_SUCCESS;
|
||||||
char *s = tag;
|
uint32_t *c = NULL;
|
||||||
char *c = NULL;
|
int vis_start = 0;
|
||||||
|
int cursor = -1;
|
||||||
do {
|
do {
|
||||||
c = tag_next_comma(s);
|
c = bxs_strchr(tag, to_utf32(','), &cursor);
|
||||||
char *single_tag = trimdup(s, c - 1);
|
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);
|
int rc_add = tag_add(bison_args, single_tag);
|
||||||
|
bxs_free(single_tag);
|
||||||
if (rc_add != 0) {
|
if (rc_add != 0) {
|
||||||
rc = rc_add;
|
rc = rc_add;
|
||||||
}
|
}
|
||||||
s = c + 1;
|
vis_start = cursor + 1;
|
||||||
} while (*c != '\0');
|
} while (c != NULL);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
int tag_record(pass_to_bison *bison_args, bxstr_t *tag)
|
||||||
* 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 rc = RC_SUCCESS;
|
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);
|
rc = tag_split_add(bison_args, tag);
|
||||||
}
|
}
|
||||||
else {
|
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);
|
rc = tag_add(bison_args, trimmed);
|
||||||
|
bxs_free(trimmed);
|
||||||
}
|
}
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
@ -495,7 +489,8 @@ int action_finalize_shapes(pass_to_bison *bison_args)
|
|||||||
if (side == BLEF || side == BRIG) {
|
if (side == BLEF || side == BRIG) {
|
||||||
if (nshape == SHAPES_PER_SIDE) {
|
if (nshape == SHAPES_PER_SIDE) {
|
||||||
c->height = 1;
|
c->height = 1;
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
c->height = c[nshape].height;
|
c->height = c[nshape].height;
|
||||||
}
|
}
|
||||||
c->width = curdes.shape[fshape].width;
|
c->width = curdes.shape[fshape].width;
|
||||||
@ -503,13 +498,14 @@ int action_finalize_shapes(pass_to_bison *bison_args)
|
|||||||
else {
|
else {
|
||||||
if (nshape == SHAPES_PER_SIDE) {
|
if (nshape == SHAPES_PER_SIDE) {
|
||||||
c->width = 1;
|
c->width = 1;
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
c->width = c[nshape].width;
|
c->width = c[nshape].width;
|
||||||
}
|
}
|
||||||
c->height = curdes.shape[fshape].height;
|
c->height = curdes.shape[fshape].height;
|
||||||
}
|
}
|
||||||
c->elastic = 0;
|
c->elastic = 0;
|
||||||
rc = genshape (c->width, c->height, &(c->chars));
|
rc = genshape(c->width, c->height, &(c->chars), &(c->mbcs));
|
||||||
if (rc) {
|
if (rc) {
|
||||||
return RC_ABORT;
|
return RC_ABORT;
|
||||||
}
|
}
|
||||||
@ -527,7 +523,8 @@ int action_finalize_shapes(pass_to_bison *bison_args)
|
|||||||
for (i = 1; i < SHAPES_PER_SIDE - 1; ++i) {
|
for (i = 1; i < SHAPES_PER_SIDE - 1; ++i) {
|
||||||
if (isempty(curdes.shape + sides[side][i])) {
|
if (isempty(curdes.shape + sides[side][i])) {
|
||||||
continue;
|
continue;
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
found = 1;
|
found = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -542,7 +539,7 @@ int action_finalize_shapes(pass_to_bison *bison_args)
|
|||||||
c->height = curdes.shape[sides[side][0]].height;
|
c->height = curdes.shape[sides[side][0]].height;
|
||||||
}
|
}
|
||||||
c->elastic = 1;
|
c->elastic = 1;
|
||||||
rc = genshape (c->width, c->height, &(c->chars));
|
rc = genshape(c->width, c->height, &(c->chars), &(c->mbcs));
|
||||||
if (rc) {
|
if (rc) {
|
||||||
return RC_ABORT;
|
return RC_ABORT;
|
||||||
}
|
}
|
||||||
@ -601,8 +598,7 @@ int action_finalize_shapes(pass_to_bison *bison_args)
|
|||||||
#ifdef PARSER_DEBUG
|
#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);
|
(int) curdes.minwidth, (int) curdes.minheight);
|
||||||
fprintf (stderr, " Parser: 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;
|
||||||
}
|
}
|
||||||
@ -640,24 +636,17 @@ int action_start_parsing_design(pass_to_bison *bison_args, char *design_name)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
int action_parent_config(pass_to_bison *bison_args, bxstr_t *filepath)
|
||||||
* 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)
|
|
||||||
{
|
{
|
||||||
#ifdef PARSER_DEBUG
|
#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
|
#endif
|
||||||
if (filepath == NULL || filepath[0] == '\0') {
|
if (bxs_is_empty(filepath)) {
|
||||||
bison_args->skipping = 1;
|
bison_args->skipping = 1;
|
||||||
yyerror(bison_args, "parent reference is empty");
|
yyerror(bison_args, "parent reference is empty");
|
||||||
return RC_ERROR;
|
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);
|
filepath = discover_config_file(1);
|
||||||
if (filepath == NULL) {
|
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 */
|
||||||
@ -665,11 +654,15 @@ int action_parent_config(pass_to_bison *bison_args, char *filepath)
|
|||||||
return RC_ERROR;
|
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 {
|
else {
|
||||||
FILE *f = fopen(filepath, "r");
|
FILE *f = bx_fopens(filepath, "r");
|
||||||
if (f == NULL) {
|
if (f == NULL) {
|
||||||
bison_args->skipping = 1;
|
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;
|
return RC_ERROR;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -677,36 +670,26 @@ int action_parent_config(pass_to_bison *bison_args, char *filepath)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#ifdef PARSER_DEBUG
|
#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
|
#endif
|
||||||
|
|
||||||
int is_new = !array_contains(bison_args->parent_configs, bison_args->num_parent_configs, filepath);
|
int is_new = !array_contains_bxs(bison_args->parent_configs, bison_args->num_parent_configs, filepath);
|
||||||
if (!is_new) {
|
if (is_new) {
|
||||||
#ifdef PARSER_DEBUG
|
bison_args->parent_configs
|
||||||
fprintf (stderr, " Parser: duplicate parent / cycle: [%s]\n", filepath);
|
= realloc(bison_args->parent_configs, (bison_args->num_parent_configs + 1) * sizeof(bxstr_t *));
|
||||||
#endif
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
bison_args->parent_configs = realloc(bison_args->parent_configs,
|
|
||||||
(bison_args->num_parent_configs + 1) * sizeof(char *));
|
|
||||||
bison_args->parent_configs[bison_args->num_parent_configs] = filepath;
|
bison_args->parent_configs[bison_args->num_parent_configs] = filepath;
|
||||||
++(bison_args->num_parent_configs);
|
++(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;
|
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)
|
int action_add_design(pass_to_bison *bison_args, char *design_primary_name, char *name_at_end)
|
||||||
{
|
{
|
||||||
design_t *tmp;
|
design_t *tmp;
|
||||||
@ -731,7 +714,7 @@ int action_add_design(pass_to_bison *bison_args, char *design_primary_name, char
|
|||||||
|
|
||||||
p = design_primary_name;
|
p = design_primary_name;
|
||||||
while (*p) {
|
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.");
|
yyerror(bison_args, "box design name must consist of printable standard ASCII characters.");
|
||||||
return RC_ERROR;
|
return RC_ERROR;
|
||||||
}
|
}
|
||||||
@ -767,50 +750,65 @@ int action_add_design(pass_to_bison *bison_args, char *design_primary_name, char
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
static int is_semantic_version(char *version)
|
||||||
* Rule action called when a keyword entry is encountered in a box design.
|
{
|
||||||
* @param bison_args the parser state
|
pcre2_code *version_pattern /* CHECK cache compiled pattern to speed up config file parsing */
|
||||||
* @param keyword the keyword
|
= compile_pattern("^(0|[1-9]\\d*)(?:\\.(0|[1-9]\\d*))?(?:\\.(0|[1-9]\\d*))?(?:[+-][a-zA-Z0-9\\.+-]+)?$");
|
||||||
* @param value the associated value
|
int result = regex_match(version_pattern, version);
|
||||||
* @return 0: success;
|
pcre2_code_free(version_pattern);
|
||||||
* 1: YYERROR must be invoked
|
return result;
|
||||||
* 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)
|
||||||
{
|
{
|
||||||
#ifdef PARSER_DEBUG
|
#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
|
#endif
|
||||||
|
|
||||||
|
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) {
|
if (strcasecmp(keyword, "author") == 0) {
|
||||||
curdes.author = (char *) strdup (value);
|
curdes.author = bxs_strdup(value);
|
||||||
if (curdes.author == NULL) {
|
if (curdes.author == NULL) {
|
||||||
perror(PROJECT);
|
perror(PROJECT);
|
||||||
return RC_ABORT;
|
return RC_ABORT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (strcasecmp(keyword, "designer") == 0) {
|
else if (strcasecmp(keyword, "designer") == 0) {
|
||||||
curdes.designer = (char *) strdup (value);
|
curdes.designer = bxs_strdup(value);
|
||||||
if (curdes.designer == NULL) {
|
if (curdes.designer == NULL) {
|
||||||
perror(PROJECT);
|
perror(PROJECT);
|
||||||
return RC_ABORT;
|
return RC_ABORT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (strcasecmp(keyword, "revision") == 0) {
|
else if (strcasecmp(keyword, "revision") == 0) {
|
||||||
curdes.revision = (char *) strdup (value);
|
if (is_semantic_version(value->ascii)) {
|
||||||
|
curdes.revision = (char *) strdup(value->ascii);
|
||||||
if (curdes.revision == NULL) {
|
if (curdes.revision == NULL) {
|
||||||
perror(PROJECT);
|
perror(PROJECT);
|
||||||
return RC_ABORT;
|
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) {
|
else if (strcasecmp(keyword, "created") == 0) {
|
||||||
curdes.created = (char *) strdup (value);
|
curdes.created = bxs_strdup(value);
|
||||||
if (curdes.created == NULL) {
|
if (curdes.created == NULL) {
|
||||||
perror(PROJECT);
|
perror(PROJECT);
|
||||||
return RC_ABORT;
|
return RC_ABORT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (strcasecmp(keyword, "revdate") == 0) {
|
else if (strcasecmp(keyword, "revdate") == 0) {
|
||||||
curdes.revdate = (char *) strdup (value);
|
curdes.revdate = bxs_strdup(value);
|
||||||
if (curdes.revdate == NULL) {
|
if (curdes.revdate == NULL) {
|
||||||
perror(PROJECT);
|
perror(PROJECT);
|
||||||
return RC_ABORT;
|
return RC_ABORT;
|
||||||
@ -820,10 +818,9 @@ int action_record_keyword(pass_to_bison *bison_args, char *keyword, char *value)
|
|||||||
tag_record(bison_args, value); /* discard return code (we print warnings, but tolerate the problem) */
|
tag_record(bison_args, value); /* discard return code (we print warnings, but tolerate the problem) */
|
||||||
}
|
}
|
||||||
else if (strcasecmp(keyword, "indent") == 0) {
|
else if (strcasecmp(keyword, "indent") == 0) {
|
||||||
if (strcasecmp (value, "text") == 0 ||
|
char *val = value->ascii;
|
||||||
strcasecmp (value, "box") == 0 ||
|
if (strcasecmp(val, "text") == 0 || strcasecmp(val, "box") == 0 || strcasecmp(val, "none") == 0) {
|
||||||
strcasecmp (value, "none") == 0) {
|
curdes.indentmode = val[0];
|
||||||
curdes.indentmode = value[0];
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
yyerror(bison_args, "indent keyword must be followed by \"text\", \"box\", or \"none\"");
|
yyerror(bison_args, "indent keyword must be followed by \"text\", \"box\", or \"none\"");
|
||||||
@ -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) {
|
if (eol_pattern == NULL) {
|
||||||
eol_pattern = compile_pattern("(?(?=\r)(\r\n?)|(\n))");
|
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, sample, u32_strlen(sample), 1);
|
||||||
uint32_t *replaced = regex_replace(eol_pattern, opt.eol, u32_sample, strlen(sample), 1);
|
bxstr_t *result = bxs_from_unicode(replaced);
|
||||||
char *result = u32_strconv_to_output(replaced);
|
|
||||||
BFREE(replaced);
|
BFREE(replaced);
|
||||||
BFREE(u32_sample);
|
|
||||||
return result;
|
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
|
#ifdef PARSER_DEBUG
|
||||||
fprintf(stderr, " Parser: SAMPLE block rule satisfied\n");
|
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;
|
return RC_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *p = sample;
|
uint32_t *p = find_first_nonblank_line(sample);
|
||||||
while ((*p == '\r' || *p == '\n') && *p != '\0') {
|
bxstr_t *line = adjust_eols(p);
|
||||||
p++;
|
|
||||||
}
|
|
||||||
char *line = adjust_eols(p);
|
|
||||||
if (line == NULL) {
|
if (line == NULL) {
|
||||||
perror(PROJECT);
|
perror(PROJECT);
|
||||||
return RC_ABORT;
|
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;
|
curdes.sample = line;
|
||||||
++(bison_args->num_mandatory);
|
++(bison_args->num_mandatory);
|
||||||
return RC_SUCCESS;
|
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;
|
* @return 0: success;
|
||||||
* 1: YYERROR must be invoked
|
* 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 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
|
* @param name_at_end the primary name of the design as specified at END statement
|
||||||
* @return 0: success;
|
* @return 0: success;
|
||||||
* 1: YYERROR must be invoked
|
* 1: YYERROR must be invoked;
|
||||||
* 2: YYABORT must be invoked
|
* 2: YYABORT must be invoked;
|
||||||
* 3: YYACCEPT 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);
|
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.
|
* Rule action called when a keyword entry is encountered in a box design.
|
||||||
* @param bison_args the parser state
|
* @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
|
* 1: YYERROR must be invoked
|
||||||
* 2: YYABORT 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);
|
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)
|
* (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 bison_args the parser state
|
||||||
* @param tag a single tag, or a comma-separated list of tags
|
* @param tag a single tag, or a comma-separated list of tags
|
||||||
* @return error code, 0 on success, anything else on failure
|
* @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
|
* 1: YYERROR must be invoked
|
||||||
* 2: YYABORT 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
|
#endif
|
||||||
|
143
src/parser.y
143
src/parser.y
@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "boxes.h"
|
#include "boxes.h"
|
||||||
|
#include "bxstring.h"
|
||||||
|
|
||||||
|
|
||||||
/** all the arguments which we pass to the bison parser */
|
/** all the arguments which we pass to the bison parser */
|
||||||
@ -40,7 +41,7 @@ typedef struct {
|
|||||||
size_t num_child_configs;
|
size_t num_child_configs;
|
||||||
|
|
||||||
/** the path to the config file we are parsing */
|
/** the path to the config file we are parsing */
|
||||||
char *config_file;
|
bxstr_t *config_file;
|
||||||
|
|
||||||
int num_mandatory;
|
int num_mandatory;
|
||||||
|
|
||||||
@ -56,7 +57,7 @@ typedef struct {
|
|||||||
int speeding;
|
int speeding;
|
||||||
|
|
||||||
/** names of config files specified via "parent" */
|
/** names of config files specified via "parent" */
|
||||||
char **parent_configs;
|
bxstr_t **parent_configs;
|
||||||
|
|
||||||
/** number of parent config files (size of parent_configs array) */
|
/** number of parent config files (size of parent_configs array) */
|
||||||
size_t num_parent_configs;
|
size_t num_parent_configs;
|
||||||
@ -72,6 +73,7 @@ typedef struct {
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <unictype.h>
|
||||||
|
|
||||||
#include "shape.h"
|
#include "shape.h"
|
||||||
#include "tools.h"
|
#include "tools.h"
|
||||||
@ -79,6 +81,7 @@ typedef struct {
|
|||||||
#include "parser.h"
|
#include "parser.h"
|
||||||
#include "lex.yy.h"
|
#include "lex.yy.h"
|
||||||
#include "parsecode.h"
|
#include "parsecode.h"
|
||||||
|
#include "unicode.h"
|
||||||
|
|
||||||
|
|
||||||
/** required for bison-flex bridge */
|
/** required for bison-flex bridge */
|
||||||
@ -113,7 +116,8 @@ typedef struct {
|
|||||||
%parse-param {pass_to_bison *bison_args}
|
%parse-param {pass_to_bison *bison_args}
|
||||||
|
|
||||||
%union {
|
%union {
|
||||||
char *s;
|
bxstr_t *s;
|
||||||
|
char *ascii;
|
||||||
char c;
|
char c;
|
||||||
shape_t shape;
|
shape_t shape;
|
||||||
sentry_t sentry;
|
sentry_t sentry;
|
||||||
@ -122,8 +126,9 @@ typedef struct {
|
|||||||
|
|
||||||
%token YPARENT YSHAPES YELASTIC YPADDING YSAMPLE YENDSAMPLE YBOX YEND YUNREC
|
%token YPARENT YSHAPES YELASTIC YPADDING YSAMPLE YENDSAMPLE YBOX YEND YUNREC
|
||||||
%token YREPLACE YREVERSE YTO YWITH YCHGDEL YTAGS
|
%token YREPLACE YREVERSE YTO YWITH YCHGDEL YTAGS
|
||||||
%token <s> KEYWORD
|
%token <ascii> KEYWORD
|
||||||
%token <s> WORD
|
%token <s> WORD
|
||||||
|
%token <ascii> ASCII_ID
|
||||||
%token <s> STRING
|
%token <s> STRING
|
||||||
%token <s> FILENAME
|
%token <s> FILENAME
|
||||||
%token <shape> SHAPE
|
%token <shape> SHAPE
|
||||||
@ -166,7 +171,8 @@ config_file
|
|||||||
BFREE (bison_args->designs);
|
BFREE (bison_args->designs);
|
||||||
bison_args->num_designs = 0;
|
bison_args->num_designs = 0;
|
||||||
if (!opt.design_choice_by_user && bison_args->num_parent_configs == 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;
|
YYABORT;
|
||||||
}
|
}
|
||||||
YYACCEPT;
|
YYACCEPT;
|
||||||
@ -209,22 +215,22 @@ design_or_error: design | error
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
alias: WORD
|
alias: ASCII_ID
|
||||||
{
|
{
|
||||||
invoke_action(action_add_alias(bison_args, $1));
|
invoke_action(action_add_alias(bison_args, $1));
|
||||||
}
|
}
|
||||||
|
|
||||||
alias_list: alias | alias_list ',' alias;
|
alias_list: alias | alias_list ',' alias;
|
||||||
|
|
||||||
design_id: WORD | WORD ',' alias_list;
|
design_id: ASCII_ID | ASCII_ID ',' alias_list;
|
||||||
|
|
||||||
design: YBOX design_id
|
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
|
| YPARENT FILENAME
|
||||||
{
|
{
|
||||||
char *filename = $2;
|
/*
|
||||||
if (filename[0] != filename[strlen(filename) - 1]
|
* Called when PARENT appears as a key inside a box design. That's a user mistake, but not an error.
|
||||||
|| (filename[0] >= 'a' && filename[0] <= 'z')
|
*/
|
||||||
|| (filename[0] >= 'A' && filename[0] <= 'Z')
|
bxstr_t *filename = $2;
|
||||||
|| (filename[0] >= '0' && filename[0] <= '9'))
|
if (filename->memory[0] != filename->memory[filename->num_chars - 1] || uc_is_alnum(filename->memory[0])) {
|
||||||
{
|
yyerror(bison_args, "string expected");
|
||||||
yyerror(bison_args, "string expected", filename);
|
|
||||||
YYERROR;
|
YYERROR;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
#ifdef PARSER_DEBUG
|
#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
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -270,10 +275,10 @@ entry: KEYWORD STRING
|
|||||||
|
|
||||||
| YTAGS '(' tag_list ')' | YTAGS tag_entry
|
| YTAGS '(' tag_list ')' | YTAGS tag_entry
|
||||||
|
|
||||||
| WORD STRING
|
| WORD STRING | ASCII_ID STRING
|
||||||
{
|
{
|
||||||
#ifdef PARSER_DEBUG
|
#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
|
#endif
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
@ -301,53 +306,12 @@ block: YSAMPLE STRING YENDSAMPLE
|
|||||||
|
|
||||||
| YREPLACE rflag STRING YWITH STRING
|
| YREPLACE rflag STRING YWITH STRING
|
||||||
{
|
{
|
||||||
int a = curdes.anz_reprules;
|
invoke_action(action_add_regex_rule(bison_args, "rep", &curdes.reprules, &curdes.anz_reprules, $3, $5, $2));
|
||||||
|
|
||||||
#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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
| YREVERSE rflag STRING YTO STRING
|
| YREVERSE rflag STRING YTO STRING
|
||||||
{
|
{
|
||||||
int a = curdes.anz_revrules;
|
invoke_action(action_add_regex_rule(bison_args, "rev", &curdes.revrules, &curdes.anz_revrules, $3, $5, $2));
|
||||||
|
|
||||||
#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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
| YPADDING '{' wlist '}'
|
| YPADDING '{' wlist '}'
|
||||||
@ -426,9 +390,8 @@ shape_def: '(' shape_lines ')'
|
|||||||
|
|
||||||
shape_lines: shape_lines ',' STRING
|
shape_lines: shape_lines ',' STRING
|
||||||
{
|
{
|
||||||
sentry_t rval = $1;
|
sentry_t rval = $1; // TODO move this to parsecode.c
|
||||||
size_t slen = strlen ($3);
|
size_t slen = $3->num_columns;
|
||||||
char **tmp;
|
|
||||||
|
|
||||||
#ifdef PARSER_DEBUG
|
#ifdef PARSER_DEBUG
|
||||||
fprintf (stderr, "Extending a shape entry\n");
|
fprintf (stderr, "Extending a shape entry\n");
|
||||||
@ -439,18 +402,38 @@ shape_lines: shape_lines ',' STRING
|
|||||||
YYERROR;
|
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++;
|
rval.height++;
|
||||||
tmp = (char **) realloc (rval.chars, rval.height*sizeof(char*));
|
|
||||||
|
char **tmp = (char **) realloc(rval.chars, rval.height * sizeof(char *));
|
||||||
if (tmp == NULL) {
|
if (tmp == NULL) {
|
||||||
perror (PROJECT": shape_lines11");
|
perror (PROJECT": shape_lines11");
|
||||||
YYABORT;
|
YYABORT;
|
||||||
}
|
}
|
||||||
rval.chars = tmp;
|
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) {
|
if (rval.chars[rval.height-1] == NULL) {
|
||||||
perror (PROJECT": shape_lines12");
|
perror (PROJECT": shape_lines12");
|
||||||
YYABORT;
|
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;
|
$$ = rval;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -462,18 +445,36 @@ shape_lines: shape_lines ',' STRING
|
|||||||
fprintf (stderr, "Initializing a shape entry with first line\n");
|
fprintf (stderr, "Initializing a shape entry with first line\n");
|
||||||
#endif
|
#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.height = 1;
|
||||||
|
|
||||||
rval.chars = (char **) malloc (sizeof(char*));
|
rval.chars = (char **) malloc (sizeof(char*));
|
||||||
if (rval.chars == NULL) {
|
if (rval.chars == NULL) {
|
||||||
perror (PROJECT": shape_lines21");
|
perror (PROJECT": shape_lines21");
|
||||||
YYABORT;
|
YYABORT;
|
||||||
}
|
}
|
||||||
rval.chars[0] = (char *) strdup ($1);
|
rval.chars[0] = (char *) strdup ($1->ascii);
|
||||||
if (rval.chars[0] == NULL) {
|
if (rval.chars[0] == NULL) {
|
||||||
perror (PROJECT": shape_lines22");
|
perror (PROJECT": shape_lines22");
|
||||||
YYABORT;
|
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;
|
$$ = rval;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
@ -481,7 +482,7 @@ shape_lines: shape_lines ',' STRING
|
|||||||
|
|
||||||
wlist: wlist wlist_entry | wlist_entry;
|
wlist: wlist wlist_entry | wlist_entry;
|
||||||
|
|
||||||
wlist_entry: WORD YNUMBER
|
wlist_entry: ASCII_ID YNUMBER
|
||||||
{
|
{
|
||||||
invoke_action(action_padding_entry(bison_args, $1, $2));
|
invoke_action(action_padding_entry(bison_args, $1, $2));
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user