mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 10:12:06 +00:00
Implement FOREACH (#658)
This acts like `REPT` with a variable automatically incremented across a range of values Fixes #432
This commit is contained in:
@@ -73,6 +73,8 @@ bool yywrap(void);
|
|||||||
void fstk_RunInclude(char const *path);
|
void fstk_RunInclude(char const *path);
|
||||||
void fstk_RunMacro(char const *macroName, struct MacroArgs *args);
|
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_RunForeach(char const *symName, int32_t start, int32_t stop, int32_t step,
|
||||||
|
int32_t reptLineNo, char *body, size_t size);
|
||||||
|
|
||||||
void fstk_Init(char const *mainPath, size_t maxRecursionDepth);
|
void fstk_Init(char const *mainPath, size_t maxRecursionDepth);
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,9 @@ struct Context {
|
|||||||
uint32_t uniqueID;
|
uint32_t uniqueID;
|
||||||
struct MacroArgs *macroArgs; /* Macro args are *saved* here */
|
struct MacroArgs *macroArgs; /* Macro args are *saved* here */
|
||||||
uint32_t nbReptIters;
|
uint32_t nbReptIters;
|
||||||
|
int32_t foreachValue;
|
||||||
|
int32_t foreachStep;
|
||||||
|
char *foreachName;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct Context *contextStack;
|
static struct Context *contextStack;
|
||||||
@@ -217,6 +220,15 @@ bool yywrap(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
fileInfo->iters[0]++;
|
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 (sym->type != SYM_SET)
|
||||||
|
fatalerror("Failed to update FOREACH symbol value\n");
|
||||||
|
}
|
||||||
/* 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);
|
||||||
@@ -242,6 +254,8 @@ bool yywrap(void)
|
|||||||
/* Free the file stack node */
|
/* Free the file stack node */
|
||||||
if (!context->fileInfo->referenced)
|
if (!context->fileInfo->referenced)
|
||||||
free(context->fileInfo);
|
free(context->fileInfo);
|
||||||
|
/* Free the FOREACH symbol name */
|
||||||
|
free(context->foreachName);
|
||||||
/* Free the entry and make its parent the current entry */
|
/* Free the entry and make its parent the current entry */
|
||||||
free(context);
|
free(context);
|
||||||
|
|
||||||
@@ -267,13 +281,13 @@ static void newContext(struct FileStackNode *fileInfo)
|
|||||||
fileInfo->referenced = false;
|
fileInfo->referenced = false;
|
||||||
fileInfo->lineNo = lexer_GetLineNo();
|
fileInfo->lineNo = lexer_GetLineNo();
|
||||||
context->fileInfo = fileInfo;
|
context->fileInfo = fileInfo;
|
||||||
|
context->foreachName = NULL;
|
||||||
/*
|
/*
|
||||||
* Link new entry to its parent so it's reachable later
|
* Link new entry to its parent so it's reachable later
|
||||||
* ERRORS SHOULD NOT OCCUR AFTER THIS!!
|
* ERRORS SHOULD NOT OCCUR AFTER THIS!!
|
||||||
*/
|
*/
|
||||||
context->parent = contextStack;
|
context->parent = contextStack;
|
||||||
contextStack = context;
|
contextStack = context;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void fstk_RunInclude(char const *path)
|
void fstk_RunInclude(char const *path)
|
||||||
@@ -386,12 +400,8 @@ void fstk_RunMacro(char const *macroName, struct MacroArgs *args)
|
|||||||
macro_UseNewArgs(args);
|
macro_UseNewArgs(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
void fstk_RunRept(uint32_t count, int32_t reptLineNo, char *body, size_t size)
|
static bool newReptContext(int32_t reptLineNo, char *body, size_t size)
|
||||||
{
|
{
|
||||||
dbgPrint("Running REPT(%" PRIu32 ")\n", count);
|
|
||||||
if (count == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
uint32_t reptDepth = contextStack->fileInfo->type == NODE_REPT
|
uint32_t reptDepth = contextStack->fileInfo->type == NODE_REPT
|
||||||
? ((struct FileStackReptNode *)contextStack->fileInfo)->reptDepth
|
? ((struct FileStackReptNode *)contextStack->fileInfo)->reptDepth
|
||||||
: 0;
|
: 0;
|
||||||
@@ -400,7 +410,7 @@ void fstk_RunRept(uint32_t count, int32_t reptLineNo, char *body, size_t size)
|
|||||||
|
|
||||||
if (!fileInfo) {
|
if (!fileInfo) {
|
||||||
error("Failed to alloc file info for REPT: %s\n", strerror(errno));
|
error("Failed to alloc file info for REPT: %s\n", strerror(errno));
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
fileInfo->node.type = NODE_REPT;
|
fileInfo->node.type = NODE_REPT;
|
||||||
fileInfo->reptDepth = reptDepth + 1;
|
fileInfo->reptDepth = reptDepth + 1;
|
||||||
@@ -420,8 +430,53 @@ void fstk_RunRept(uint32_t count, int32_t reptLineNo, char *body, size_t size)
|
|||||||
fatalerror("Failed to set up lexer for rept block\n");
|
fatalerror("Failed to set up lexer for rept block\n");
|
||||||
lexer_SetStateAtEOL(contextStack->lexerState);
|
lexer_SetStateAtEOL(contextStack->lexerState);
|
||||||
contextStack->uniqueID = macro_UseNewUniqueID();
|
contextStack->uniqueID = macro_UseNewUniqueID();
|
||||||
contextStack->nbReptIters = count;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fstk_RunRept(uint32_t count, int32_t reptLineNo, char *body, size_t size)
|
||||||
|
{
|
||||||
|
dbgPrint("Running REPT(%" PRIu32 ")\n", count);
|
||||||
|
|
||||||
|
if (count == 0)
|
||||||
|
return;
|
||||||
|
if (!newReptContext(reptLineNo, body, size))
|
||||||
|
return;
|
||||||
|
|
||||||
|
contextStack->nbReptIters = count;
|
||||||
|
contextStack->foreachName = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fstk_RunForeach(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",
|
||||||
|
symName, start, stop, step);
|
||||||
|
|
||||||
|
struct Symbol *sym = sym_AddSet(symName, start);
|
||||||
|
|
||||||
|
if (sym->type != SYM_SET)
|
||||||
|
return;
|
||||||
|
|
||||||
|
uint32_t count = 0;
|
||||||
|
|
||||||
|
if (step > 0 && start < stop)
|
||||||
|
count = (stop - start - 1) / step + 1;
|
||||||
|
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");
|
||||||
|
|
||||||
|
if (count == 0)
|
||||||
|
return;
|
||||||
|
if (!newReptContext(reptLineNo, body, size))
|
||||||
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
void fstk_Init(char const *mainPath, size_t maxRecursionDepth)
|
void fstk_Init(char const *mainPath, size_t maxRecursionDepth)
|
||||||
@@ -453,6 +508,9 @@ void fstk_Init(char const *mainPath, size_t maxRecursionDepth)
|
|||||||
context->uniqueID = 0;
|
context->uniqueID = 0;
|
||||||
macro_SetUniqueID(0);
|
macro_SetUniqueID(0);
|
||||||
context->nbReptIters = 0;
|
context->nbReptIters = 0;
|
||||||
|
context->foreachValue = 0;
|
||||||
|
context->foreachStep = 0;
|
||||||
|
context->foreachName = NULL;
|
||||||
|
|
||||||
/* Now that it's set up properly, register the context */
|
/* Now that it's set up properly, register the context */
|
||||||
contextStack = context;
|
contextStack = context;
|
||||||
|
|||||||
@@ -238,6 +238,7 @@ static struct KeywordMapping {
|
|||||||
{"SHIFT", T_POP_SHIFT},
|
{"SHIFT", T_POP_SHIFT},
|
||||||
|
|
||||||
{"REPT", T_POP_REPT},
|
{"REPT", T_POP_REPT},
|
||||||
|
{"FOREACH", T_POP_FOREACH},
|
||||||
{"ENDR", T_POP_ENDR},
|
{"ENDR", T_POP_ENDR},
|
||||||
|
|
||||||
{"LOAD", T_POP_LOAD},
|
{"LOAD", T_POP_LOAD},
|
||||||
@@ -453,7 +454,7 @@ struct LexerState *lexer_OpenFileView(char *buf, size_t size, uint32_t lineNo)
|
|||||||
|
|
||||||
void lexer_RestartRept(uint32_t lineNo)
|
void lexer_RestartRept(uint32_t lineNo)
|
||||||
{
|
{
|
||||||
dbgPrint("Restarting REPT\n");
|
dbgPrint("Restarting REPT/FOREACH\n");
|
||||||
lexerState->offset = 0;
|
lexerState->offset = 0;
|
||||||
initState(lexerState);
|
initState(lexerState);
|
||||||
lexerState->lineNo = lineNo;
|
lexerState->lineNo = lineNo;
|
||||||
@@ -479,7 +480,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[341] = {0}; /* Make sure to keep this correct when adding keywords! */
|
} keywordDict[347] = {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)
|
||||||
@@ -2161,10 +2162,11 @@ void lexer_CaptureRept(char **capture, size_t *size)
|
|||||||
do { /* Discard initial whitespace */
|
do { /* Discard initial whitespace */
|
||||||
c = nextChar();
|
c = nextChar();
|
||||||
} while (isWhitespace(c));
|
} while (isWhitespace(c));
|
||||||
/* Now, try to match either `REPT` or `ENDR` as a **whole** identifier */
|
/* Now, try to match `REPT`, `FOREACH` or `ENDR` as a **whole** identifier */
|
||||||
if (startsIdentifier(c)) {
|
if (startsIdentifier(c)) {
|
||||||
switch (readIdentifier(c)) {
|
switch (readIdentifier(c)) {
|
||||||
case T_POP_REPT:
|
case T_POP_REPT:
|
||||||
|
case T_POP_FOREACH:
|
||||||
level++;
|
level++;
|
||||||
/* Ignore the rest of that line */
|
/* Ignore the rest of that line */
|
||||||
break;
|
break;
|
||||||
@@ -2188,7 +2190,7 @@ void lexer_CaptureRept(char **capture, size_t *size)
|
|||||||
/* Just consume characters until EOL or EOF */
|
/* Just consume characters until EOL or EOF */
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (c == EOF) {
|
if (c == EOF) {
|
||||||
error("Unterminated REPT block\n");
|
error("Unterminated REPT/FOREACH block\n");
|
||||||
lexerState->capturing = false;
|
lexerState->capturing = false;
|
||||||
goto finish;
|
goto finish;
|
||||||
} else if (c == '\n' || c == '\r') {
|
} else if (c == '\n' || c == '\r') {
|
||||||
@@ -2246,7 +2248,7 @@ void lexer_CaptureMacroBody(char **capture, size_t *size)
|
|||||||
do { /* Discard initial whitespace */
|
do { /* Discard initial whitespace */
|
||||||
c = nextChar();
|
c = nextChar();
|
||||||
} while (isWhitespace(c));
|
} while (isWhitespace(c));
|
||||||
/* Now, try to match either `REPT` or `ENDR` as a **whole** identifier */
|
/* Now, try to match `ENDM` as a **whole** identifier */
|
||||||
if (startsIdentifier(c)) {
|
if (startsIdentifier(c)) {
|
||||||
if (readIdentifier(c) == T_POP_ENDM) {
|
if (readIdentifier(c) == T_POP_ENDM) {
|
||||||
/* Read (but don't capture) until EOL or EOF */
|
/* Read (but don't capture) until EOL or EOF */
|
||||||
|
|||||||
@@ -205,6 +205,11 @@ static inline void failAssertMsg(enum AssertionType type, char const *msg)
|
|||||||
struct SectionSpec sectSpec;
|
struct SectionSpec sectSpec;
|
||||||
struct MacroArgs *macroArg;
|
struct MacroArgs *macroArg;
|
||||||
enum AssertionType assertType;
|
enum AssertionType assertType;
|
||||||
|
struct {
|
||||||
|
int32_t start;
|
||||||
|
int32_t stop;
|
||||||
|
int32_t step;
|
||||||
|
} foreachArgs;
|
||||||
}
|
}
|
||||||
|
|
||||||
%type <sVal> relocexpr
|
%type <sVal> relocexpr
|
||||||
@@ -296,7 +301,7 @@ static inline void failAssertMsg(enum AssertionType type, char const *msg)
|
|||||||
%token T_POP_ENDM
|
%token T_POP_ENDM
|
||||||
%token T_POP_RSRESET T_POP_RSSET
|
%token T_POP_RSRESET T_POP_RSSET
|
||||||
%token T_POP_UNION T_POP_NEXTU T_POP_ENDU
|
%token T_POP_UNION T_POP_NEXTU T_POP_ENDU
|
||||||
%token T_POP_INCBIN T_POP_REPT
|
%token T_POP_INCBIN T_POP_REPT T_POP_FOREACH
|
||||||
%token T_POP_CHARMAP
|
%token T_POP_CHARMAP
|
||||||
%token T_POP_NEWCHARMAP
|
%token T_POP_NEWCHARMAP
|
||||||
%token T_POP_SETCHARMAP
|
%token T_POP_SETCHARMAP
|
||||||
@@ -321,6 +326,8 @@ static inline void failAssertMsg(enum AssertionType type, char const *msg)
|
|||||||
%type <sectMod> sectmod
|
%type <sectMod> sectmod
|
||||||
%type <macroArg> macroargs
|
%type <macroArg> macroargs
|
||||||
|
|
||||||
|
%type <foreachArgs> foreach_args
|
||||||
|
|
||||||
%token T_Z80_ADC T_Z80_ADD T_Z80_AND
|
%token T_Z80_ADC T_Z80_ADD T_Z80_AND
|
||||||
%token T_Z80_BIT
|
%token T_Z80_BIT
|
||||||
%token T_Z80_CALL T_Z80_CCF T_Z80_CP T_Z80_CPL
|
%token T_Z80_CALL T_Z80_CCF T_Z80_CP T_Z80_CPL
|
||||||
@@ -522,6 +529,7 @@ simple_pseudoop : include
|
|||||||
| popc
|
| popc
|
||||||
| load
|
| load
|
||||||
| rept
|
| rept
|
||||||
|
| foreach
|
||||||
| shift
|
| shift
|
||||||
| fail
|
| fail
|
||||||
| warn
|
| warn
|
||||||
@@ -645,6 +653,31 @@ rept : T_POP_REPT uconst {
|
|||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
foreach : T_POP_FOREACH T_ID T_COMMA foreach_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);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach_args : const {
|
||||||
|
$$.start = 0;
|
||||||
|
$$.stop = $1;
|
||||||
|
$$.step = 1;
|
||||||
|
}
|
||||||
|
| const T_COMMA const {
|
||||||
|
$$.start = $1;
|
||||||
|
$$.stop = $3;
|
||||||
|
$$.step = 1;
|
||||||
|
}
|
||||||
|
| const T_COMMA const T_COMMA const {
|
||||||
|
$$.start = $1;
|
||||||
|
$$.stop = $3;
|
||||||
|
$$.step = $5;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
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;
|
||||||
|
|||||||
@@ -1470,6 +1470,73 @@ As in macros, you can also use the escape sequence
|
|||||||
.Ic \[rs]@ .
|
.Ic \[rs]@ .
|
||||||
.Ic REPT
|
.Ic REPT
|
||||||
blocks can be nested.
|
blocks can be nested.
|
||||||
|
.Pp
|
||||||
|
A common pattern is to repeat a block for each value in some range.
|
||||||
|
.Ic FOREACH
|
||||||
|
is simpler than
|
||||||
|
.Ic REPT
|
||||||
|
for that purpose.
|
||||||
|
Everything between
|
||||||
|
.Ic FOREACH
|
||||||
|
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
|
||||||
|
dw N * N
|
||||||
|
ENDR
|
||||||
|
.Ed
|
||||||
|
.Pp
|
||||||
|
It acts just as if you had done:
|
||||||
|
.Bd -literal -offset ident
|
||||||
|
N = 0
|
||||||
|
dw N * N
|
||||||
|
N = 1
|
||||||
|
dw N * N
|
||||||
|
N = 2
|
||||||
|
dw N * N
|
||||||
|
; ...
|
||||||
|
N = 255
|
||||||
|
dw N * N
|
||||||
|
N = 256
|
||||||
|
.Ed
|
||||||
|
.Pp
|
||||||
|
You can customize the range of
|
||||||
|
.Ic FOREACH
|
||||||
|
values:
|
||||||
|
.Bl -column "FOREACH 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
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
The
|
||||||
|
.Ic FOREACH
|
||||||
|
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
|
||||||
|
PRINTT "{d:V} "
|
||||||
|
ENDR
|
||||||
|
PRINTT "done {d:V}\n"
|
||||||
|
.Ed
|
||||||
|
This will print:
|
||||||
|
.Bd -literal -offset indent
|
||||||
|
4 9 14 19 24 done 29
|
||||||
|
.Ed
|
||||||
|
.Pp
|
||||||
|
Just like with
|
||||||
|
.Ic REPT
|
||||||
|
blocks, you can use the escape sequence
|
||||||
|
.Ic \[rs]@
|
||||||
|
inside of
|
||||||
|
.Ic FOREACH
|
||||||
|
blocks, and they can be nested.
|
||||||
.Ss Aborting the assembly process
|
.Ss Aborting the assembly process
|
||||||
.Ic FAIL
|
.Ic FAIL
|
||||||
and
|
and
|
||||||
|
|||||||
48
test/asm/foreach.asm
Normal file
48
test/asm/foreach.asm
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
foreach n, 10
|
||||||
|
printt "{d:n} "
|
||||||
|
endr
|
||||||
|
printt "-> {d:n}\n"
|
||||||
|
|
||||||
|
foreach v, 0
|
||||||
|
printt "unreached"
|
||||||
|
endr
|
||||||
|
|
||||||
|
foreach v, 2, 1
|
||||||
|
printt "unreached"
|
||||||
|
endr
|
||||||
|
|
||||||
|
foreach v, 1, 2, 0
|
||||||
|
printt "unreached"
|
||||||
|
endr
|
||||||
|
|
||||||
|
foreach x, 1, 5+1
|
||||||
|
printt "{d:x} "
|
||||||
|
endr
|
||||||
|
printt "-> {d:x}\n"
|
||||||
|
|
||||||
|
foreach v, 10, -1, -1
|
||||||
|
printt "{d:v} "
|
||||||
|
v = 42
|
||||||
|
endr
|
||||||
|
printt "-> {d:v}\n"
|
||||||
|
|
||||||
|
foreach q, 5, 21, 5
|
||||||
|
printt "{d:q} "
|
||||||
|
purge q
|
||||||
|
endr
|
||||||
|
printt "-> {d:q}\n"
|
||||||
|
|
||||||
|
s EQUS "x"
|
||||||
|
foreach s, 3, 30, 3
|
||||||
|
printt "{d:x} "
|
||||||
|
endr
|
||||||
|
printt "-> {d:x}\n"
|
||||||
|
|
||||||
|
foreach v, 10
|
||||||
|
printt "{d:v}\n"
|
||||||
|
if v == 3
|
||||||
|
purge v
|
||||||
|
v equ 42 ; causes a fatal error
|
||||||
|
endc
|
||||||
|
endr
|
||||||
|
printt "-> {d:v}\n"
|
||||||
6
test/asm/foreach.err
Normal file
6
test/asm/foreach.err
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
ERROR: foreach.asm(16):
|
||||||
|
FOREACH cannot have a step value of 0
|
||||||
|
ERROR: foreach.asm(41) -> foreach.asm::REPT~5(47):
|
||||||
|
'v' already defined as constant at foreach.asm(41) -> foreach.asm::REPT~4(45)
|
||||||
|
FATAL: foreach.asm(41) -> foreach.asm::REPT~5(47):
|
||||||
|
Failed to update FOREACH symbol value
|
||||||
9
test/asm/foreach.out
Normal file
9
test/asm/foreach.out
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
0 1 2 3 4 5 6 7 8 9 -> 10
|
||||||
|
1 2 3 4 5 -> 6
|
||||||
|
10 9 8 7 6 5 4 3 2 1 0 -> -1
|
||||||
|
5 10 15 20 -> 25
|
||||||
|
3 6 9 12 15 18 21 24 27 -> 30
|
||||||
|
0
|
||||||
|
1
|
||||||
|
2
|
||||||
|
3
|
||||||
Reference in New Issue
Block a user