Prevent ELIF or ELSE after an ELSE

Fixes #749
This commit is contained in:
Rangi
2021-02-24 21:19:08 -05:00
committed by Eldred Habert
parent dd892d61d8
commit d4028fff10
12 changed files with 163 additions and 45 deletions

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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();
}
;

View 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

View File

@@ -0,0 +1,2 @@
FATAL: elif-after-else.asm(14):
Found ELIF after an ELSE block

View File

@@ -0,0 +1,2 @@
one
A

View File

@@ -0,0 +1,2 @@
FATAL: multiple-else.asm(11):
Found ELSE after an ELSE block

View 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

View File

@@ -0,0 +1,2 @@
FATAL: skip-elif-condition.asm(17) -> skip-elif-condition.asm::mac(6):
Division by zero

View File

@@ -0,0 +1,3 @@
small 2 + 2
small STRLEN("abcdef")
large 101