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;
|
||||
|
||||
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_DumpCurrent(void);
|
||||
struct FileStackNode *fstk_GetFileStack(void);
|
||||
|
||||
@@ -69,6 +69,14 @@ enum LexerMode {
|
||||
void lexer_SetMode(enum LexerMode mode);
|
||||
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 {
|
||||
uint32_t lineNo;
|
||||
char *body;
|
||||
|
||||
@@ -33,7 +33,6 @@ struct Context {
|
||||
struct Context *parent;
|
||||
struct FileStackNode *fileInfo;
|
||||
struct LexerState *lexerState;
|
||||
uint32_t nIFDepth;
|
||||
uint32_t uniqueID;
|
||||
struct MacroArgs *macroArgs; /* Macro args are *saved* here */
|
||||
uint32_t nbReptIters;
|
||||
@@ -50,21 +49,6 @@ size_t nMaxRecursionDepth;
|
||||
static unsigned int nbIncPaths = 0;
|
||||
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)
|
||||
{
|
||||
char const *name;
|
||||
@@ -221,9 +205,11 @@ bool fstk_FindFile(char const *path, char **fullPath, size_t *size)
|
||||
|
||||
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",
|
||||
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 */
|
||||
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
|
||||
* 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)
|
||||
{
|
||||
@@ -309,7 +296,6 @@ static void newContext(struct FileStackNode *fileInfo)
|
||||
fileInfo->referenced = false;
|
||||
fileInfo->lineNo = lexer_GetLineNo();
|
||||
context->fileInfo = fileInfo;
|
||||
context->nIFDepth = 0;
|
||||
context->forName = NULL;
|
||||
/*
|
||||
* 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->lexerState = state;
|
||||
context->nIFDepth = 0;
|
||||
context->uniqueID = 0;
|
||||
macro_SetUniqueID(0);
|
||||
context->nbReptIters = 0;
|
||||
|
||||
@@ -314,6 +314,12 @@ struct Expansion {
|
||||
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 {
|
||||
char const *path;
|
||||
|
||||
@@ -342,6 +348,8 @@ struct LexerState {
|
||||
uint32_t lineNo;
|
||||
uint32_t colNo;
|
||||
|
||||
struct IfStack *ifStack;
|
||||
|
||||
bool capturing; /* Whether the text being lexed should be captured */
|
||||
size_t captureSize; /* Amount of text captured */
|
||||
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->atLineStart = true; /* yylex() will init colNo due to this */
|
||||
|
||||
state->ifStack = NULL;
|
||||
|
||||
state->capturing = false;
|
||||
state->captureBuf = NULL;
|
||||
|
||||
@@ -380,6 +390,62 @@ static void nextLine(void)
|
||||
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)
|
||||
{
|
||||
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");
|
||||
lexer_SetMode(LEXER_NORMAL);
|
||||
int startingDepth = fstk_GetIFDepth();
|
||||
int startingDepth = lexer_GetIFDepth();
|
||||
int token;
|
||||
bool atLineStart = lexerState->atLineStart;
|
||||
|
||||
@@ -2185,19 +2251,28 @@ static int skipIfBlock(bool toEndc)
|
||||
token = readIdentifier(c);
|
||||
switch (token) {
|
||||
case T_POP_IF:
|
||||
fstk_IncIFDepth();
|
||||
lexer_IncIFDepth();
|
||||
break;
|
||||
|
||||
case T_POP_ELIF:
|
||||
if (lexer_ReachedELSEBlock())
|
||||
fatalerror("Found ELIF after an ELSE block\n");
|
||||
goto maybeFinish;
|
||||
|
||||
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 */
|
||||
break;
|
||||
/* fallthrough */
|
||||
case T_POP_ENDC:
|
||||
if (fstk_GetIFDepth() == startingDepth)
|
||||
if (lexer_GetIFDepth() == startingDepth)
|
||||
goto finish;
|
||||
if (token == T_POP_ENDC)
|
||||
fstk_DecIFDepth();
|
||||
lexer_DecIFDepth();
|
||||
}
|
||||
}
|
||||
atLineStart = false;
|
||||
@@ -2282,11 +2357,11 @@ static int yylex_SKIP_TO_ENDR(void)
|
||||
break;
|
||||
|
||||
case T_POP_IF:
|
||||
fstk_IncIFDepth();
|
||||
lexer_IncIFDepth();
|
||||
break;
|
||||
|
||||
case T_POP_ENDC:
|
||||
fstk_DecIFDepth();
|
||||
lexer_DecIFDepth();
|
||||
}
|
||||
}
|
||||
atLineStart = false;
|
||||
|
||||
@@ -36,7 +36,6 @@
|
||||
#include "linkdefs.h"
|
||||
#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 void upperstring(char *dest, char const *src)
|
||||
@@ -654,42 +653,50 @@ line_directive : macrodef
|
||||
;
|
||||
|
||||
if : T_POP_IF const T_NEWLINE {
|
||||
fstk_IncIFDepth();
|
||||
executeElseBlock = !$2;
|
||||
if (executeElseBlock)
|
||||
lexer_IncIFDepth();
|
||||
|
||||
if ($2)
|
||||
lexer_RunIFBlock();
|
||||
else
|
||||
lexer_SetMode(LEXER_SKIP_TO_ELIF);
|
||||
}
|
||||
;
|
||||
|
||||
elif : T_POP_ELIF const T_NEWLINE {
|
||||
if (fstk_GetIFDepth() == 0)
|
||||
if (lexer_GetIFDepth() == 0)
|
||||
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);
|
||||
} else if ($2) {
|
||||
lexer_RunIFBlock();
|
||||
} else {
|
||||
executeElseBlock = !$2;
|
||||
if (executeElseBlock)
|
||||
lexer_SetMode(LEXER_SKIP_TO_ELIF);
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
else : T_POP_ELSE T_NEWLINE {
|
||||
if (fstk_GetIFDepth() == 0)
|
||||
if (lexer_GetIFDepth() == 0)
|
||||
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);
|
||||
} else {
|
||||
lexer_RunIFBlock();
|
||||
lexer_ReachELSEBlock();
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
endc : T_POP_ENDC {
|
||||
if (fstk_GetIFDepth() == 0)
|
||||
fatalerror("Found ENDC outside an IF construct\n");
|
||||
|
||||
fstk_DecIFDepth();
|
||||
executeElseBlock = false;
|
||||
lexer_DecIFDepth();
|
||||
}
|
||||
;
|
||||
|
||||
|
||||
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