mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 10:12:06 +00:00
Allow changing recursion depth limit at runtime
This commit is contained in:
@@ -77,6 +77,7 @@ void fstk_RunFor(char const *symName, int32_t start, int32_t stop, int32_t step,
|
||||
void fstk_StopRept(void);
|
||||
bool fstk_Break(void);
|
||||
|
||||
void fstk_NewRecursionDepth(size_t newDepth);
|
||||
void fstk_Init(char const *mainPath, size_t maxDepth);
|
||||
|
||||
#endif /* RGBDS_ASM_FSTACK_H */
|
||||
|
||||
@@ -83,6 +83,7 @@ struct CaptureBody {
|
||||
size_t size;
|
||||
};
|
||||
|
||||
void lexer_CheckRecursionDepth(void);
|
||||
char const *lexer_GetFileName(void);
|
||||
uint32_t lexer_GetLineNo(void);
|
||||
uint32_t lexer_GetColNo(void);
|
||||
|
||||
@@ -284,8 +284,9 @@ bool yywrap(void)
|
||||
*/
|
||||
static void newContext(struct FileStackNode *fileInfo)
|
||||
{
|
||||
if (++contextDepth >= maxRecursionDepth)
|
||||
fatalerror("Recursion limit (%zu) exceeded\n", maxRecursionDepth);
|
||||
++contextDepth;
|
||||
fstk_NewRecursionDepth(maxRecursionDepth); // Only checks if the max depth was exceeded
|
||||
|
||||
struct Context *context = malloc(sizeof(*context));
|
||||
|
||||
if (!context)
|
||||
@@ -507,6 +508,13 @@ bool fstk_Break(void)
|
||||
return true;
|
||||
}
|
||||
|
||||
void fstk_NewRecursionDepth(size_t newDepth)
|
||||
{
|
||||
if (contextDepth >= newDepth)
|
||||
fatalerror("Recursion limit (%zu) exceeded\n", newDepth);
|
||||
maxRecursionDepth = newDepth;
|
||||
}
|
||||
|
||||
void fstk_Init(char const *mainPath, size_t maxDepth)
|
||||
{
|
||||
struct LexerState *state = lexer_OpenFile(mainPath);
|
||||
|
||||
@@ -668,14 +668,8 @@ static void beginExpansion(char const *str, bool owned, char const *name)
|
||||
if (!size)
|
||||
return;
|
||||
|
||||
if (name) {
|
||||
size_t depth = 0;
|
||||
|
||||
for (struct Expansion *exp = lexerState->expansions; exp; exp = exp->parent) {
|
||||
if (depth++ >= maxRecursionDepth)
|
||||
fatalerror("Recursion limit (%zu) exceeded\n", maxRecursionDepth);
|
||||
}
|
||||
}
|
||||
if (name)
|
||||
lexer_CheckRecursionDepth();
|
||||
|
||||
struct Expansion *exp = malloc(sizeof(*exp));
|
||||
|
||||
@@ -692,6 +686,16 @@ static void beginExpansion(char const *str, bool owned, char const *name)
|
||||
lexerState->expansions = exp;
|
||||
}
|
||||
|
||||
void lexer_CheckRecursionDepth(void)
|
||||
{
|
||||
size_t depth = 0;
|
||||
|
||||
for (struct Expansion *exp = lexerState->expansions; exp; exp = exp->parent) {
|
||||
if (depth++ >= maxRecursionDepth)
|
||||
fatalerror("Recursion limit (%zu) exceeded\n", maxRecursionDepth);
|
||||
}
|
||||
}
|
||||
|
||||
static void freeExpansion(struct Expansion *expansion)
|
||||
{
|
||||
free(expansion->name);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
@@ -5,6 +6,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "asm/fstack.h"
|
||||
#include "asm/lexer.h"
|
||||
#include "asm/main.h"
|
||||
#include "asm/section.h"
|
||||
@@ -17,6 +19,7 @@ struct OptStackEntry {
|
||||
bool haltnop;
|
||||
bool optimizeLoads;
|
||||
bool warningsAreErrors;
|
||||
size_t maxRecursionDepth;
|
||||
// Don't be confused: we use the size of the **global variable** `warningStates`!
|
||||
enum WarningState warningStates[sizeof(warningStates)];
|
||||
struct OptStackEntry *next;
|
||||
@@ -39,6 +42,12 @@ void opt_P(uint8_t fill)
|
||||
fillByte = fill;
|
||||
}
|
||||
|
||||
void opt_R(size_t newDepth)
|
||||
{
|
||||
fstk_NewRecursionDepth(newDepth);
|
||||
lexer_CheckRecursionDepth();
|
||||
}
|
||||
|
||||
void opt_h(bool halt)
|
||||
{
|
||||
haltnop = halt;
|
||||
@@ -86,6 +95,29 @@ void opt_Parse(char *s)
|
||||
}
|
||||
break;
|
||||
|
||||
case 'r': {
|
||||
++s; // Skip 'r'
|
||||
while (isblank(*s))
|
||||
++s; // Skip leading whitespace
|
||||
|
||||
if (s[0] == '\0') {
|
||||
error("Missing argument to option 'r'\n");
|
||||
break;
|
||||
}
|
||||
|
||||
char *endptr;
|
||||
unsigned long newDepth = strtoul(s, &endptr, 10);
|
||||
|
||||
if (*endptr != '\0') {
|
||||
error("Invalid argument to option 'r' (\"%s\")\n", s);
|
||||
} else if (errno == ERANGE) {
|
||||
error("Argument to 'r' is out of range (\"%s\")\n", s);
|
||||
} else {
|
||||
opt_R(newDepth);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'h':
|
||||
if (s[1] == '\0')
|
||||
opt_h(false);
|
||||
|
||||
@@ -1993,6 +1993,7 @@ block, all of them but the first one are ignored.
|
||||
.Ss Changing options while assembling
|
||||
.Ic OPT
|
||||
can be used to change some of the options during assembling from within the source, instead of defining them on the command-line.
|
||||
.Pq See Xr rgbasm 1 .
|
||||
.Pp
|
||||
.Ic OPT
|
||||
takes a comma-separated list of options as its argument:
|
||||
@@ -2008,8 +2009,10 @@ POPO
|
||||
LD [$FF88], A ; optimized to use LDH by default
|
||||
.Ed
|
||||
.Pp
|
||||
The options that OPT can modify are currently:
|
||||
.Cm b , g , p , h , L ,
|
||||
The options that
|
||||
.Ic OPT
|
||||
can modify are currently:
|
||||
.Cm b , g , p , r , h , L ,
|
||||
and
|
||||
.Cm W .
|
||||
The Boolean flag options
|
||||
|
||||
13
test/asm/opt-r.asm
Normal file
13
test/asm/opt-r.asm
Normal file
@@ -0,0 +1,13 @@
|
||||
OPT r34 ; :3
|
||||
OPT r 360
|
||||
|
||||
; Invalid
|
||||
OPT r ; Missing arg
|
||||
OPT r 2a ; Bad decimal
|
||||
|
||||
; Check that it has an effect
|
||||
OPT r 1
|
||||
MACRO m
|
||||
m
|
||||
ENDM
|
||||
m
|
||||
6
test/asm/opt-r.err
Normal file
6
test/asm/opt-r.err
Normal file
@@ -0,0 +1,6 @@
|
||||
error: opt-r.asm(5):
|
||||
Missing argument to option 'r'
|
||||
error: opt-r.asm(6):
|
||||
Invalid argument to option 'r' ("2a")
|
||||
FATAL: opt-r.asm(13):
|
||||
Recursion limit (1) exceeded
|
||||
0
test/asm/opt-r.out
Normal file
0
test/asm/opt-r.out
Normal file
Reference in New Issue
Block a user