ref: 462fd7539cf4a24897571738e0b453e65bdfc8c1
parent: f16e34b8046ad62653a68b979c78cc9b225c06fa
author: ISSOtm <eldredhabert0@gmail.com>
date: Wed Dec 9 05:44:39 EST 2020
Prohibit nested macros After discussion (starting there: https://github.com/gbdev/rgbds/pull/594#issuecomment-706437458 ), it was decided that plain nested macros should not be allowed. Since #590 is fixed, EQUS can be used as a workaround; multiline strings (#589) will make that easier on the user when implemented. Fixes #588, supersedes and closes #594. Additionally, closes #388.
--- a/src/asm/lexer.c
+++ b/src/asm/lexer.c
@@ -2045,7 +2045,6 @@
void lexer_CaptureMacroBody(char **capture, size_t *size)
{
char *captureStart = startCapture();
- unsigned int level = 0;
int c = peek(0);
/* If the file is `mmap`ed, we need not to unmap it to keep access to the macro */
@@ -2082,41 +2081,19 @@
} while (isWhitespace(c));
/* Now, try to match either `REPT` or `ENDR` as a **whole** identifier */
if (startsIdentifier(c)) {
- switch (readIdentifier(c)) {
- case T_ID:
- /* We have an initial label, look for a single colon */
+ if (readIdentifier(c) == T_POP_ENDM) {
+ /* Read (but don't capture) until EOL or EOF */
+ lexerState->capturing = false;
do {
- c = nextChar();
- } while (isWhitespace(c));
- if (c != ':') /* If not a colon, give up */
- break;
- /* And finally, a `MACRO` token */
- do {
- c = nextChar();
- } while (isWhitespace(c));
- if (!startsIdentifier(c))
- break;
- if (readIdentifier(c) != T_POP_MACRO)
- break;
- level++;
- break;
-
- case T_POP_ENDM:
- if (!level) {
- /* Read (but don't capture) until EOL or EOF */
- lexerState->capturing = false;
- do {
- c = peek(0);
- if (c == EOF || c == '\r' || c == '\n')
- break;
- shiftChars(1);
- } while (c != EOF && c != '\r' && c != '\n');
- /* Handle Windows CRLF */
- if (c == '\r' && peek(1) == '\n')
- shiftChars(1);
- goto finish;
- }
- level--;
+ c = peek(0);
+ if (c == EOF || c == '\r' || c == '\n')
+ break;
+ shiftChars(1);
+ } while (c != EOF && c != '\r' && c != '\n');
+ /* Handle Windows CRLF */
+ if (c == '\r' && peek(1) == '\n')
+ shiftChars(1);
+ goto finish;
}
}
lexerState->lineNo++;
--- a/src/asm/rgbasm.5
+++ b/src/asm/rgbasm.5
@@ -923,6 +923,26 @@
.Ql \&:
following the macro's name is required.
Macros can't be exported or imported.
+.Pp
+Plainly nesting macro definitions is not allowed, but this can be worked around using
+.Ic EQUS .
+This won't work:
+.Bd -literal -offset indent
+outer: MACRO
+inner: MACRO
+ PRINTT "Hello!\[rs]n"
+ENDM
+ENDM
+.Ed
+.Pp
+But this will:
+.Bd -literal -offset indent
+outer: MACRO
+definition equs "inner: MACRO\[rs]nPRINTT \[rs]"Hello!\[rs]\[rs]n\[rs]"\[rs]nENDM"
+definition
+ PURGE definition
+ENDM
+.Ed
.El
.Ss Exporting and importing symbols
Importing and exporting of symbols is a feature that is very useful when your project spans many source files and, for example, you need to jump to a routine defined in another file.
--- /dev/null
+++ b/test/asm/nested-macrodef.asm
@@ -1,0 +1,27 @@
+outer_ok: MACRO
+definition equs "inner_ok: MACRO\nPRINTT \"Hello!\\n\"\nENDM"
+definition
+ PURGE definition
+ENDM
+
+ outer_ok
+ inner_ok
+
+
+outer_arg: MACRO
+definition equs "inner_arg: MACRO\nPRINTT \"outer: \1\\ninner: \\1\\n\"\nENDM"
+definition
+ PURGE definition
+ENDM
+
+ outer_arg outside
+ inner_arg inside
+
+
+outer: MACRO
+ WARN "Nested macros shouldn't work, whose argument would be \\1?"
+inner: MACRO
+ENDM
+
+ outer
+ inner
--- /dev/null
+++ b/test/asm/nested-macrodef.err
@@ -1,0 +1,5 @@
+warning: nested-macrodef.asm(26) -> nested-macrodef.asm::outer(22): [-Wuser]
+ Nested macros shouldn't work, whose argument would be \1?
+ERROR: nested-macrodef.asm(26) -> nested-macrodef.asm::outer(25):
+ Unterminated macro definition
+error: Assembly aborted (1 errors)!
--- /dev/null
+++ b/test/asm/nested-macrodef.out
@@ -1,0 +1,3 @@
+Hello!
+outer: outside
+inner: inside