mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 18:22:07 +00:00
Change FOREACH to FOR (#680)
This commit is contained in:
@@ -34,9 +34,9 @@ struct Context {
|
||||
uint32_t uniqueID;
|
||||
struct MacroArgs *macroArgs; /* Macro args are *saved* here */
|
||||
uint32_t nbReptIters;
|
||||
int32_t foreachValue;
|
||||
int32_t foreachStep;
|
||||
char *foreachName;
|
||||
int32_t forValue;
|
||||
int32_t forStep;
|
||||
char *forName;
|
||||
};
|
||||
|
||||
static struct Context *contextStack;
|
||||
@@ -220,14 +220,14 @@ bool yywrap(void)
|
||||
}
|
||||
|
||||
fileInfo->iters[0]++;
|
||||
/* If this is a FOREACH, update the symbol value */
|
||||
if (contextStack->foreachName) {
|
||||
contextStack->foreachValue += contextStack->foreachStep;
|
||||
struct Symbol *sym = sym_AddSet(contextStack->foreachName,
|
||||
contextStack->foreachValue);
|
||||
/* If this is a FOR, update the symbol value */
|
||||
if (contextStack->forName) {
|
||||
contextStack->forValue += contextStack->forStep;
|
||||
struct Symbol *sym = sym_AddSet(contextStack->forName,
|
||||
contextStack->forValue);
|
||||
|
||||
if (sym->type != SYM_SET)
|
||||
fatalerror("Failed to update FOREACH symbol value\n");
|
||||
fatalerror("Failed to update FOR symbol value\n");
|
||||
}
|
||||
/* If this wasn't the last iteration, wrap instead of popping */
|
||||
if (fileInfo->iters[0] <= contextStack->nbReptIters) {
|
||||
@@ -254,8 +254,8 @@ bool yywrap(void)
|
||||
/* Free the file stack node */
|
||||
if (!context->fileInfo->referenced)
|
||||
free(context->fileInfo);
|
||||
/* Free the FOREACH symbol name */
|
||||
free(context->foreachName);
|
||||
/* Free the FOR symbol name */
|
||||
free(context->forName);
|
||||
/* Free the entry and make its parent the current entry */
|
||||
free(context);
|
||||
|
||||
@@ -281,7 +281,7 @@ static void newContext(struct FileStackNode *fileInfo)
|
||||
fileInfo->referenced = false;
|
||||
fileInfo->lineNo = lexer_GetLineNo();
|
||||
context->fileInfo = fileInfo;
|
||||
context->foreachName = NULL;
|
||||
context->forName = NULL;
|
||||
/*
|
||||
* Link new entry to its parent so it's reachable later
|
||||
* ERRORS SHOULD NOT OCCUR AFTER THIS!!
|
||||
@@ -443,13 +443,13 @@ void fstk_RunRept(uint32_t count, int32_t reptLineNo, char *body, size_t size)
|
||||
return;
|
||||
|
||||
contextStack->nbReptIters = count;
|
||||
contextStack->foreachName = NULL;
|
||||
contextStack->forName = NULL;
|
||||
}
|
||||
|
||||
void fstk_RunForeach(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)
|
||||
{
|
||||
dbgPrint("Running FOREACH(\"%s\", %" PRId32 ", %" PRId32 ", %" PRId32 ")\n",
|
||||
dbgPrint("Running FOR(\"%s\", %" PRId32 ", %" PRId32 ", %" PRId32 ")\n",
|
||||
symName, start, stop, step);
|
||||
|
||||
struct Symbol *sym = sym_AddSet(symName, start);
|
||||
@@ -464,7 +464,7 @@ void fstk_RunForeach(char const *symName, int32_t start, int32_t stop, int32_t s
|
||||
else if (step < 0 && stop < start)
|
||||
count = (start - stop - 1) / -step + 1;
|
||||
else if (step == 0)
|
||||
error("FOREACH cannot have a step value of 0\n");
|
||||
error("FOR cannot have a step value of 0\n");
|
||||
|
||||
if (count == 0)
|
||||
return;
|
||||
@@ -472,11 +472,11 @@ void fstk_RunForeach(char const *symName, int32_t start, int32_t stop, int32_t s
|
||||
return;
|
||||
|
||||
contextStack->nbReptIters = count;
|
||||
contextStack->foreachValue = start;
|
||||
contextStack->foreachStep = step;
|
||||
contextStack->foreachName = strdup(symName);
|
||||
if (!contextStack->foreachName)
|
||||
fatalerror("Not enough memory for FOREACH name: %s\n", strerror(errno));
|
||||
contextStack->forValue = start;
|
||||
contextStack->forStep = step;
|
||||
contextStack->forName = strdup(symName);
|
||||
if (!contextStack->forName)
|
||||
fatalerror("Not enough memory for FOR symbol name: %s\n", strerror(errno));
|
||||
}
|
||||
|
||||
void fstk_Init(char const *mainPath, size_t maxRecursionDepth)
|
||||
@@ -508,9 +508,9 @@ void fstk_Init(char const *mainPath, size_t maxRecursionDepth)
|
||||
context->uniqueID = 0;
|
||||
macro_SetUniqueID(0);
|
||||
context->nbReptIters = 0;
|
||||
context->foreachValue = 0;
|
||||
context->foreachStep = 0;
|
||||
context->foreachName = NULL;
|
||||
context->forValue = 0;
|
||||
context->forStep = 0;
|
||||
context->forName = NULL;
|
||||
|
||||
/* Now that it's set up properly, register the context */
|
||||
contextStack = context;
|
||||
|
||||
@@ -240,7 +240,7 @@ static struct KeywordMapping {
|
||||
{"SHIFT", T_POP_SHIFT},
|
||||
|
||||
{"REPT", T_POP_REPT},
|
||||
{"FOREACH", T_POP_FOREACH},
|
||||
{"FOR", T_POP_FOR},
|
||||
{"ENDR", T_POP_ENDR},
|
||||
|
||||
{"LOAD", T_POP_LOAD},
|
||||
@@ -462,7 +462,7 @@ struct LexerState *lexer_OpenFileView(char *buf, size_t size, uint32_t lineNo)
|
||||
|
||||
void lexer_RestartRept(uint32_t lineNo)
|
||||
{
|
||||
dbgPrint("Restarting REPT/FOREACH\n");
|
||||
dbgPrint("Restarting REPT/FOR\n");
|
||||
lexerState->offset = 0;
|
||||
initState(lexerState);
|
||||
lexerState->lineNo = lineNo;
|
||||
@@ -2123,11 +2123,11 @@ void lexer_CaptureRept(char **capture, size_t *size)
|
||||
do { /* Discard initial whitespace */
|
||||
c = nextChar();
|
||||
} while (isWhitespace(c));
|
||||
/* Now, try to match `REPT`, `FOREACH` or `ENDR` as a **whole** identifier */
|
||||
/* Now, try to match `REPT`, `FOR` or `ENDR` as a **whole** identifier */
|
||||
if (startsIdentifier(c)) {
|
||||
switch (readIdentifier(c)) {
|
||||
case T_POP_REPT:
|
||||
case T_POP_FOREACH:
|
||||
case T_POP_FOR:
|
||||
level++;
|
||||
/* Ignore the rest of that line */
|
||||
break;
|
||||
@@ -2156,7 +2156,7 @@ void lexer_CaptureRept(char **capture, size_t *size)
|
||||
/* Just consume characters until EOL or EOF */
|
||||
for (;;) {
|
||||
if (c == EOF) {
|
||||
error("Unterminated REPT/FOREACH block\n");
|
||||
error("Unterminated REPT/FOR block\n");
|
||||
lexerState->capturing = false;
|
||||
goto finish;
|
||||
} else if (c == '\n' || c == '\r') {
|
||||
|
||||
@@ -310,7 +310,7 @@ static inline void failAssertMsg(enum AssertionType type, char const *msg)
|
||||
int32_t start;
|
||||
int32_t stop;
|
||||
int32_t step;
|
||||
} foreachArgs;
|
||||
} forArgs;
|
||||
struct StrFmtArgList strfmtArgs;
|
||||
}
|
||||
|
||||
@@ -406,7 +406,7 @@ static inline void failAssertMsg(enum AssertionType type, char const *msg)
|
||||
%token T_POP_ENDM
|
||||
%token T_POP_RSRESET T_POP_RSSET
|
||||
%token T_POP_UNION T_POP_NEXTU T_POP_ENDU
|
||||
%token T_POP_INCBIN T_POP_REPT T_POP_FOREACH
|
||||
%token T_POP_INCBIN T_POP_REPT T_POP_FOR
|
||||
%token T_POP_CHARMAP
|
||||
%token T_POP_NEWCHARMAP
|
||||
%token T_POP_SETCHARMAP
|
||||
@@ -431,7 +431,7 @@ static inline void failAssertMsg(enum AssertionType type, char const *msg)
|
||||
%type <sectMod> sectmod
|
||||
%type <macroArg> macroargs
|
||||
|
||||
%type <foreachArgs> foreach_args
|
||||
%type <forArgs> for_args
|
||||
|
||||
%token T_Z80_ADC T_Z80_ADD T_Z80_AND
|
||||
%token T_Z80_BIT
|
||||
@@ -634,7 +634,7 @@ simple_pseudoop : include
|
||||
| popc
|
||||
| load
|
||||
| rept
|
||||
| foreach
|
||||
| for
|
||||
| shift
|
||||
| fail
|
||||
| warn
|
||||
@@ -758,15 +758,15 @@ rept : T_POP_REPT uconst {
|
||||
}
|
||||
;
|
||||
|
||||
foreach : T_POP_FOREACH T_ID T_COMMA foreach_args {
|
||||
for : T_POP_FOR T_ID T_COMMA for_args {
|
||||
uint32_t nDefinitionLineNo = lexer_GetLineNo();
|
||||
char *body;
|
||||
size_t size;
|
||||
lexer_CaptureRept(&body, &size);
|
||||
fstk_RunForeach($2, $4.start, $4.stop, $4.step, nDefinitionLineNo, body, size);
|
||||
fstk_RunFor($2, $4.start, $4.stop, $4.step, nDefinitionLineNo, body, size);
|
||||
}
|
||||
|
||||
foreach_args : const {
|
||||
for_args : const {
|
||||
$$.start = 0;
|
||||
$$.stop = $1;
|
||||
$$.step = 1;
|
||||
|
||||
@@ -1533,18 +1533,18 @@ As in macros, you can also use the escape sequence
|
||||
blocks can be nested.
|
||||
.Pp
|
||||
A common pattern is to repeat a block for each value in some range.
|
||||
.Ic FOREACH
|
||||
.Ic FOR
|
||||
is simpler than
|
||||
.Ic REPT
|
||||
for that purpose.
|
||||
Everything between
|
||||
.Ic FOREACH
|
||||
.Ic FOR
|
||||
and the matching
|
||||
.Ic ENDR
|
||||
will be repeated for each value of a given symbol.
|
||||
For example, this code will produce a table of squared values from 0 to 255:
|
||||
.Bd -literal -offset indent
|
||||
FOREACH N, 256
|
||||
FOR N, 256
|
||||
dw N * N
|
||||
ENDR
|
||||
.Ed
|
||||
@@ -1564,24 +1564,24 @@ N = 256
|
||||
.Ed
|
||||
.Pp
|
||||
You can customize the range of
|
||||
.Ic FOREACH
|
||||
.Ic FOR
|
||||
values:
|
||||
.Bl -column "FOREACH V, start, stop, step"
|
||||
.Bl -column "FOR V, start, stop, step"
|
||||
.It Sy Code Ta Sy Range
|
||||
.It Ic FOREACH Ar V , stop Ta Ar V No increments from 0 to Ar stop No
|
||||
.It Ic FOREACH Ar V , start , stop Ta Ar V No increments from Ar start No to Ar stop No
|
||||
.It Ic FOREACH Ar V , start , stop , step Ta Ar V No goes from Ar start No to Ar stop No by Ar step No
|
||||
.It Ic FOR Ar V , stop Ta Ar V No increments from 0 to Ar stop No
|
||||
.It Ic FOR Ar V , start , stop Ta Ar V No increments from Ar start No to Ar stop No
|
||||
.It Ic FOR Ar V , start , stop , step Ta Ar V No goes from Ar start No to Ar stop No by Ar step No
|
||||
.El
|
||||
.Pp
|
||||
The
|
||||
.Ic FOREACH
|
||||
.Ic FOR
|
||||
value will be updated by
|
||||
.Ar step
|
||||
until it reaches or exceeds
|
||||
.Ar stop.
|
||||
For example:
|
||||
.Bd -literal -offset indent
|
||||
FOREACH V, 4, 25, 5
|
||||
FOR V, 4, 25, 5
|
||||
PRINTT "{d:V} "
|
||||
ENDR
|
||||
PRINTT "done {d:V}\n"
|
||||
@@ -1596,7 +1596,7 @@ Just like with
|
||||
blocks, you can use the escape sequence
|
||||
.Ic \[rs]@
|
||||
inside of
|
||||
.Ic FOREACH
|
||||
.Ic FOR
|
||||
blocks, and they can be nested.
|
||||
.Ss Aborting the assembly process
|
||||
.Ic FAIL
|
||||
|
||||
Reference in New Issue
Block a user