mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 10:12:06 +00:00
@@ -53,9 +53,6 @@ extern size_t nMaxRecursionDepth;
|
|||||||
|
|
||||||
struct MacroArgs;
|
struct MacroArgs;
|
||||||
|
|
||||||
uint32_t fstk_GetIFDepth(void);
|
|
||||||
void fstk_IncIFDepth(void);
|
|
||||||
void fstk_DecIFDepth(void);
|
|
||||||
void fstk_Dump(struct FileStackNode const *node, uint32_t lineNo);
|
void fstk_Dump(struct FileStackNode const *node, uint32_t lineNo);
|
||||||
void fstk_DumpCurrent(void);
|
void fstk_DumpCurrent(void);
|
||||||
struct FileStackNode *fstk_GetFileStack(void);
|
struct FileStackNode *fstk_GetFileStack(void);
|
||||||
|
|||||||
@@ -69,6 +69,14 @@ enum LexerMode {
|
|||||||
void lexer_SetMode(enum LexerMode mode);
|
void lexer_SetMode(enum LexerMode mode);
|
||||||
void lexer_ToggleStringExpansion(bool enable);
|
void lexer_ToggleStringExpansion(bool enable);
|
||||||
|
|
||||||
|
uint32_t lexer_GetIFDepth(void);
|
||||||
|
void lexer_IncIFDepth(void);
|
||||||
|
void lexer_DecIFDepth(void);
|
||||||
|
bool lexer_RanIFBlock(void);
|
||||||
|
bool lexer_ReachedELSEBlock(void);
|
||||||
|
void lexer_RunIFBlock(void);
|
||||||
|
void lexer_ReachELSEBlock(void);
|
||||||
|
|
||||||
struct CaptureBody {
|
struct CaptureBody {
|
||||||
uint32_t lineNo;
|
uint32_t lineNo;
|
||||||
char *body;
|
char *body;
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ struct Context {
|
|||||||
struct Context *parent;
|
struct Context *parent;
|
||||||
struct FileStackNode *fileInfo;
|
struct FileStackNode *fileInfo;
|
||||||
struct LexerState *lexerState;
|
struct LexerState *lexerState;
|
||||||
uint32_t nIFDepth;
|
|
||||||
uint32_t uniqueID;
|
uint32_t uniqueID;
|
||||||
struct MacroArgs *macroArgs; /* Macro args are *saved* here */
|
struct MacroArgs *macroArgs; /* Macro args are *saved* here */
|
||||||
uint32_t nbReptIters;
|
uint32_t nbReptIters;
|
||||||
@@ -50,21 +49,6 @@ size_t nMaxRecursionDepth;
|
|||||||
static unsigned int nbIncPaths = 0;
|
static unsigned int nbIncPaths = 0;
|
||||||
static char const *includePaths[MAXINCPATHS];
|
static char const *includePaths[MAXINCPATHS];
|
||||||
|
|
||||||
uint32_t fstk_GetIFDepth(void)
|
|
||||||
{
|
|
||||||
return contextStack->nIFDepth;
|
|
||||||
}
|
|
||||||
|
|
||||||
void fstk_IncIFDepth(void)
|
|
||||||
{
|
|
||||||
contextStack->nIFDepth++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void fstk_DecIFDepth(void)
|
|
||||||
{
|
|
||||||
contextStack->nIFDepth--;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *dumpNodeAndParents(struct FileStackNode const *node)
|
static const char *dumpNodeAndParents(struct FileStackNode const *node)
|
||||||
{
|
{
|
||||||
char const *name;
|
char const *name;
|
||||||
@@ -221,9 +205,11 @@ bool fstk_FindFile(char const *path, char **fullPath, size_t *size)
|
|||||||
|
|
||||||
bool yywrap(void)
|
bool yywrap(void)
|
||||||
{
|
{
|
||||||
if (contextStack->nIFDepth != 0)
|
uint32_t nIFDepth = lexer_GetIFDepth();
|
||||||
|
|
||||||
|
if (nIFDepth != 0)
|
||||||
fatalerror("Ended block with %" PRIu32 " unterminated IF construct%s\n",
|
fatalerror("Ended block with %" PRIu32 " unterminated IF construct%s\n",
|
||||||
contextStack->nIFDepth, contextStack->nIFDepth == 1 ? "" : "s");
|
nIFDepth, nIFDepth == 1 ? "" : "s");
|
||||||
|
|
||||||
if (contextStack->fileInfo->type == NODE_REPT) { /* The context is a REPT block, which may loop */
|
if (contextStack->fileInfo->type == NODE_REPT) { /* The context is a REPT block, which may loop */
|
||||||
struct FileStackReptNode *fileInfo = (struct FileStackReptNode *)contextStack->fileInfo;
|
struct FileStackReptNode *fileInfo = (struct FileStackReptNode *)contextStack->fileInfo;
|
||||||
@@ -295,6 +281,7 @@ bool yywrap(void)
|
|||||||
/*
|
/*
|
||||||
* Make sure not to switch the lexer state before calling this, so the saved line no is correct
|
* Make sure not to switch the lexer state before calling this, so the saved line no is correct
|
||||||
* BE CAREFUL!! This modifies the file stack directly, you should have set up the file info first
|
* BE CAREFUL!! This modifies the file stack directly, you should have set up the file info first
|
||||||
|
* Callers should set contextStack->lexerState after this so it is not NULL
|
||||||
*/
|
*/
|
||||||
static void newContext(struct FileStackNode *fileInfo)
|
static void newContext(struct FileStackNode *fileInfo)
|
||||||
{
|
{
|
||||||
@@ -309,7 +296,6 @@ static void newContext(struct FileStackNode *fileInfo)
|
|||||||
fileInfo->referenced = false;
|
fileInfo->referenced = false;
|
||||||
fileInfo->lineNo = lexer_GetLineNo();
|
fileInfo->lineNo = lexer_GetLineNo();
|
||||||
context->fileInfo = fileInfo;
|
context->fileInfo = fileInfo;
|
||||||
context->nIFDepth = 0;
|
|
||||||
context->forName = NULL;
|
context->forName = NULL;
|
||||||
/*
|
/*
|
||||||
* Link new entry to its parent so it's reachable later
|
* Link new entry to its parent so it's reachable later
|
||||||
@@ -552,7 +538,6 @@ void fstk_Init(char const *mainPath, size_t maxRecursionDepth)
|
|||||||
|
|
||||||
context->parent = NULL;
|
context->parent = NULL;
|
||||||
context->lexerState = state;
|
context->lexerState = state;
|
||||||
context->nIFDepth = 0;
|
|
||||||
context->uniqueID = 0;
|
context->uniqueID = 0;
|
||||||
macro_SetUniqueID(0);
|
macro_SetUniqueID(0);
|
||||||
context->nbReptIters = 0;
|
context->nbReptIters = 0;
|
||||||
|
|||||||
@@ -314,6 +314,12 @@ struct Expansion {
|
|||||||
bool owned; /* Whether or not to free contents when this expansion is freed */
|
bool owned; /* Whether or not to free contents when this expansion is freed */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct IfStack {
|
||||||
|
struct IfStack *next;
|
||||||
|
bool ranIfBlock; /* Whether an IF/ELIF/ELSE block ran already */
|
||||||
|
bool reachedElseBlock; /* Whether an ELSE block ran already */
|
||||||
|
};
|
||||||
|
|
||||||
struct LexerState {
|
struct LexerState {
|
||||||
char const *path;
|
char const *path;
|
||||||
|
|
||||||
@@ -342,6 +348,8 @@ struct LexerState {
|
|||||||
uint32_t lineNo;
|
uint32_t lineNo;
|
||||||
uint32_t colNo;
|
uint32_t colNo;
|
||||||
|
|
||||||
|
struct IfStack *ifStack;
|
||||||
|
|
||||||
bool capturing; /* Whether the text being lexed should be captured */
|
bool capturing; /* Whether the text being lexed should be captured */
|
||||||
size_t captureSize; /* Amount of text captured */
|
size_t captureSize; /* Amount of text captured */
|
||||||
char *captureBuf; /* Buffer to send the captured text to if non-NULL */
|
char *captureBuf; /* Buffer to send the captured text to if non-NULL */
|
||||||
@@ -363,6 +371,8 @@ static void initState(struct LexerState *state)
|
|||||||
state->mode = LEXER_NORMAL;
|
state->mode = LEXER_NORMAL;
|
||||||
state->atLineStart = true; /* yylex() will init colNo due to this */
|
state->atLineStart = true; /* yylex() will init colNo due to this */
|
||||||
|
|
||||||
|
state->ifStack = NULL;
|
||||||
|
|
||||||
state->capturing = false;
|
state->capturing = false;
|
||||||
state->captureBuf = NULL;
|
state->captureBuf = NULL;
|
||||||
|
|
||||||
@@ -380,6 +390,62 @@ static void nextLine(void)
|
|||||||
lexerState->colNo = 1;
|
lexerState->colNo = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t lexer_GetIFDepth(void)
|
||||||
|
{
|
||||||
|
uint32_t depth = 0;
|
||||||
|
|
||||||
|
for (struct IfStack *stack = lexerState->ifStack; stack != NULL; stack = stack->next)
|
||||||
|
depth++;
|
||||||
|
|
||||||
|
return depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
void lexer_IncIFDepth(void)
|
||||||
|
{
|
||||||
|
struct IfStack *new = malloc(sizeof(*new));
|
||||||
|
|
||||||
|
if (!new)
|
||||||
|
fatalerror("Unable to allocate new IF depth: %s\n", strerror(errno));
|
||||||
|
|
||||||
|
new->ranIfBlock = false;
|
||||||
|
new->reachedElseBlock = false;
|
||||||
|
new->next = lexerState->ifStack;
|
||||||
|
|
||||||
|
lexerState->ifStack = new;
|
||||||
|
}
|
||||||
|
|
||||||
|
void lexer_DecIFDepth(void)
|
||||||
|
{
|
||||||
|
if (!lexerState->ifStack)
|
||||||
|
fatalerror("Found ENDC outside an IF construct\n");
|
||||||
|
|
||||||
|
struct IfStack *top = lexerState->ifStack->next;
|
||||||
|
|
||||||
|
free(lexerState->ifStack);
|
||||||
|
|
||||||
|
lexerState->ifStack = top;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool lexer_RanIFBlock(void)
|
||||||
|
{
|
||||||
|
return lexerState->ifStack->ranIfBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool lexer_ReachedELSEBlock(void)
|
||||||
|
{
|
||||||
|
return lexerState->ifStack->reachedElseBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
void lexer_RunIFBlock(void)
|
||||||
|
{
|
||||||
|
lexerState->ifStack->ranIfBlock = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void lexer_ReachELSEBlock(void)
|
||||||
|
{
|
||||||
|
lexerState->ifStack->reachedElseBlock = true;
|
||||||
|
}
|
||||||
|
|
||||||
struct LexerState *lexer_OpenFile(char const *path)
|
struct LexerState *lexer_OpenFile(char const *path)
|
||||||
{
|
{
|
||||||
dbgPrint("Opening file \"%s\"\n", path);
|
dbgPrint("Opening file \"%s\"\n", path);
|
||||||
@@ -2161,7 +2227,7 @@ static int skipIfBlock(bool toEndc)
|
|||||||
{
|
{
|
||||||
dbgPrint("Skipping IF block (toEndc = %s)\n", toEndc ? "true" : "false");
|
dbgPrint("Skipping IF block (toEndc = %s)\n", toEndc ? "true" : "false");
|
||||||
lexer_SetMode(LEXER_NORMAL);
|
lexer_SetMode(LEXER_NORMAL);
|
||||||
int startingDepth = fstk_GetIFDepth();
|
int startingDepth = lexer_GetIFDepth();
|
||||||
int token;
|
int token;
|
||||||
bool atLineStart = lexerState->atLineStart;
|
bool atLineStart = lexerState->atLineStart;
|
||||||
|
|
||||||
@@ -2185,19 +2251,28 @@ static int skipIfBlock(bool toEndc)
|
|||||||
token = readIdentifier(c);
|
token = readIdentifier(c);
|
||||||
switch (token) {
|
switch (token) {
|
||||||
case T_POP_IF:
|
case T_POP_IF:
|
||||||
fstk_IncIFDepth();
|
lexer_IncIFDepth();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case T_POP_ELIF:
|
case T_POP_ELIF:
|
||||||
|
if (lexer_ReachedELSEBlock())
|
||||||
|
fatalerror("Found ELIF after an ELSE block\n");
|
||||||
|
goto maybeFinish;
|
||||||
|
|
||||||
case T_POP_ELSE:
|
case T_POP_ELSE:
|
||||||
|
if (lexer_ReachedELSEBlock())
|
||||||
|
fatalerror("Found ELSE after an ELSE block\n");
|
||||||
|
lexer_ReachELSEBlock();
|
||||||
|
/* fallthrough */
|
||||||
|
maybeFinish:
|
||||||
if (toEndc) /* Ignore ELIF and ELSE, go to ENDC */
|
if (toEndc) /* Ignore ELIF and ELSE, go to ENDC */
|
||||||
break;
|
break;
|
||||||
/* fallthrough */
|
/* fallthrough */
|
||||||
case T_POP_ENDC:
|
case T_POP_ENDC:
|
||||||
if (fstk_GetIFDepth() == startingDepth)
|
if (lexer_GetIFDepth() == startingDepth)
|
||||||
goto finish;
|
goto finish;
|
||||||
if (token == T_POP_ENDC)
|
if (token == T_POP_ENDC)
|
||||||
fstk_DecIFDepth();
|
lexer_DecIFDepth();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
atLineStart = false;
|
atLineStart = false;
|
||||||
@@ -2282,11 +2357,11 @@ static int yylex_SKIP_TO_ENDR(void)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case T_POP_IF:
|
case T_POP_IF:
|
||||||
fstk_IncIFDepth();
|
lexer_IncIFDepth();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case T_POP_ENDC:
|
case T_POP_ENDC:
|
||||||
fstk_DecIFDepth();
|
lexer_DecIFDepth();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
atLineStart = false;
|
atLineStart = false;
|
||||||
|
|||||||
@@ -36,7 +36,6 @@
|
|||||||
#include "linkdefs.h"
|
#include "linkdefs.h"
|
||||||
#include "platform.h" // strncasecmp, strdup
|
#include "platform.h" // strncasecmp, strdup
|
||||||
|
|
||||||
static bool executeElseBlock; /* If this is set, ELIFs cannot be executed anymore */
|
|
||||||
static struct CaptureBody captureBody; /* Captures a REPT/FOR or MACRO */
|
static struct CaptureBody captureBody; /* Captures a REPT/FOR or MACRO */
|
||||||
|
|
||||||
static void upperstring(char *dest, char const *src)
|
static void upperstring(char *dest, char const *src)
|
||||||
@@ -654,42 +653,50 @@ line_directive : macrodef
|
|||||||
;
|
;
|
||||||
|
|
||||||
if : T_POP_IF const T_NEWLINE {
|
if : T_POP_IF const T_NEWLINE {
|
||||||
fstk_IncIFDepth();
|
lexer_IncIFDepth();
|
||||||
executeElseBlock = !$2;
|
|
||||||
if (executeElseBlock)
|
if ($2)
|
||||||
|
lexer_RunIFBlock();
|
||||||
|
else
|
||||||
lexer_SetMode(LEXER_SKIP_TO_ELIF);
|
lexer_SetMode(LEXER_SKIP_TO_ELIF);
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
elif : T_POP_ELIF const T_NEWLINE {
|
elif : T_POP_ELIF const T_NEWLINE {
|
||||||
if (fstk_GetIFDepth() == 0)
|
if (lexer_GetIFDepth() == 0)
|
||||||
fatalerror("Found ELIF outside an IF construct\n");
|
fatalerror("Found ELIF outside an IF construct\n");
|
||||||
|
|
||||||
if (!executeElseBlock) {
|
if (lexer_RanIFBlock()) {
|
||||||
|
if (lexer_ReachedELSEBlock())
|
||||||
|
fatalerror("Found ELIF after an ELSE block\n");
|
||||||
|
|
||||||
lexer_SetMode(LEXER_SKIP_TO_ENDC);
|
lexer_SetMode(LEXER_SKIP_TO_ENDC);
|
||||||
|
} else if ($2) {
|
||||||
|
lexer_RunIFBlock();
|
||||||
} else {
|
} else {
|
||||||
executeElseBlock = !$2;
|
lexer_SetMode(LEXER_SKIP_TO_ELIF);
|
||||||
if (executeElseBlock)
|
|
||||||
lexer_SetMode(LEXER_SKIP_TO_ELIF);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
else : T_POP_ELSE T_NEWLINE {
|
else : T_POP_ELSE T_NEWLINE {
|
||||||
if (fstk_GetIFDepth() == 0)
|
if (lexer_GetIFDepth() == 0)
|
||||||
fatalerror("Found ELSE outside an IF construct\n");
|
fatalerror("Found ELSE outside an IF construct\n");
|
||||||
|
|
||||||
if (!executeElseBlock)
|
if (lexer_RanIFBlock()) {
|
||||||
|
if (lexer_ReachedELSEBlock())
|
||||||
|
fatalerror("Found ELSE after an ELSE block\n");
|
||||||
|
|
||||||
lexer_SetMode(LEXER_SKIP_TO_ENDC);
|
lexer_SetMode(LEXER_SKIP_TO_ENDC);
|
||||||
|
} else {
|
||||||
|
lexer_RunIFBlock();
|
||||||
|
lexer_ReachELSEBlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
endc : T_POP_ENDC {
|
endc : T_POP_ENDC {
|
||||||
if (fstk_GetIFDepth() == 0)
|
lexer_DecIFDepth();
|
||||||
fatalerror("Found ENDC outside an IF construct\n");
|
|
||||||
|
|
||||||
fstk_DecIFDepth();
|
|
||||||
executeElseBlock = false;
|
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|||||||
18
test/asm/elif-after-else.asm
Normal file
18
test/asm/elif-after-else.asm
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
if 0
|
||||||
|
println "zero"
|
||||||
|
else
|
||||||
|
println "one"
|
||||||
|
if 1
|
||||||
|
println "A"
|
||||||
|
else
|
||||||
|
println "B"
|
||||||
|
elif 2
|
||||||
|
println "C"
|
||||||
|
else
|
||||||
|
println "D"
|
||||||
|
endc
|
||||||
|
elif 2
|
||||||
|
println "two"
|
||||||
|
else
|
||||||
|
println "three"
|
||||||
|
endc
|
||||||
2
test/asm/elif-after-else.err
Normal file
2
test/asm/elif-after-else.err
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
FATAL: elif-after-else.asm(14):
|
||||||
|
Found ELIF after an ELSE block
|
||||||
2
test/asm/elif-after-else.out
Normal file
2
test/asm/elif-after-else.out
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
one
|
||||||
|
A
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
FATAL: multiple-else.asm(11):
|
||||||
|
Found ELSE after an ELSE block
|
||||||
|
|||||||
17
test/asm/skip-elif-condition.asm
Normal file
17
test/asm/skip-elif-condition.asm
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
mac: MACRO
|
||||||
|
if (\1) < 10
|
||||||
|
println "small \1"
|
||||||
|
elif (\1) > 100
|
||||||
|
println "large \1"
|
||||||
|
elif (\1) / 0 == 42 ; only evaluated if the "large" condition was taken
|
||||||
|
println "division by zero!?"
|
||||||
|
elif syntax! error?
|
||||||
|
println "X_X"
|
||||||
|
else
|
||||||
|
println "unreachable"
|
||||||
|
endc
|
||||||
|
ENDM
|
||||||
|
|
||||||
|
mac 2 + 2
|
||||||
|
mac STRLEN("abcdef")
|
||||||
|
mac 101
|
||||||
2
test/asm/skip-elif-condition.err
Normal file
2
test/asm/skip-elif-condition.err
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
FATAL: skip-elif-condition.asm(17) -> skip-elif-condition.asm::mac(6):
|
||||||
|
Division by zero
|
||||||
3
test/asm/skip-elif-condition.out
Normal file
3
test/asm/skip-elif-condition.out
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
small 2 + 2
|
||||||
|
small STRLEN("abcdef")
|
||||||
|
large 101
|
||||||
Reference in New Issue
Block a user