ref: d4028fff102a0fd1cd3d522b8483e3b5a2a7254a
parent: dd892d61d8a8b3b7d9b51eaeba167bb8fc68df4c
author: Rangi <remy.oukaour+rangi42@gmail.com>
date: Wed Feb 24 16:19:08 EST 2021
Prevent ELIF or ELSE after an ELSE Fixes #749
--- a/include/asm/fstack.h
+++ b/include/asm/fstack.h
@@ -53,9 +53,6 @@
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);
--- a/include/asm/lexer.h
+++ b/include/asm/lexer.h
@@ -69,6 +69,14 @@
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;
--- a/src/asm/fstack.c
+++ b/src/asm/fstack.c
@@ -33,7 +33,6 @@
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 @@
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 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 @@
/*
* 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 @@
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 @@
context->parent = NULL;
context->lexerState = state;
- context->nIFDepth = 0;
context->uniqueID = 0;
macro_SetUniqueID(0);
context->nbReptIters = 0;
--- a/src/asm/lexer.c
+++ b/src/asm/lexer.c
@@ -314,6 +314,12 @@
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 @@
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 @@
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 @@
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 @@
{
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 @@
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 @@
break;
case T_POP_IF:
- fstk_IncIFDepth();
+ lexer_IncIFDepth();
break;
case T_POP_ENDC:
- fstk_DecIFDepth();
+ lexer_DecIFDepth();
}
}
atLineStart = false;
--- a/src/asm/parser.y
+++ b/src/asm/parser.y
@@ -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 @@
;
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);
+ 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();
}
;
--- /dev/null
+++ b/test/asm/elif-after-else.asm
@@ -1,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
--- /dev/null
+++ b/test/asm/elif-after-else.err
@@ -1,0 +1,2 @@
+FATAL: elif-after-else.asm(14):
+ Found ELIF after an ELSE block
--- /dev/null
+++ b/test/asm/elif-after-else.out
@@ -1,0 +1,2 @@
+one
+A
--- a/test/asm/multiple-else.err
+++ b/test/asm/multiple-else.err
@@ -1,0 +1,2 @@
+FATAL: multiple-else.asm(11):
+ Found ELSE after an ELSE block
--- /dev/null
+++ b/test/asm/skip-elif-condition.asm
@@ -1,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
--- /dev/null
+++ b/test/asm/skip-elif-condition.err
@@ -1,0 +1,2 @@
+FATAL: skip-elif-condition.asm(17) -> skip-elif-condition.asm::mac(6):
+ Division by zero
--- /dev/null
+++ b/test/asm/skip-elif-condition.out
@@ -1,0 +1,3 @@
+small 2 + 2
+small STRLEN("abcdef")
+large 101