mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 18:22:07 +00:00
@@ -75,6 +75,7 @@ void fstk_RunMacro(char const *macroName, struct MacroArgs *args);
|
|||||||
void fstk_RunRept(uint32_t count, int32_t nReptLineNo, char *body, size_t size);
|
void fstk_RunRept(uint32_t count, int32_t nReptLineNo, char *body, size_t size);
|
||||||
void fstk_RunFor(char const *symName, int32_t start, int32_t stop, int32_t step,
|
void fstk_RunFor(char const *symName, int32_t start, int32_t stop, int32_t step,
|
||||||
int32_t reptLineNo, char *body, size_t size);
|
int32_t reptLineNo, char *body, size_t size);
|
||||||
|
bool fstk_Break(void);
|
||||||
|
|
||||||
void fstk_Init(char const *mainPath, size_t maxRecursionDepth);
|
void fstk_Init(char const *mainPath, size_t maxRecursionDepth);
|
||||||
|
|
||||||
|
|||||||
@@ -56,7 +56,8 @@ enum LexerMode {
|
|||||||
LEXER_NORMAL,
|
LEXER_NORMAL,
|
||||||
LEXER_RAW,
|
LEXER_RAW,
|
||||||
LEXER_SKIP_TO_ELIF,
|
LEXER_SKIP_TO_ELIF,
|
||||||
LEXER_SKIP_TO_ENDC
|
LEXER_SKIP_TO_ENDC,
|
||||||
|
LEXER_SKIP_TO_ENDR
|
||||||
};
|
};
|
||||||
|
|
||||||
void lexer_SetMode(enum LexerMode mode);
|
void lexer_SetMode(enum LexerMode mode);
|
||||||
|
|||||||
@@ -219,16 +219,18 @@ bool yywrap(void)
|
|||||||
contextStack->fileInfo = (struct FileStackNode *)fileInfo;
|
contextStack->fileInfo = (struct FileStackNode *)fileInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
fileInfo->iters[0]++;
|
|
||||||
/* If this is a FOR, update the symbol value */
|
/* If this is a FOR, update the symbol value */
|
||||||
if (contextStack->forName) {
|
if (contextStack->forName && fileInfo->iters[0] <= contextStack->nbReptIters) {
|
||||||
contextStack->forValue += contextStack->forStep;
|
contextStack->forValue += contextStack->forStep;
|
||||||
struct Symbol *sym = sym_AddSet(contextStack->forName,
|
struct Symbol *sym = sym_AddSet(contextStack->forName,
|
||||||
contextStack->forValue);
|
contextStack->forValue);
|
||||||
|
|
||||||
|
/* This error message will refer to the current iteration */
|
||||||
if (sym->type != SYM_SET)
|
if (sym->type != SYM_SET)
|
||||||
fatalerror("Failed to update FOR symbol value\n");
|
fatalerror("Failed to update FOR symbol value\n");
|
||||||
}
|
}
|
||||||
|
/* Advance to the next iteration */
|
||||||
|
fileInfo->iters[0]++;
|
||||||
/* If this wasn't the last iteration, wrap instead of popping */
|
/* If this wasn't the last iteration, wrap instead of popping */
|
||||||
if (fileInfo->iters[0] <= contextStack->nbReptIters) {
|
if (fileInfo->iters[0] <= contextStack->nbReptIters) {
|
||||||
lexer_RestartRept(contextStack->fileInfo->lineNo);
|
lexer_RestartRept(contextStack->fileInfo->lineNo);
|
||||||
@@ -479,6 +481,20 @@ void fstk_RunFor(char const *symName, int32_t start, int32_t stop, int32_t step,
|
|||||||
fatalerror("Not enough memory for FOR symbol name: %s\n", strerror(errno));
|
fatalerror("Not enough memory for FOR symbol name: %s\n", strerror(errno));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool fstk_Break(void)
|
||||||
|
{
|
||||||
|
dbgPrint("Breaking out of REPT/FOR\n");
|
||||||
|
|
||||||
|
if (contextStack->fileInfo->type != NODE_REPT) {
|
||||||
|
error("BREAK can only be used inside a REPT/FOR block\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Prevent more iterations */
|
||||||
|
contextStack->nbReptIters = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void fstk_Init(char const *mainPath, size_t maxRecursionDepth)
|
void fstk_Init(char const *mainPath, size_t maxRecursionDepth)
|
||||||
{
|
{
|
||||||
struct LexerState *state = lexer_OpenFile(mainPath);
|
struct LexerState *state = lexer_OpenFile(mainPath);
|
||||||
|
|||||||
@@ -251,6 +251,7 @@ static struct KeywordMapping {
|
|||||||
{"REPT", T_POP_REPT},
|
{"REPT", T_POP_REPT},
|
||||||
{"FOR", T_POP_FOR},
|
{"FOR", T_POP_FOR},
|
||||||
{"ENDR", T_POP_ENDR},
|
{"ENDR", T_POP_ENDR},
|
||||||
|
{"BREAK", T_POP_BREAK},
|
||||||
|
|
||||||
{"LOAD", T_POP_LOAD},
|
{"LOAD", T_POP_LOAD},
|
||||||
{"ENDL", T_POP_ENDL},
|
{"ENDL", T_POP_ENDL},
|
||||||
@@ -498,7 +499,7 @@ struct KeywordDictNode {
|
|||||||
uint16_t children[0x60 - ' '];
|
uint16_t children[0x60 - ' '];
|
||||||
struct KeywordMapping const *keyword;
|
struct KeywordMapping const *keyword;
|
||||||
/* Since the keyword structure is invariant, the min number of nodes is known at compile time */
|
/* Since the keyword structure is invariant, the min number of nodes is known at compile time */
|
||||||
} keywordDict[355] = {0}; /* Make sure to keep this correct when adding keywords! */
|
} keywordDict[359] = {0}; /* Make sure to keep this correct when adding keywords! */
|
||||||
|
|
||||||
/* Convert a char into its index into the dict */
|
/* Convert a char into its index into the dict */
|
||||||
static inline uint8_t dictIndex(char c)
|
static inline uint8_t dictIndex(char c)
|
||||||
@@ -2049,6 +2050,85 @@ static int yylex_SKIP_TO_ENDC(void)
|
|||||||
return skipIfBlock(true);
|
return skipIfBlock(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int yylex_SKIP_TO_ENDR(void)
|
||||||
|
{
|
||||||
|
dbgPrint("Skipping remainder of REPT/FOR block\n");
|
||||||
|
lexer_SetMode(LEXER_NORMAL);
|
||||||
|
int depth = 1;
|
||||||
|
bool atLineStart = lexerState->atLineStart;
|
||||||
|
|
||||||
|
/* Prevent expanding macro args and symbol interpolation in this state */
|
||||||
|
lexerState->disableMacroArgs = true;
|
||||||
|
lexerState->disableInterpolation = true;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
if (atLineStart) {
|
||||||
|
int c;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
c = peek(0);
|
||||||
|
if (!isWhitespace(c))
|
||||||
|
break;
|
||||||
|
shiftChars(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startsIdentifier(c)) {
|
||||||
|
shiftChars(1);
|
||||||
|
switch (readIdentifier(c)) {
|
||||||
|
case T_POP_FOR:
|
||||||
|
case T_POP_REPT:
|
||||||
|
depth++;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case T_POP_ENDR:
|
||||||
|
depth--;
|
||||||
|
if (!depth)
|
||||||
|
goto finish;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case T_POP_IF:
|
||||||
|
nIFDepth++;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case T_POP_ENDC:
|
||||||
|
nIFDepth--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
atLineStart = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read chars until EOL */
|
||||||
|
do {
|
||||||
|
int c = nextChar();
|
||||||
|
|
||||||
|
if (c == EOF) {
|
||||||
|
goto finish;
|
||||||
|
} else if (c == '\\') {
|
||||||
|
/* Unconditionally skip the next char, including line conts */
|
||||||
|
c = nextChar();
|
||||||
|
} else if (c == '\r' || c == '\n') {
|
||||||
|
atLineStart = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c == '\r' || c == '\n') {
|
||||||
|
/* Handle CRLF before nextLine() since shiftChars updates colNo */
|
||||||
|
if (c == '\r' && peek(0) == '\n')
|
||||||
|
shiftChars(1);
|
||||||
|
/* Do this both on line continuations and plain EOLs */
|
||||||
|
nextLine();
|
||||||
|
}
|
||||||
|
} while (!atLineStart);
|
||||||
|
}
|
||||||
|
finish:
|
||||||
|
|
||||||
|
lexerState->disableMacroArgs = false;
|
||||||
|
lexerState->disableInterpolation = false;
|
||||||
|
lexerState->atLineStart = false;
|
||||||
|
|
||||||
|
/* yywrap() will finish the REPT/FOR loop */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int yylex(void)
|
int yylex(void)
|
||||||
{
|
{
|
||||||
restart:
|
restart:
|
||||||
@@ -2066,7 +2146,8 @@ restart:
|
|||||||
[LEXER_NORMAL] = yylex_NORMAL,
|
[LEXER_NORMAL] = yylex_NORMAL,
|
||||||
[LEXER_RAW] = yylex_RAW,
|
[LEXER_RAW] = yylex_RAW,
|
||||||
[LEXER_SKIP_TO_ELIF] = yylex_SKIP_TO_ELIF,
|
[LEXER_SKIP_TO_ELIF] = yylex_SKIP_TO_ELIF,
|
||||||
[LEXER_SKIP_TO_ENDC] = yylex_SKIP_TO_ENDC
|
[LEXER_SKIP_TO_ENDC] = yylex_SKIP_TO_ENDC,
|
||||||
|
[LEXER_SKIP_TO_ENDR] = yylex_SKIP_TO_ENDR
|
||||||
};
|
};
|
||||||
int token = lexerModeFuncs[lexerState->mode]();
|
int token = lexerModeFuncs[lexerState->mode]();
|
||||||
|
|
||||||
|
|||||||
@@ -463,6 +463,7 @@ static inline void failAssertMsg(enum AssertionType type, char const *msg)
|
|||||||
%token T_POP_POPC
|
%token T_POP_POPC
|
||||||
%token T_POP_SHIFT
|
%token T_POP_SHIFT
|
||||||
%token T_POP_ENDR
|
%token T_POP_ENDR
|
||||||
|
%token T_POP_BREAK
|
||||||
%token T_POP_LOAD T_POP_ENDL
|
%token T_POP_LOAD T_POP_ENDL
|
||||||
%token T_POP_FAIL
|
%token T_POP_FAIL
|
||||||
%token T_POP_WARN
|
%token T_POP_WARN
|
||||||
@@ -684,6 +685,7 @@ simple_pseudoop : include
|
|||||||
| rept
|
| rept
|
||||||
| for
|
| for
|
||||||
| shift
|
| shift
|
||||||
|
| break
|
||||||
| fail
|
| fail
|
||||||
| warn
|
| warn
|
||||||
| assert
|
| assert
|
||||||
@@ -832,6 +834,12 @@ for_args : const {
|
|||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
break : T_POP_BREAK {
|
||||||
|
if (fstk_Break())
|
||||||
|
lexer_SetMode(LEXER_SKIP_TO_ENDR);
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
macrodef : T_LABEL T_COLON T_POP_MACRO {
|
macrodef : T_LABEL T_COLON T_POP_MACRO {
|
||||||
int32_t nDefinitionLineNo = lexer_GetLineNo();
|
int32_t nDefinitionLineNo = lexer_GetLineNo();
|
||||||
char *body;
|
char *body;
|
||||||
|
|||||||
@@ -1616,6 +1616,35 @@ blocks, you can use the escape sequence
|
|||||||
inside of
|
inside of
|
||||||
.Ic FOR
|
.Ic FOR
|
||||||
blocks, and they can be nested.
|
blocks, and they can be nested.
|
||||||
|
.Pp
|
||||||
|
You can stop a repeating block with the
|
||||||
|
.Ic BREAK
|
||||||
|
command.
|
||||||
|
A
|
||||||
|
.Ic BREAK
|
||||||
|
inside of a
|
||||||
|
.Ic REPT
|
||||||
|
or
|
||||||
|
.Ic FOR
|
||||||
|
block will interrupt the current iteration and not repeat any more.
|
||||||
|
It will continue running code after the block's
|
||||||
|
.Ic ENDR .
|
||||||
|
For example:
|
||||||
|
.Bd -literal -offset indent
|
||||||
|
FOR V, 1, 100
|
||||||
|
PRINT "{d:V}"
|
||||||
|
IF V == 5
|
||||||
|
PRINT " stop! "
|
||||||
|
BREAK
|
||||||
|
ENDC
|
||||||
|
PRINT ", "
|
||||||
|
ENDR
|
||||||
|
PRINTLN "done {d:V}"
|
||||||
|
.Ed
|
||||||
|
This will print:
|
||||||
|
.Bd -literal -offset indent
|
||||||
|
1, 2, 3, 4, 5 stop! done 5
|
||||||
|
.Ed
|
||||||
.Ss Aborting the assembly process
|
.Ss Aborting the assembly process
|
||||||
.Ic FAIL
|
.Ic FAIL
|
||||||
and
|
and
|
||||||
|
|||||||
22
test/asm/break.asm
Normal file
22
test/asm/break.asm
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
FOR V, 1, 100
|
||||||
|
PRINTLN "- {d:V}"
|
||||||
|
IF V == 5
|
||||||
|
PRINTLN "stop"
|
||||||
|
BREAK
|
||||||
|
ENDC
|
||||||
|
PRINTLN "cont"
|
||||||
|
ENDR
|
||||||
|
WARN "done {d:V}"
|
||||||
|
rept 1
|
||||||
|
break
|
||||||
|
; skips invalid code
|
||||||
|
!@#$%
|
||||||
|
elif: macro
|
||||||
|
invalid
|
||||||
|
endr
|
||||||
|
warn "OK"
|
||||||
|
rept 1
|
||||||
|
if 1
|
||||||
|
break
|
||||||
|
no endc
|
||||||
|
endr
|
||||||
5
test/asm/break.err
Normal file
5
test/asm/break.err
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
warning: break.asm(9): [-Wuser]
|
||||||
|
done 5
|
||||||
|
warning: break.asm(17): [-Wuser]
|
||||||
|
OK
|
||||||
|
error: Unterminated IF construct (1 levels)!
|
||||||
10
test/asm/break.out
Normal file
10
test/asm/break.out
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
- 1
|
||||||
|
cont
|
||||||
|
- 2
|
||||||
|
cont
|
||||||
|
- 3
|
||||||
|
cont
|
||||||
|
- 4
|
||||||
|
cont
|
||||||
|
- 5
|
||||||
|
stop
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
ERROR: for.asm(16):
|
ERROR: for.asm(16):
|
||||||
FOR cannot have a step value of 0
|
FOR cannot have a step value of 0
|
||||||
ERROR: for.asm(41) -> for.asm::REPT~5(47):
|
ERROR: for.asm(41) -> for.asm::REPT~4(47):
|
||||||
'v' already defined as constant at for.asm(41) -> for.asm::REPT~4(45)
|
'v' already defined as constant at for.asm(41) -> for.asm::REPT~4(45)
|
||||||
FATAL: for.asm(41) -> for.asm::REPT~5(47):
|
FATAL: for.asm(41) -> for.asm::REPT~4(47):
|
||||||
Failed to update FOR symbol value
|
Failed to update FOR symbol value
|
||||||
|
|||||||
17
test/asm/nested-break.asm
Normal file
17
test/asm/nested-break.asm
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
n=1
|
||||||
|
rept 10
|
||||||
|
print "A"
|
||||||
|
for x, 10
|
||||||
|
print "a"
|
||||||
|
if x==n
|
||||||
|
break
|
||||||
|
endc
|
||||||
|
print "z"
|
||||||
|
endr
|
||||||
|
if n==6
|
||||||
|
break
|
||||||
|
endc
|
||||||
|
println "Z"
|
||||||
|
n=n+1
|
||||||
|
endr
|
||||||
|
println "\nn={d:n} x={d:x}"
|
||||||
0
test/asm/nested-break.err
Normal file
0
test/asm/nested-break.err
Normal file
7
test/asm/nested-break.out
Normal file
7
test/asm/nested-break.out
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
AazaZ
|
||||||
|
AazazaZ
|
||||||
|
AazazazaZ
|
||||||
|
AazazazazaZ
|
||||||
|
AazazazazazaZ
|
||||||
|
Aazazazazazaza
|
||||||
|
n=6 x=6
|
||||||
Reference in New Issue
Block a user