From d4028fff102a0fd1cd3d522b8483e3b5a2a7254a Mon Sep 17 00:00:00 2001 From: Rangi Date: Wed, 24 Feb 2021 21:19:08 -0500 Subject: [PATCH] Prevent ELIF or ELSE after an ELSE Fixes #749 --- include/asm/fstack.h | 3 -- include/asm/lexer.h | 8 +++ src/asm/fstack.c | 25 ++------- src/asm/lexer.c | 87 +++++++++++++++++++++++++++++--- src/asm/parser.y | 39 ++++++++------ test/asm/elif-after-else.asm | 18 +++++++ test/asm/elif-after-else.err | 2 + test/asm/elif-after-else.out | 2 + test/asm/multiple-else.err | 2 + test/asm/skip-elif-condition.asm | 17 +++++++ test/asm/skip-elif-condition.err | 2 + test/asm/skip-elif-condition.out | 3 ++ 12 files changed, 163 insertions(+), 45 deletions(-) create mode 100644 test/asm/elif-after-else.asm create mode 100644 test/asm/elif-after-else.err create mode 100644 test/asm/elif-after-else.out create mode 100644 test/asm/skip-elif-condition.asm create mode 100644 test/asm/skip-elif-condition.err create mode 100644 test/asm/skip-elif-condition.out diff --git a/include/asm/fstack.h b/include/asm/fstack.h index 87870819..ca9ca16b 100644 --- a/include/asm/fstack.h +++ b/include/asm/fstack.h @@ -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); diff --git a/include/asm/lexer.h b/include/asm/lexer.h index fccdc2dc..73057d88 100644 --- a/include/asm/lexer.h +++ b/include/asm/lexer.h @@ -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; diff --git a/src/asm/fstack.c b/src/asm/fstack.c index a938f6bb..c04950b7 100644 --- a/src/asm/fstack.c +++ b/src/asm/fstack.c @@ -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; diff --git a/src/asm/lexer.c b/src/asm/lexer.c index 3d2bcf49..c55a3be1 100644 --- a/src/asm/lexer.c +++ b/src/asm/lexer.c @@ -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; diff --git a/src/asm/parser.y b/src/asm/parser.y index e2a96021..c0ef3d09 100644 --- 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 @@ 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); + 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(); } ; diff --git a/test/asm/elif-after-else.asm b/test/asm/elif-after-else.asm new file mode 100644 index 00000000..fd9294d3 --- /dev/null +++ b/test/asm/elif-after-else.asm @@ -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 diff --git a/test/asm/elif-after-else.err b/test/asm/elif-after-else.err new file mode 100644 index 00000000..e4683db5 --- /dev/null +++ b/test/asm/elif-after-else.err @@ -0,0 +1,2 @@ +FATAL: elif-after-else.asm(14): + Found ELIF after an ELSE block diff --git a/test/asm/elif-after-else.out b/test/asm/elif-after-else.out new file mode 100644 index 00000000..2047852c --- /dev/null +++ b/test/asm/elif-after-else.out @@ -0,0 +1,2 @@ +one +A diff --git a/test/asm/multiple-else.err b/test/asm/multiple-else.err index e69de29b..76d7b41a 100644 --- a/test/asm/multiple-else.err +++ b/test/asm/multiple-else.err @@ -0,0 +1,2 @@ +FATAL: multiple-else.asm(11): + Found ELSE after an ELSE block diff --git a/test/asm/skip-elif-condition.asm b/test/asm/skip-elif-condition.asm new file mode 100644 index 00000000..1eae498d --- /dev/null +++ b/test/asm/skip-elif-condition.asm @@ -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 diff --git a/test/asm/skip-elif-condition.err b/test/asm/skip-elif-condition.err new file mode 100644 index 00000000..7001c8d9 --- /dev/null +++ b/test/asm/skip-elif-condition.err @@ -0,0 +1,2 @@ +FATAL: skip-elif-condition.asm(17) -> skip-elif-condition.asm::mac(6): + Division by zero diff --git a/test/asm/skip-elif-condition.out b/test/asm/skip-elif-condition.out new file mode 100644 index 00000000..00288fc6 --- /dev/null +++ b/test/asm/skip-elif-condition.out @@ -0,0 +1,3 @@ +small 2 + 2 +small STRLEN("abcdef") +large 101