shithub: rgbds

Download patch

ref: d4028fff102a0fd1cd3d522b8483e3b5a2a7254a
parent: dd892d61d8a8b3b7d9b51eaeba167bb8fc68df4c
author: Rangi <[email protected]>
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