Implement infrastructure around new lexer

The lexer itself is very much incomplete, but this is intended to be a
safe point to revert to should further implementation go south.
This commit is contained in:
ISSOtm
2020-07-23 12:12:58 +02:00
parent 3a44cc7722
commit 6dc4ce6599
15 changed files with 497 additions and 1586 deletions

View File

@@ -56,7 +56,6 @@ rgbasm_obj := \
src/asm/asmy.o \ src/asm/asmy.o \
src/asm/charmap.o \ src/asm/charmap.o \
src/asm/fstack.o \ src/asm/fstack.o \
src/asm/globlex.o \
src/asm/lexer.o \ src/asm/lexer.o \
src/asm/macro.o \ src/asm/macro.o \
src/asm/main.o \ src/asm/main.o \
@@ -73,7 +72,7 @@ rgbasm_obj := \
src/hashmap.o \ src/hashmap.o \
src/linkdefs.o src/linkdefs.o
src/asm/globlex.o src/asm/lexer.o src/asm/constexpr.o: src/asm/asmy.h src/asm/lexer.o: src/asm/asmy.h
rgblink_obj := \ rgblink_obj := \
src/link/assign.o \ src/link/assign.o \

View File

@@ -24,11 +24,8 @@
#define MAXMACROARGS 99999 #define MAXMACROARGS 99999
#define MAXINCPATHS 128 #define MAXINCPATHS 128
extern int32_t nLineNo;
extern uint32_t nTotalLines; extern uint32_t nTotalLines;
extern uint32_t nIFDepth; extern uint32_t nIFDepth;
extern bool skipElif;
extern char tzCurrentFileName[_MAX_PATH + 1];
extern struct Section *pCurrentSection; extern struct Section *pCurrentSection;
extern bool oDontExpandStrings; extern bool oDontExpandStrings;

View File

@@ -24,7 +24,7 @@
struct MacroArgs; struct MacroArgs;
struct sContext { struct sContext {
YY_BUFFER_STATE FlexHandle; struct LexerState *lexerState;
struct Symbol const *pMacro; struct Symbol const *pMacro;
struct sContext *next; struct sContext *next;
char tzFileName[_MAX_PATH + 1]; char tzFileName[_MAX_PATH + 1];
@@ -32,7 +32,6 @@ struct sContext {
uint32_t uniqueID; uint32_t uniqueID;
int32_t nLine; int32_t nLine;
uint32_t nStatus; uint32_t nStatus;
FILE *pFile;
char *pREPTBlock; char *pREPTBlock;
uint32_t nREPTBlockCount; uint32_t nREPTBlockCount;
uint32_t nREPTBlockSize; uint32_t nREPTBlockSize;
@@ -46,11 +45,17 @@ void fstk_RunInclude(char *tzFileName);
void fstk_Init(char *s); void fstk_Init(char *s);
void fstk_Dump(void); void fstk_Dump(void);
void fstk_DumpToStr(char *buf, size_t len); void fstk_DumpToStr(char *buf, size_t len);
void fstk_DumpStringExpansions(void);
void fstk_AddIncludePath(char *s); void fstk_AddIncludePath(char *s);
void fstk_RunMacro(char *s, struct MacroArgs *args); void fstk_RunMacro(char *s, struct MacroArgs *args);
void fstk_RunRept(uint32_t count, int32_t nReptLineNo); void fstk_RunRept(uint32_t count, int32_t nReptLineNo);
FILE *fstk_FindFile(char const *fname, char **incPathUsed); /**
* @param path The user-provided file name
* @param fullPath The address of a pointer, which will be made to point at the full path
* The pointer's value must be a valid argument to `realloc`, including NULL
* @param size Current size of the buffer, or 0 if the pointer is NULL
* @return True if the file was found, false if no path worked
*/
bool fstk_FindFile(char const *path, char **fullPath, size_t *size);
int32_t fstk_GetLine(void); int32_t fstk_GetLine(void);
#endif /* RGBDS_ASM_FSTACK_H */ #endif /* RGBDS_ASM_FSTACK_H */

View File

@@ -9,78 +9,44 @@
#ifndef RGBDS_ASM_LEXER_H #ifndef RGBDS_ASM_LEXER_H
#define RGBDS_ASM_LEXER_H #define RGBDS_ASM_LEXER_H
#include <stdint.h>
#include <stdio.h>
#define LEXHASHSIZE (1 << 11)
#define MAXSTRLEN 255 #define MAXSTRLEN 255
struct sLexInitString { struct LexerState;
char *tzName; extern struct LexerState *lexerState;
uint32_t nToken; extern struct LexerState *lexerStateEOL;
static inline struct LexerState *lexer_GetState(void)
{
return lexerState;
}
static inline void lexer_SetState(struct LexerState *state)
{
lexerState = state;
}
static inline void lexer_SetStateAtEOL(struct LexerState *state)
{
lexerStateEOL = state;
}
struct LexerState *lexer_OpenFile(char const *path);
struct LexerState *lexer_OpenFileView(void);
void lexer_DeleteState(struct LexerState *state);
enum LexerMode {
LEXER_NORMAL,
LEXER_RAW
}; };
struct sLexFloat { void lexer_SetMode(enum LexerMode mode);
uint32_t (*Callback)(char *s, uint32_t size); void lexer_ToggleStringExpansion(bool enable);
uint32_t nToken;
};
struct yy_buffer_state { char const *lexer_GetFileName(void);
/* Actual starting address */ unsigned int lexer_GetLineNo(void);
char *pBufferRealStart; void lexer_DumpStringExpansions(void);
/* Address where the data is initially written after a safety margin */
char *pBufferStart;
char *pBuffer;
size_t nBufferSize;
uint32_t oAtLineStart;
};
enum eLexerState {
LEX_STATE_NORMAL,
LEX_STATE_MACROARGS
};
struct sStringExpansionPos {
char *tzName;
char *pBuffer;
char *pBufferPos;
struct sStringExpansionPos *pParent;
};
#define INITIAL 0
#define macroarg 3
typedef struct yy_buffer_state *YY_BUFFER_STATE;
void setup_lexer(void);
void yy_set_state(enum eLexerState i);
YY_BUFFER_STATE yy_create_buffer(FILE *f);
YY_BUFFER_STATE yy_scan_bytes(char const *mem, uint32_t size);
void yy_delete_buffer(YY_BUFFER_STATE buf);
void yy_switch_to_buffer(YY_BUFFER_STATE buf);
uint32_t lex_FloatAlloc(const struct sLexFloat *tok);
void lex_FloatAddRange(uint32_t id, uint16_t start, uint16_t end);
void lex_FloatDeleteRange(uint32_t id, uint16_t start, uint16_t end);
void lex_FloatAddFirstRange(uint32_t id, uint16_t start, uint16_t end);
void lex_FloatDeleteFirstRange(uint32_t id, uint16_t start, uint16_t end);
void lex_FloatAddSecondRange(uint32_t id, uint16_t start, uint16_t end);
void lex_FloatDeleteSecondRange(uint32_t id, uint16_t start, uint16_t end);
void lex_Init(void);
void lex_AddStrings(const struct sLexInitString *lex);
void lex_SetBuffer(char *buffer, uint32_t len);
void lex_BeginStringExpansion(const char *tzName);
int yywrap(void);
int yylex(void); int yylex(void);
void yyunput(char c); void lexer_SkipToBlockEnd(int blockStartToken, int blockEndToken, int endToken,
void yyunputstr(const char *s); char **capture, size_t *size, char const *name);
void yyskipbytes(uint32_t count);
void yyunputbytes(uint32_t count);
extern YY_BUFFER_STATE pCurrentBuffer;
extern struct sStringExpansionPos *pCurrentStringExpansion;
void upperstring(char *s);
void lowerstring(char *s);
#endif /* RGBDS_ASM_LEXER_H */ #endif /* RGBDS_ASM_LEXER_H */

View File

@@ -43,6 +43,10 @@ void opt_Push(void);
void opt_Pop(void); void opt_Pop(void);
void opt_Parse(char *s); void opt_Parse(char *s);
void upperstring(char *s);
void lowerstring(char *s);
/* TODO: are these really needed? */
#define YY_FATAL_ERROR fatalerror #define YY_FATAL_ERROR fatalerror
#ifdef YYLMAX #ifdef YYLMAX

View File

@@ -31,7 +31,6 @@ set(rgbasm_src
"${BISON_ASMy_OUTPUT_SOURCE}" "${BISON_ASMy_OUTPUT_SOURCE}"
"asm/charmap.c" "asm/charmap.c"
"asm/fstack.c" "asm/fstack.c"
"asm/globlex.c"
"asm/lexer.c" "asm/lexer.c"
"asm/macro.c" "asm/macro.c"
"asm/main.c" "asm/main.c"

View File

@@ -39,6 +39,7 @@ uint32_t nListCountEmpty;
char *tzNewMacro; char *tzNewMacro;
uint32_t ulNewMacroSize; uint32_t ulNewMacroSize;
int32_t nPCOffset; int32_t nPCOffset;
bool skipElifs; /* If this is set, ELIFs cannot be executed anymore */
size_t symvaluetostring(char *dest, size_t maxLength, char *symName, size_t symvaluetostring(char *dest, size_t maxLength, char *symName,
const char *mode) const char *mode)
@@ -111,278 +112,6 @@ static uint32_t str2int2(uint8_t *s, int32_t length)
return r; return r;
} }
static uint32_t isWhiteSpace(char s)
{
return (s == ' ') || (s == '\t') || (s == '\0') || (s == '\n');
}
static uint32_t isRept(char *s)
{
return (strncasecmp(s, "REPT", 4) == 0)
&& isWhiteSpace(*(s - 1)) && isWhiteSpace(s[4]);
}
static uint32_t isEndr(char *s)
{
return (strncasecmp(s, "ENDR", 4) == 0)
&& isWhiteSpace(*(s - 1)) && isWhiteSpace(s[4]);
}
static void copyrept(void)
{
int32_t level = 1, len, instring = 0;
char *src = pCurrentBuffer->pBuffer;
char *bufferEnd = pCurrentBuffer->pBufferStart
+ pCurrentBuffer->nBufferSize;
while (src < bufferEnd && level) {
if (instring == 0) {
if (isRept(src)) {
level++;
src += 4;
} else if (isEndr(src)) {
level--;
src += 4;
} else {
if (*src == '\"')
instring = 1;
src++;
}
} else {
if (*src == '\\') {
src += 2;
} else if (*src == '\"') {
src++;
instring = 0;
} else {
src++;
}
}
}
if (level != 0)
fatalerror("Unterminated REPT block\n");
len = src - pCurrentBuffer->pBuffer - 4;
src = pCurrentBuffer->pBuffer;
ulNewMacroSize = len;
tzNewMacro = malloc(ulNewMacroSize + 1);
if (tzNewMacro == NULL)
fatalerror("Not enough memory for REPT block.\n");
uint32_t i;
tzNewMacro[ulNewMacroSize] = 0;
for (i = 0; i < ulNewMacroSize; i++) {
tzNewMacro[i] = src[i];
if (src[i] == '\n')
nLineNo++;
}
yyskipbytes(ulNewMacroSize + 4);
}
static uint32_t isMacro(char *s)
{
return (strncasecmp(s, "MACRO", 4) == 0)
&& isWhiteSpace(*(s - 1)) && isWhiteSpace(s[5]);
}
static uint32_t isEndm(char *s)
{
return (strncasecmp(s, "ENDM", 4) == 0)
&& isWhiteSpace(*(s - 1)) && isWhiteSpace(s[4]);
}
static void copymacro(void)
{
int32_t level = 1, len, instring = 0;
char *src = pCurrentBuffer->pBuffer;
char *bufferEnd = pCurrentBuffer->pBufferStart
+ pCurrentBuffer->nBufferSize;
while (src < bufferEnd && level) {
if (instring == 0) {
if (isMacro(src)) {
level++;
src += 4;
} else if (isEndm(src)) {
level--;
src += 4;
} else {
if(*src == '\"')
instring = 1;
src++;
}
} else {
if (*src == '\\') {
src += 2;
} else if (*src == '\"') {
src++;
instring = 0;
} else {
src++;
}
}
}
if (level != 0)
fatalerror("Unterminated MACRO definition.\n");
len = src - pCurrentBuffer->pBuffer - 4;
src = pCurrentBuffer->pBuffer;
ulNewMacroSize = len;
tzNewMacro = (char *)malloc(ulNewMacroSize + 1);
if (tzNewMacro == NULL)
fatalerror("Not enough memory for MACRO definition.\n");
uint32_t i;
tzNewMacro[ulNewMacroSize] = 0;
for (i = 0; i < ulNewMacroSize; i++) {
tzNewMacro[i] = src[i];
if (src[i] == '\n')
nLineNo++;
}
yyskipbytes(ulNewMacroSize + 4);
}
static bool endsIf(char c)
{
return isWhiteSpace(c) || c == '(' || c == '{';
}
static uint32_t isIf(char *s)
{
return (strncasecmp(s, "IF", 2) == 0)
&& isWhiteSpace(s[-1]) && endsIf(s[2]);
}
static uint32_t isElif(char *s)
{
return (strncasecmp(s, "ELIF", 4) == 0)
&& isWhiteSpace(s[-1]) && endsIf(s[4]);
}
static uint32_t isElse(char *s)
{
return (strncasecmp(s, "ELSE", 4) == 0)
&& isWhiteSpace(s[-1]) && isWhiteSpace(s[4]);
}
static uint32_t isEndc(char *s)
{
return (strncasecmp(s, "ENDC", 4) == 0)
&& isWhiteSpace(s[-1]) && isWhiteSpace(s[4]);
}
static void if_skip_to_else(void)
{
int32_t level = 1;
bool inString = false;
char *src = pCurrentBuffer->pBuffer;
while (*src && level) {
if (*src == '\n')
nLineNo++;
if (!inString) {
if (isIf(src)) {
level++;
src += 2;
} else if (level == 1 && isElif(src)) {
level--;
skipElif = false;
} else if (level == 1 && isElse(src)) {
level--;
src += 4;
} else if (isEndc(src)) {
level--;
if (level != 0)
src += 4;
} else {
if (*src == '\"')
inString = true;
src++;
}
} else {
if (*src == '\"') {
inString = false;
} else if (*src == '\\') {
/* Escaped quotes don't end the string */
if (*++src != '\"')
src--;
}
src++;
}
}
if (level != 0)
fatalerror("Unterminated IF construct\n");
int32_t len = src - pCurrentBuffer->pBuffer;
yyskipbytes(len);
yyunput('\n');
nLineNo--;
}
static void if_skip_to_endc(void)
{
int32_t level = 1;
bool inString = false;
char *src = pCurrentBuffer->pBuffer;
while (*src && level) {
if (*src == '\n')
nLineNo++;
if (!inString) {
if (isIf(src)) {
level++;
src += 2;
} else if (isEndc(src)) {
level--;
if (level != 0)
src += 4;
} else {
if (*src == '\"')
inString = true;
src++;
}
} else {
if (*src == '\"') {
inString = false;
} else if (*src == '\\') {
/* Escaped quotes don't end the string */
if (*++src != '\"')
src--;
}
src++;
}
}
if (level != 0)
fatalerror("Unterminated IF construct\n");
int32_t len = src - pCurrentBuffer->pBuffer;
yyskipbytes(len);
yyunput('\n');
nLineNo--;
}
static size_t strlenUTF8(const char *s) static size_t strlenUTF8(const char *s)
{ {
size_t len = 0; size_t len = 0;
@@ -660,7 +389,6 @@ lines : /* empty */
nListCountEmpty = 0; nListCountEmpty = 0;
nPCOffset = 0; nPCOffset = 0;
} line '\n' { } line '\n' {
nLineNo++;
nTotalLines++; nTotalLines++;
} }
; ;
@@ -699,9 +427,9 @@ label : /* empty */
; ;
macro : T_ID { macro : T_ID {
yy_set_state(LEX_STATE_MACROARGS); lexer_SetMode(LEXER_RAW);
} macroargs { } macroargs {
yy_set_state(LEX_STATE_NORMAL); lexer_SetMode(LEXER_NORMAL);
fstk_RunMacro($1, $3); fstk_RunMacro($1, $3);
} }
; ;
@@ -786,9 +514,9 @@ align : T_OP_ALIGN uconst {
; ;
opt : T_POP_OPT { opt : T_POP_OPT {
yy_set_state(LEX_STATE_MACROARGS); lexer_SetMode(LEXER_RAW);
} opt_list { } opt_list {
yy_set_state(LEX_STATE_NORMAL); lexer_SetMode(LEXER_NORMAL);
} }
; ;
@@ -875,15 +603,21 @@ load : T_POP_LOAD string ',' sectiontype sectorg sectattrs {
; ;
rept : T_POP_REPT uconst { rept : T_POP_REPT uconst {
uint32_t nDefinitionLineNo = nLineNo; uint32_t nDefinitionLineNo = lexer_GetLineNo();
copyrept(); char *body;
size_t size;
lexer_SkipToBlockEnd(T_POP_REPT, T_POP_ENDR, T_POP_ENDR,
&body, &size, "REPT block");
fstk_RunRept($2, nDefinitionLineNo); fstk_RunRept($2, nDefinitionLineNo);
} }
; ;
macrodef : T_LABEL ':' T_POP_MACRO { macrodef : T_LABEL ':' T_POP_MACRO {
int32_t nDefinitionLineNo = nLineNo; int32_t nDefinitionLineNo = lexer_GetLineNo();
copymacro(); char *body;
size_t size;
lexer_SkipToBlockEnd(T_POP_MACRO, T_POP_ENDM, T_POP_ENDM,
&body, &size, "macro definition");
sym_AddMacro($1, nDefinitionLineNo); sym_AddMacro($1, nDefinitionLineNo);
} }
; ;
@@ -956,9 +690,9 @@ dl : T_POP_DL constlist_32bit_entry ',' constlist_32bit {
; ;
purge : T_POP_PURGE { purge : T_POP_PURGE {
oDontExpandStrings = true; lexer_ToggleStringExpansion(false);
} purge_list { } purge_list {
oDontExpandStrings = false; lexer_ToggleStringExpansion(true);
} }
; ;
@@ -1054,8 +788,14 @@ printf : T_POP_PRINTF const { math_Print($2); }
if : T_POP_IF const { if : T_POP_IF const {
nIFDepth++; nIFDepth++;
if (!$2) if (!$2) {
if_skip_to_else(); /* The function is hardcoded to also stop on T_POP_ELSE and ENDC */
lexer_SkipToBlockEnd(T_POP_IF, T_POP_ENDC, T_POP_ELIF,
NULL, NULL, "if block");
skipElifs = false;
} else {
skipElifs = true;
}
} }
; ;
@@ -1063,7 +803,7 @@ elif : T_POP_ELIF const {
if (nIFDepth <= 0) if (nIFDepth <= 0)
fatalerror("Found ELIF outside an IF construct\n"); fatalerror("Found ELIF outside an IF construct\n");
if (skipElif) { if (skipElifs) {
/* /*
* Executed when ELIF is reached at the end of * Executed when ELIF is reached at the end of
* an IF or ELIF block for which the condition * an IF or ELIF block for which the condition
@@ -1071,21 +811,24 @@ elif : T_POP_ELIF const {
* *
* Continue parsing at ENDC keyword * Continue parsing at ENDC keyword
*/ */
if_skip_to_endc(); lexer_SkipToBlockEnd(T_POP_IF, T_POP_ENDC, T_POP_ENDC,
NULL, NULL, "elif block");
} else { } else {
/* /*
* Executed when ELIF is skipped to because the * Executed when ELIF is skipped to because the
* condition of the previous IF or ELIF block * condition of the previous IF or ELIF block
* was false. * was false.
*/ */
skipElif = true;
if (!$2) { if (!$2) {
/* /*
* Continue parsing after ELSE, or at * Continue parsing after ELSE, or at
* ELIF or ENDC keyword. * ELIF or ENDC keyword.
*/ */
if_skip_to_else(); lexer_SkipToBlockEnd(T_POP_IF, T_POP_ENDC, T_POP_ELIF,
NULL, NULL, "elif block");
} else {
skipElifs = true;
} }
} }
} }
@@ -1096,7 +839,8 @@ else : T_POP_ELSE {
fatalerror("Found ELSE outside an IF construct\n"); fatalerror("Found ELSE outside an IF construct\n");
/* Continue parsing at ENDC keyword */ /* Continue parsing at ENDC keyword */
if_skip_to_endc(); lexer_SkipToBlockEnd(T_POP_IF, T_POP_ENDC, T_POP_ENDC,
NULL, NULL, "else block");
} }
; ;
@@ -1267,13 +1011,13 @@ relocexpr_no_str : scoped_id { rpn_Symbol(&$$, $1); }
} }
| T_OP_BANK '(' string ')' { rpn_BankSection(&$$, $3); } | T_OP_BANK '(' string ')' { rpn_BankSection(&$$, $3); }
| T_OP_DEF { | T_OP_DEF {
oDontExpandStrings = true; lexer_ToggleStringExpansion(false);
} '(' scoped_id ')' { } '(' scoped_id ')' {
struct Symbol const *sym = sym_FindSymbol($4); struct Symbol const *sym = sym_FindSymbol($4);
rpn_Number(&$$, !!sym); rpn_Number(&$$, !!sym);
oDontExpandStrings = false; lexer_ToggleStringExpansion(true);
} }
| T_OP_ROUND '(' const ')' { | T_OP_ROUND '(' const ')' {
rpn_Number(&$$, math_Round($3)); rpn_Number(&$$, math_Round($3));

View File

@@ -36,10 +36,7 @@ static struct sContext *pFileStack;
static unsigned int nFileStackDepth; static unsigned int nFileStackDepth;
unsigned int nMaxRecursionDepth; unsigned int nMaxRecursionDepth;
static struct Symbol const *pCurrentMacro; static struct Symbol const *pCurrentMacro;
static YY_BUFFER_STATE CurrentFlexHandle;
static FILE *pCurrentFile;
static uint32_t nCurrentStatus; static uint32_t nCurrentStatus;
char tzCurrentFileName[_MAX_PATH + 1];
static char IncludePaths[MAXINCPATHS][_MAX_PATH + 1]; static char IncludePaths[MAXINCPATHS][_MAX_PATH + 1];
static int32_t NextIncPath; static int32_t NextIncPath;
static uint32_t nMacroCount; static uint32_t nMacroCount;
@@ -81,10 +78,8 @@ static void pushcontext(void)
if (*ppFileStack == NULL) if (*ppFileStack == NULL)
fatalerror("No memory for context\n"); fatalerror("No memory for context\n");
(*ppFileStack)->FlexHandle = CurrentFlexHandle;
(*ppFileStack)->next = NULL; (*ppFileStack)->next = NULL;
strcpy((char *)(*ppFileStack)->tzFileName, (char *)tzCurrentFileName); (*ppFileStack)->nLine = lexer_GetLineNo();
(*ppFileStack)->nLine = nLineNo;
switch ((*ppFileStack)->nStatus = nCurrentStatus) { switch ((*ppFileStack)->nStatus = nCurrentStatus) {
case STAT_isMacroArg: case STAT_isMacroArg:
@@ -93,7 +88,6 @@ static void pushcontext(void)
(*ppFileStack)->pMacro = pCurrentMacro; (*ppFileStack)->pMacro = pCurrentMacro;
break; break;
case STAT_isInclude: case STAT_isInclude:
(*ppFileStack)->pFile = pCurrentFile;
break; break;
case STAT_isREPTBlock: case STAT_isREPTBlock:
(*ppFileStack)->macroArgs = macro_GetCurrentArgs(); (*ppFileStack)->macroArgs = macro_GetCurrentArgs();
@@ -107,8 +101,6 @@ static void pushcontext(void)
fatalerror("%s: Internal error.\n", __func__); fatalerror("%s: Internal error.\n", __func__);
} }
(*ppFileStack)->uniqueID = macro_GetUniqueID(); (*ppFileStack)->uniqueID = macro_GetUniqueID();
nLineNo = 0;
} }
static int32_t popcontext(void) static int32_t popcontext(void)
@@ -122,20 +114,15 @@ static int32_t popcontext(void)
int nNbCharsWritten; int nNbCharsWritten;
int nNbCharsLeft; int nNbCharsLeft;
yy_delete_buffer(CurrentFlexHandle);
CurrentFlexHandle =
yy_scan_bytes(pCurrentREPTBlock,
nCurrentREPTBlockSize);
yy_switch_to_buffer(CurrentFlexHandle);
macro_SetUniqueID(nMacroCount++); macro_SetUniqueID(nMacroCount++);
/* Increment REPT count in file path */ /* Increment REPT count in file path */
pREPTIterationWritePtr = pREPTIterationWritePtr =
strrchr(tzCurrentFileName, '~') + 1; strrchr(lexer_GetFileName(), '~') + 1;
nREPTIterationNo = nREPTIterationNo =
strtoul(pREPTIterationWritePtr, NULL, 10); strtoul(pREPTIterationWritePtr, NULL, 10);
nNbCharsLeft = sizeof(tzCurrentFileName) nNbCharsLeft = sizeof(lexer_GetFileName())
- (pREPTIterationWritePtr - tzCurrentFileName); - (pREPTIterationWritePtr - lexer_GetFileName());
nNbCharsWritten = snprintf(pREPTIterationWritePtr, nNbCharsWritten = snprintf(pREPTIterationWritePtr,
nNbCharsLeft, "%lu", nNbCharsLeft, "%lu",
nREPTIterationNo + 1); nREPTIterationNo + 1);
@@ -150,7 +137,6 @@ static int32_t popcontext(void)
fatalerror("Cannot write REPT count to file path\n"); fatalerror("Cannot write REPT count to file path\n");
} }
nLineNo = nCurrentREPTBodyFirstLine;
return 0; return 0;
} }
} }
@@ -165,19 +151,8 @@ static int32_t popcontext(void)
pLastFile = *ppLastFile; pLastFile = *ppLastFile;
} }
yy_delete_buffer(CurrentFlexHandle); lexer_DeleteState(lexer_GetState());
nLineNo = nCurrentStatus == STAT_isREPTBlock ? nCurrentREPTBodyLastLine lexer_SetState(pLastFile->lexerState);
: pLastFile->nLine;
if (nCurrentStatus == STAT_isInclude)
fclose(pCurrentFile);
if (nCurrentStatus == STAT_isMacro
|| nCurrentStatus == STAT_isREPTBlock)
nLineNo++;
CurrentFlexHandle = pLastFile->FlexHandle;
strcpy((char *)tzCurrentFileName, (char *)pLastFile->tzFileName);
switch (pLastFile->nStatus) { switch (pLastFile->nStatus) {
struct MacroArgs *args; struct MacroArgs *args;
@@ -193,7 +168,6 @@ static int32_t popcontext(void)
pCurrentMacro = pLastFile->pMacro; pCurrentMacro = pLastFile->pMacro;
break; break;
case STAT_isInclude: case STAT_isInclude:
pCurrentFile = pLastFile->pFile;
break; break;
case STAT_isREPTBlock: case STAT_isREPTBlock:
args = macro_GetCurrentArgs(); args = macro_GetCurrentArgs();
@@ -218,7 +192,6 @@ static int32_t popcontext(void)
free(*ppLastFile); free(*ppLastFile);
*ppLastFile = NULL; *ppLastFile = NULL;
yy_switch_to_buffer(CurrentFlexHandle);
return 0; return 0;
} }
@@ -229,11 +202,11 @@ int32_t fstk_GetLine(void)
switch (nCurrentStatus) { switch (nCurrentStatus) {
case STAT_isInclude: case STAT_isInclude:
/* This is the normal mode, also used when including a file. */ /* This is the normal mode, also used when including a file. */
return nLineNo; return lexer_GetLineNo();
case STAT_isMacro: case STAT_isMacro:
break; /* Peek top file of the stack */ break; /* Peek top file of the stack */
case STAT_isMacroArg: case STAT_isMacroArg:
return nLineNo; /* ??? */ return lexer_GetLineNo(); /* ??? */
case STAT_isREPTBlock: case STAT_isREPTBlock:
break; /* Peek top file of the stack */ break; /* Peek top file of the stack */
default: default:
@@ -277,7 +250,7 @@ void fstk_Dump(void)
pLastFile = pLastFile->next; pLastFile = pLastFile->next;
} }
fprintf(stderr, "%s(%" PRId32 ")", tzCurrentFileName, nLineNo); fprintf(stderr, "%s(%" PRId32 ")", lexer_GetFileName(), lexer_GetLineNo());
} }
void fstk_DumpToStr(char *buf, size_t buflen) void fstk_DumpToStr(char *buf, size_t buflen)
@@ -299,7 +272,7 @@ void fstk_DumpToStr(char *buf, size_t buflen)
} }
retcode = snprintf(&buf[buflen - len], len, "%s(%" PRId32 ")", retcode = snprintf(&buf[buflen - len], len, "%s(%" PRId32 ")",
tzCurrentFileName, nLineNo); lexer_GetFileName(), lexer_GetLineNo());
if (retcode < 0) if (retcode < 0)
fatalerror("Failed to dump file stack to string: %s\n", strerror(errno)); fatalerror("Failed to dump file stack to string: %s\n", strerror(errno));
else if (retcode >= len) else if (retcode >= len)
@@ -311,20 +284,6 @@ void fstk_DumpToStr(char *buf, size_t buflen)
warning(WARNING_LONG_STR, "File stack dump too long, got truncated\n"); warning(WARNING_LONG_STR, "File stack dump too long, got truncated\n");
} }
/*
* Dump the string expansion stack to stderr
*/
void fstk_DumpStringExpansions(void)
{
const struct sStringExpansionPos *pExpansion = pCurrentStringExpansion;
while (pExpansion) {
fprintf(stderr, "while expanding symbol \"%s\"\n",
pExpansion->tzName);
pExpansion = pExpansion->pParent;
}
}
/* /*
* Extra includepath stuff * Extra includepath stuff
*/ */
@@ -351,63 +310,58 @@ static void printdep(const char *fileName)
} }
} }
static FILE *getFile(char const *pathname) static bool isPathValid(char const *pathname)
{ {
struct stat statbuf; struct stat statbuf;
if (stat(pathname, &statbuf) != 0) if (stat(pathname, &statbuf) != 0)
return NULL; return false;
/* Reject directories */ /* Reject directories */
if (S_ISDIR(statbuf.st_mode)) return !S_ISDIR(statbuf.st_mode);
return NULL;
return fopen(pathname, "rb");
} }
FILE *fstk_FindFile(char const *fname, char **incPathUsed) bool fstk_FindFile(char const *path, char **fullPath, size_t *size)
{ {
if (fname == NULL) if (!*size) {
return NULL; *size = 64; /* This is arbitrary, really */
*fullPath = realloc(*fullPath, *size);
char path[_MAX_PATH]; if (!*fullPath)
FILE *f = getFile(fname); error("realloc error during include path search: %s\n",
strerror(errno));
if (f) {
printdep(fname);
return f;
} }
for (size_t i = 0; i < NextIncPath; ++i) { if (*fullPath) {
/* for (size_t i = 0; i <= NextIncPath; ++i) {
* The function snprintf() does not write more than `size` bytes char *incPath = i ? IncludePaths[i - 1] : "";
* (including the terminating null byte ('\0')). If the output int len = snprintf(*fullPath, *size, "%s%s", incPath, path);
* was truncated due to this limit, the return value is the
* number of characters (excluding the terminating null byte)
* which would have been written to the final string if enough
* space had been available. Thus, a return value of `size` or
* more means that the output was truncated.
*/
int fullpathlen = snprintf(path, sizeof(path), "%s%s",
IncludePaths[i], fname);
if (fullpathlen >= (int)sizeof(path)) /* Oh how I wish `asnprintf` was standard... */
continue; if (len >= *size) { /* `len` doesn't include the terminator, `size` does */
*size = len + 1;
*fullPath = realloc(*fullPath, *size);
if (!*fullPath) {
error("realloc error during include path search: %s\n",
strerror(errno));
break;
}
len = sprintf(*fullPath, "%s%s", incPath, path);
}
f = getFile(path); if (len < 0) {
if (f) { error("snprintf error during include path search: %s\n",
printdep(path); strerror(errno));
} else if (isPathValid(*fullPath)) {
if (incPathUsed) printdep(*fullPath);
*incPathUsed = IncludePaths[i]; return true;
return f; }
} }
} }
errno = ENOENT; errno = ENOENT;
if (oGeneratedMissingIncludes) if (oGeneratedMissingIncludes)
printdep(fname); printdep(path);
return NULL; return false;
} }
/* /*
@@ -415,33 +369,31 @@ FILE *fstk_FindFile(char const *fname, char **incPathUsed)
*/ */
void fstk_RunInclude(char *tzFileName) void fstk_RunInclude(char *tzFileName)
{ {
char *incPathUsed = ""; char *fullPath = NULL;
FILE *f = fstk_FindFile(tzFileName, &incPathUsed); size_t size = 0;
if (f == NULL) { if (!fstk_FindFile(tzFileName, &fullPath, &size)) {
if (oGeneratedMissingIncludes) { if (oGeneratedMissingIncludes)
oFailedOnMissingInclude = true; oFailedOnMissingInclude = true;
return; else
} error("Unable to open included file '%s': %s\n",
error("Unable to open included file '%s': %s\n", tzFileName, strerror(errno)); tzFileName, strerror(errno));
free(fullPath);
return; return;
} }
pushcontext(); pushcontext();
nLineNo = 1;
nCurrentStatus = STAT_isInclude; nCurrentStatus = STAT_isInclude;
snprintf(tzCurrentFileName, sizeof(tzCurrentFileName), "%s%s",
incPathUsed, tzFileName);
if (verbose) if (verbose)
printf("Assembling %s\n", tzCurrentFileName); printf("Assembling %s\n", fullPath);
pCurrentFile = f;
CurrentFlexHandle = yy_create_buffer(pCurrentFile);
yy_switch_to_buffer(CurrentFlexHandle);
/* Dirty hack to give the INCLUDE directive a linefeed */ struct LexerState *state = lexer_OpenFile(fullPath);
yyunput('\n'); if (!state)
nLineNo--; /* If lexer had an error, it already reported it */
fatalerror("Failed to open file for INCLUDE\n"); /* TODO: make this non-fatal? */
lexer_SetStateAtEOL(state);
free(fullPath);
} }
/* /*
@@ -450,7 +402,6 @@ void fstk_RunInclude(char *tzFileName)
void fstk_RunMacro(char *s, struct MacroArgs *args) void fstk_RunMacro(char *s, struct MacroArgs *args)
{ {
struct Symbol const *sym = sym_FindSymbol(s); struct Symbol const *sym = sym_FindSymbol(s);
int nPrintedChars;
if (sym == NULL) { if (sym == NULL) {
error("Macro \"%s\" not defined\n", s); error("Macro \"%s\" not defined\n", s);
@@ -464,21 +415,10 @@ void fstk_RunMacro(char *s, struct MacroArgs *args)
pushcontext(); pushcontext();
macro_SetUniqueID(nMacroCount++); macro_SetUniqueID(nMacroCount++);
/* Minus 1 because there is a newline at the beginning of the buffer */ /* Minus 1 because there is a newline at the beginning of the buffer */
nLineNo = sym->fileLine - 1;
macro_UseNewArgs(args); macro_UseNewArgs(args);
nCurrentStatus = STAT_isMacro; nCurrentStatus = STAT_isMacro;
nPrintedChars = snprintf(tzCurrentFileName, _MAX_PATH + 1,
"%s::%s", sym->fileName, s);
if (nPrintedChars > _MAX_PATH) {
popcontext();
fatalerror("File name + macro name is too large to fit into buffer\n");
}
pCurrentMacro = sym; pCurrentMacro = sym;
/* TODO: why is `strlen` being used when there's a macro size field? */
CurrentFlexHandle = yy_scan_bytes(pCurrentMacro->macro,
strlen(pCurrentMacro->macro));
yy_switch_to_buffer(CurrentFlexHandle);
} }
/* /*
@@ -487,11 +427,6 @@ void fstk_RunMacro(char *s, struct MacroArgs *args)
void fstk_RunRept(uint32_t count, int32_t nReptLineNo) void fstk_RunRept(uint32_t count, int32_t nReptLineNo)
{ {
if (count) { if (count) {
static const char *tzReptStr = "::REPT~1";
/* For error printing to make sense, fake nLineNo */
nCurrentREPTBodyLastLine = nLineNo;
nLineNo = nReptLineNo;
pushcontext(); pushcontext();
macro_SetUniqueID(nMacroCount++); macro_SetUniqueID(nMacroCount++);
nCurrentREPTBlockCount = count; nCurrentREPTBlockCount = count;
@@ -499,15 +434,6 @@ void fstk_RunRept(uint32_t count, int32_t nReptLineNo)
nCurrentREPTBlockSize = ulNewMacroSize; nCurrentREPTBlockSize = ulNewMacroSize;
pCurrentREPTBlock = tzNewMacro; pCurrentREPTBlock = tzNewMacro;
nCurrentREPTBodyFirstLine = nReptLineNo + 1; nCurrentREPTBodyFirstLine = nReptLineNo + 1;
nLineNo = nReptLineNo;
if (strlen(tzCurrentFileName) + strlen(tzReptStr) > _MAX_PATH)
fatalerror("Cannot append \"%s\" to file path\n", tzReptStr);
strcat(tzCurrentFileName, tzReptStr);
CurrentFlexHandle =
yy_scan_bytes(pCurrentREPTBlock, nCurrentREPTBlockSize);
yy_switch_to_buffer(CurrentFlexHandle);
} }
} }
@@ -526,7 +452,6 @@ void fstk_Init(char *pFileName)
// minus 2 to account for trailing "\"\0" // minus 2 to account for trailing "\"\0"
// minus 1 to avoid a buffer overflow in extreme cases // minus 1 to avoid a buffer overflow in extreme cases
while (*c && fileNameIndex < sizeof(tzSymFileName) - 2 - 1) { while (*c && fileNameIndex < sizeof(tzSymFileName) - 2 - 1) {
if (*c == '"') { if (*c == '"') {
tzSymFileName[fileNameIndex++] = '\\'; tzSymFileName[fileNameIndex++] = '\\';
} }
@@ -541,19 +466,8 @@ void fstk_Init(char *pFileName)
sym_AddString("__FILE__", tzSymFileName); sym_AddString("__FILE__", tzSymFileName);
pFileStack = NULL; pFileStack = NULL;
if (strcmp(pFileName, "-") == 0) {
pCurrentFile = stdin;
} else {
pCurrentFile = fopen(pFileName, "rb");
if (pCurrentFile == NULL)
fatalerror("Unable to open file '%s': %s\n", pFileName, strerror(errno));
}
nFileStackDepth = 0; nFileStackDepth = 0;
nMacroCount = 0; nMacroCount = 0;
nCurrentStatus = STAT_isInclude; nCurrentStatus = STAT_isInclude;
snprintf(tzCurrentFileName, _MAX_PATH + 1, "%s", pFileName);
CurrentFlexHandle = yy_create_buffer(pCurrentFile);
yy_switch_to_buffer(CurrentFlexHandle);
nLineNo = 1;
} }

View File

@@ -287,10 +287,10 @@ uint32_t ParseSymbol(char *src, uint32_t size)
/* Feed the symbol's contents into the buffer */ /* Feed the symbol's contents into the buffer */
yyunputstr(s = sym_GetStringValue(sym)); yyunputstr(s = sym_GetStringValue(sym));
/* Lines inserted this way shall not increase nLineNo */ /* Lines inserted this way shall not increase lexer_GetLineNo() */
while (*s) { while (*s) {
if (*s++ == '\n') if (*s++ == '\n')
nLineNo--; lexer_GetLineNo()--;
} }
return 0; return 0;
} }

File diff suppressed because it is too large Load Diff

View File

@@ -6,6 +6,7 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
#include <ctype.h>
#include <errno.h> #include <errno.h>
#include <float.h> #include <float.h>
#include <inttypes.h> #include <inttypes.h>
@@ -41,10 +42,6 @@ char **cldefines;
clock_t nStartClock, nEndClock; clock_t nStartClock, nEndClock;
uint32_t nTotalLines, nIFDepth; uint32_t nTotalLines, nIFDepth;
bool skipElif;
uint32_t unionStart[128], unionSize[128];
int32_t nLineNo;
#if defined(YYDEBUG) && YYDEBUG #if defined(YYDEBUG) && YYDEBUG
extern int yydebug; extern int yydebug;
@@ -76,64 +73,8 @@ struct sOptionStackEntry *pOptionStack;
void opt_SetCurrentOptions(struct sOptions *pOpt) void opt_SetCurrentOptions(struct sOptions *pOpt)
{ {
if (nGBGfxID != -1) { /* TODO */
lex_FloatDeleteRange(nGBGfxID, CurrentOptions.gbgfx[0], (void)pOpt;
CurrentOptions.gbgfx[0]);
lex_FloatDeleteRange(nGBGfxID, CurrentOptions.gbgfx[1],
CurrentOptions.gbgfx[1]);
lex_FloatDeleteRange(nGBGfxID, CurrentOptions.gbgfx[2],
CurrentOptions.gbgfx[2]);
lex_FloatDeleteRange(nGBGfxID, CurrentOptions.gbgfx[3],
CurrentOptions.gbgfx[3]);
lex_FloatDeleteSecondRange(nGBGfxID, CurrentOptions.gbgfx[0],
CurrentOptions.gbgfx[0]);
lex_FloatDeleteSecondRange(nGBGfxID, CurrentOptions.gbgfx[1],
CurrentOptions.gbgfx[1]);
lex_FloatDeleteSecondRange(nGBGfxID, CurrentOptions.gbgfx[2],
CurrentOptions.gbgfx[2]);
lex_FloatDeleteSecondRange(nGBGfxID, CurrentOptions.gbgfx[3],
CurrentOptions.gbgfx[3]);
}
if (nBinaryID != -1) {
lex_FloatDeleteRange(nBinaryID, CurrentOptions.binary[0],
CurrentOptions.binary[0]);
lex_FloatDeleteRange(nBinaryID, CurrentOptions.binary[1],
CurrentOptions.binary[1]);
lex_FloatDeleteSecondRange(nBinaryID, CurrentOptions.binary[0],
CurrentOptions.binary[0]);
lex_FloatDeleteSecondRange(nBinaryID, CurrentOptions.binary[1],
CurrentOptions.binary[1]);
}
CurrentOptions = *pOpt;
if (nGBGfxID != -1) {
lex_FloatAddRange(nGBGfxID, CurrentOptions.gbgfx[0],
CurrentOptions.gbgfx[0]);
lex_FloatAddRange(nGBGfxID, CurrentOptions.gbgfx[1],
CurrentOptions.gbgfx[1]);
lex_FloatAddRange(nGBGfxID, CurrentOptions.gbgfx[2],
CurrentOptions.gbgfx[2]);
lex_FloatAddRange(nGBGfxID, CurrentOptions.gbgfx[3],
CurrentOptions.gbgfx[3]);
lex_FloatAddSecondRange(nGBGfxID, CurrentOptions.gbgfx[0],
CurrentOptions.gbgfx[0]);
lex_FloatAddSecondRange(nGBGfxID, CurrentOptions.gbgfx[1],
CurrentOptions.gbgfx[1]);
lex_FloatAddSecondRange(nGBGfxID, CurrentOptions.gbgfx[2],
CurrentOptions.gbgfx[2]);
lex_FloatAddSecondRange(nGBGfxID, CurrentOptions.gbgfx[3],
CurrentOptions.gbgfx[3]);
}
if (nBinaryID != -1) {
lex_FloatAddRange(nBinaryID, CurrentOptions.binary[0],
CurrentOptions.binary[0]);
lex_FloatAddRange(nBinaryID, CurrentOptions.binary[1],
CurrentOptions.binary[1]);
lex_FloatAddSecondRange(nBinaryID, CurrentOptions.binary[0],
CurrentOptions.binary[0]);
lex_FloatAddSecondRange(nBinaryID, CurrentOptions.binary[1],
CurrentOptions.binary[1]);
}
} }
void opt_Parse(char *s) void opt_Parse(char *s)
@@ -251,6 +192,22 @@ static void opt_ParseDefines(void)
sym_AddString(cldefines[i], cldefines[i + 1]); sym_AddString(cldefines[i], cldefines[i + 1]);
} }
void upperstring(char *s)
{
while (*s) {
*s = toupper(*s);
s++;
}
}
void lowerstring(char *s)
{
while (*s) {
*s = tolower(*s);
s++;
}
}
/* Escapes Make-special chars from a string */ /* Escapes Make-special chars from a string */
static char *make_escape(const char *str) static char *make_escape(const char *str)
{ {
@@ -516,8 +473,6 @@ int main(int argc, char *argv[])
tzMainfile = argv[argc - 1]; tzMainfile = argv[argc - 1];
setup_lexer();
if (verbose) if (verbose)
printf("Assembling %s\n", tzMainfile); printf("Assembling %s\n", tzMainfile);
@@ -530,17 +485,20 @@ int main(int argc, char *argv[])
nStartClock = clock(); nStartClock = clock();
nLineNo = 1;
nTotalLines = 0; nTotalLines = 0;
nIFDepth = 0; nIFDepth = 0;
skipElif = true;
sym_Init(); sym_Init();
sym_SetExportAll(exportall); sym_SetExportAll(exportall);
fstk_Init(tzMainfile); fstk_Init(tzMainfile);
struct LexerState *state = lexer_OpenFile(tzMainfile);
if (!state)
fatalerror("Failed to open main file!");
lexer_SetState(state);
opt_ParseDefines(); opt_ParseDefines();
charmap_New("main", NULL); charmap_New("main", NULL);
yy_set_state(LEX_STATE_NORMAL);
opt_SetCurrentOptions(&DefaultOptions); opt_SetCurrentOptions(&DefaultOptions);
if (yyparse() != 0 || nbErrors != 0) if (yyparse() != 0 || nbErrors != 0)

View File

@@ -656,9 +656,15 @@ void out_BinaryFile(char const *s, int32_t startPos)
startPos = 0; startPos = 0;
} }
FILE *f = fstk_FindFile(s, NULL); char *fullPath = NULL;
size_t size = 0;
FILE *f = NULL;
if (fstk_FindFile(s, &fullPath, &size))
f = fopen(fullPath, "rb");
if (!f) { if (!f) {
free(fullPath);
if (oGeneratedMissingIncludes) { if (oGeneratedMissingIncludes) {
oFailedOnMissingInclude = true; oFailedOnMissingInclude = true;
return; return;
@@ -699,6 +705,7 @@ void out_BinaryFile(char const *s, int32_t startPos)
error("Error reading INCBIN file '%s': %s\n", s, strerror(errno)); error("Error reading INCBIN file '%s': %s\n", s, strerror(errno));
fclose(f); fclose(f);
free(fullPath);
} }
void out_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length) void out_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length)
@@ -715,9 +722,15 @@ void out_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length)
if (length == 0) /* Don't even bother with 0-byte slices */ if (length == 0) /* Don't even bother with 0-byte slices */
return; return;
FILE *f = fstk_FindFile(s, NULL); char *fullPath = NULL;
size_t size = 0;
FILE *f = NULL;
if (fstk_FindFile(s, &fullPath, &size))
f = fopen(fullPath, "rb");
if (!f) { if (!f) {
free(fullPath);
if (oGeneratedMissingIncludes) { if (oGeneratedMissingIncludes) {
oFailedOnMissingInclude = true; oFailedOnMissingInclude = true;
return; return;
@@ -767,6 +780,7 @@ void out_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length)
} }
fclose(f); fclose(f);
free(fullPath);
} }
/* /*

View File

@@ -82,7 +82,7 @@ static int32_t Callback_NARG(void)
static int32_t Callback__LINE__(void) static int32_t Callback__LINE__(void)
{ {
return nLineNo; return lexer_GetLineNo();
} }
static int32_t CallbackPC(void) static int32_t CallbackPC(void)
@@ -113,8 +113,9 @@ int32_t sym_GetValue(struct Symbol const *sym)
static void updateSymbolFilename(struct Symbol *sym) static void updateSymbolFilename(struct Symbol *sym)
{ {
if (snprintf(sym->fileName, _MAX_PATH + 1, "%s", if (snprintf(sym->fileName, _MAX_PATH + 1, "%s",
tzCurrentFileName) > _MAX_PATH) lexer_GetFileName()) > _MAX_PATH)
fatalerror("%s: File name is too long: '%s'\n", __func__, tzCurrentFileName); fatalerror("%s: File name is too long: '%s'\n", __func__,
lexer_GetFileName());
sym->fileLine = fstk_GetLine(); sym->fileLine = fstk_GetLine();
} }

View File

@@ -204,7 +204,7 @@ void verror(const char *fmt, va_list args, char const *flag)
fstk_Dump(); fstk_Dump();
fprintf(stderr, flag ? ": [-Werror=%s]\n " : ":\n ", flag); fprintf(stderr, flag ? ": [-Werror=%s]\n " : ":\n ", flag);
vfprintf(stderr, fmt, args); vfprintf(stderr, fmt, args);
fstk_DumpStringExpansions(); lexer_DumpStringExpansions();
nbErrors++; nbErrors++;
} }
@@ -256,7 +256,7 @@ void warning(enum WarningID id, char const *fmt, ...)
fstk_Dump(); fstk_Dump();
fprintf(stderr, ": [-W%s]\n ", flag); fprintf(stderr, ": [-W%s]\n ", flag);
vfprintf(stderr, fmt, args); vfprintf(stderr, fmt, args);
fstk_DumpStringExpansions(); lexer_DumpStringExpansions();
va_end(args); va_end(args);
} }