shithub: rgbds

Download patch

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