shithub: rgbds

Download patch

ref: cd747d81750f6a04867782e3581a19b66272380b
parent: df75fd2ec2d959e8c69e54b491eb6c1de4157cd3
author: ISSOtm <eldredhabert0@gmail.com>
date: Fri Aug 14 17:11:44 EDT 2020

Fix many lexer bugs

More to come...

--- a/Makefile
+++ b/Makefile
@@ -198,7 +198,7 @@
 		-fsanitize=unreachable -fsanitize=vla-bound \
 		-fsanitize=signed-integer-overflow -fsanitize=bounds \
 		-fsanitize=object-size -fsanitize=bool -fsanitize=enum \
-		-fsanitize=alignment -fsanitize=null -DDEVELOP" CFLAGS="-g -O0"
+		-fsanitize=alignment -fsanitize=null -DDEVELOP" CFLAGS="-ggdb3 -O0"
 
 # Targets for the project maintainer to easily create Windows exes.
 # This is not for Windows users!
--- a/include/asm/util.h
+++ b/include/asm/util.h
@@ -12,7 +12,7 @@
 #include <stdint.h>
 
 uint32_t calchash(const char *s);
-char const *print(char c);
+char const *print(int c);
 size_t readUTF8Char(uint8_t *dest, char const *src);
 
 #endif /* RGBDS_UTIL_H */
--- a/src/asm/fstack.c
+++ b/src/asm/fstack.c
@@ -26,7 +26,7 @@
 	struct Context *child;
 	struct LexerState *lexerState;
 	uint32_t uniqueID;
-	char *fileName;
+	char const *fileName;
 	uint32_t lineNo; /* Line number at which the context was EXITED */
 	struct Symbol const *macro;
 	uint32_t nbReptIters; /* If zero, this isn't a REPT block */
@@ -149,14 +149,12 @@
 	contextDepth--;
 
 	lexer_DeleteState(contextStack->child->lexerState);
-	/* If at top level (= not in macro or in REPT), free the file name */
-	if (!contextStack->macro && contextStack->reptIters == 0)
-		free(contextStack->child->fileName);
 	/* Free the entry and make its parent the current entry */
 	free(contextStack->child);
 
 	contextStack->child = NULL;
 	lexer_SetState(contextStack->lexerState);
+	macro_SetUniqueID(contextStack->uniqueID);
 	return false;
 }
 
@@ -197,7 +195,7 @@
 	/* We're back at top-level, so most things are reset */
 	contextStack->uniqueID = 0;
 	macro_SetUniqueID(0);
-	contextStack->fileName = fullPath;
+	contextStack->fileName = lexer_GetFileName();
 	contextStack->macro = NULL;
 	contextStack->nbReptIters = 0;
 }
@@ -217,8 +215,9 @@
 	macro_UseNewArgs(args);
 
 	newContext(0);
+	/* Line minus 1 because buffer begins with a newline */
 	contextStack->lexerState = lexer_OpenFileView(macro->macro,
-						      macro->macroSize, macro->fileLine);
+						      macro->macroSize, macro->fileLine - 1);
 	if (!contextStack->lexerState)
 		fatalerror("Failed to set up lexer for macro invocation\n");
 	lexer_SetStateAtEOL(contextStack->lexerState);
@@ -311,7 +310,7 @@
 	lexer_SetState(topLevelContext->lexerState);
 	topLevelContext->uniqueID = 0;
 	macro_SetUniqueID(0);
-	topLevelContext->fileName = mainPath;
+	topLevelContext->fileName = lexer_GetFileName();
 	topLevelContext->macro = NULL;
 	topLevelContext->nbReptIters = 0;
 	topLevelContext->reptDepth = 0;
--- a/src/asm/lexer.c
+++ b/src/asm/lexer.c
@@ -272,7 +272,6 @@
 	char *captureBuf; /* Buffer to send the captured text to if non-NULL */
 	size_t captureCapacity; /* Size of the buffer above */
 
-	size_t expansionDistance; /* Distance already considered for expansions */
 	bool expandStrings;
 	struct Expansion *expansions;
 	size_t expansionOfs; /* Offset into the current top-level expansion (negative = before) */
@@ -290,7 +289,6 @@
 	state->capturing = false;
 	state->captureBuf = NULL;
 
-	state->expansionDistance = 0;
 	state->expandStrings = true;
 	state->expansions = NULL;
 	state->expansionOfs = 0;
@@ -538,7 +536,31 @@
 
 #define LOOKUP_PRE_NEST(exp)
 #define LOOKUP_POST_NEST(exp)
-	lookupExpansion(expansion, *distance);
+	struct Expansion *exp = lexerState->expansions;
+
+	for (;;) {
+		/* Find the closest expansion whose end is after the target */
+		while (exp && exp->totalLen + exp->distance <= *distance) {
+			*distance -= exp->totalLen - exp->skip;
+			exp = exp->next;
+		}
+
+		/* If there is none, or it begins after the target, return the previous level */
+		if (!exp || exp->distance > *distance)
+			break;
+
+		/* We know we are inside of that expansion */
+		*distance -= exp->distance; /* Distances are relative to their parent */
+
+		/* Otherwise, register this expansion and repeat the process */
+		LOOKUP_PRE_NEST(exp);
+		expansion = exp;
+		if (!exp->firstChild) /* If there are no children, this is it */
+			break;
+		exp = exp->firstChild;
+
+		LOOKUP_POST_NEST(exp);
+	}
 #undef LOOKUP_PRE_NEST
 #undef LOOKUP_POST_NEST
 
@@ -553,7 +575,7 @@
 	struct Expansion *parent = NULL;
 	unsigned int depth = 0;
 
-#define LOOKUP_PRE_NEST(exp) (exp)->totalLen += size
+#define LOOKUP_PRE_NEST(exp) (exp)->totalLen += size - skip
 #define LOOKUP_POST_NEST(exp) do { \
 	if (name && ++depth >= nMaxRecursionDepth) \
 		fatalerror("Recursion limit (%u) exceeded\n", nMaxRecursionDepth); \
@@ -604,6 +626,8 @@
 
 	if (name == '@')
 		str = macro_GetUniqueIDStr();
+	else if (name == '0')
+		fatalerror("Invalid macro argument '\\0'\n");
 	else
 		str = macro_GetArg(name - '0');
 	if (!str)
@@ -624,11 +648,11 @@
 	struct Expansion const *expansion = getExpansionAtDistance(&ofs);
 
 	if (expansion) {
-		assert(distance < expansion->len);
+		assert(ofs < expansion->len);
 		return expansion->contents[ofs];
 	}
 
-	distance = ofs - lexerState->expansionOfs;
+	distance = ofs;
 
 	if (lexerState->isMmapped) {
 		if (lexerState->offset + distance >= lexerState->size)
@@ -681,20 +705,17 @@
 {
 	int c = peekInternal(distance);
 
-	if (distance >= lexerState->expansionDistance) {
-		/* If not capturing and character is a backslash, check for a macro arg */
-		if (!lexerState->capturing && c == '\\') {
-			distance++;
-			c = peekInternal(distance);
-			if (c == '@' || (c >= '1' && c <= '9')) {
-				/* Expand the argument and return its first character */
-				c = expandMacroArg(c, distance - 1)[0];
-				/* WARNING: this assumes macro args can't be empty!! */
-			} else {
-				c = '\\';
-			}
+	/* If not capturing and character is a backslash, check for a macro arg */
+	if (!lexerState->capturing && c == '\\') {
+		distance++;
+		c = peekInternal(distance);
+		if (c == '@' || (c >= '0' && c <= '9')) {
+			/* Expand the argument and return its first character */
+			c = expandMacroArg(c, distance - 1)[0];
+			/* WARNING: this assumes macro args can't be empty!! */
+		} else {
+			c = '\\';
 		}
-		lexerState->expansionDistance = distance + 1; /* Do not consider again */
 	}
 	return c;
 }
@@ -713,8 +734,6 @@
 		}
 	}
 
-	lexerState->expansionDistance -= distance;
-
 	/* FIXME: this may not be too great, as only the top level is considered... */
 
 	/*
@@ -830,6 +849,35 @@
 	}
 }
 
+/* Function to read a line continuation */
+
+static bool isWhitespace(int c)
+{
+	return c == ' ' || c == '\t';
+}
+
+static void readLineContinuation(void)
+{
+	for (;;) {
+		int c = peek(0);
+
+		if (isWhitespace(c)) {
+			shiftChars(1);
+		} else if (c == '\r' || c == '\n') {
+			shiftChars(1);
+			if (!lexerState->expansions
+			 || lexerState->expansions->distance) {
+				lexerState->lineNo++;
+			}
+			return;
+		} else {
+			error("Begun line continuation, but encountered character %s\n",
+			      print(c));
+			return;
+		}
+	}
+}
+
 /* Functions to lex numbers of various radixes */
 
 static void readNumber(int radix, int32_t baseValue)
@@ -1190,6 +1238,13 @@
 				shiftChars(1);
 				break;
 
+			case ' ':
+			case '\r':
+			case '\n':
+				shiftChars(1); /* Shift the backslash */
+				readLineContinuation();
+				continue;
+
 			case EOF: /* Can't really print that one */
 				error("Illegal character escape at end of input\n");
 				c = '\\';
@@ -1477,15 +1532,11 @@
 	}
 }
 
-static bool isWhitespace(int c)
-{
-	return c == ' ' || c == '\t';
-}
-
 static int yylex_RAW(void)
 {
 	/* This is essentially a modified `readString` */
 	size_t i = 0;
+	bool insideString = false;
 
 	/* Trim left of string... */
 	while (isWhitespace(peek(0)))
@@ -1495,15 +1546,23 @@
 		int c = peek(0);
 
 		switch (c) {
-		case ',':
-			shiftChars(1);
+		case '"':
+			insideString = !insideString;
+			/* Other than that, just process quotes normally */
+			break;
+
+		case ';': /* Comments inside macro args */
+			if (insideString)
+				break;
+			do {
+				shiftChars(1);
+				c = peek(0);
+			} while (c != EOF && c != '\r' && c != '\n');
 			/* fallthrough */
+		case ',':
 		case '\r':
-		case '\n': /* Do not shift these! */
+		case '\n':
 		case EOF:
-			/* Empty macro args break their expansion, so prevent that */
-			if (i == 0)
-				return c;
 			if (i == sizeof(yylval.tzString)) {
 				i--;
 				warning(WARNING_LONG_STR, "Macro argument too long\n");
@@ -1511,6 +1570,11 @@
 			/* Trim whitespace */
 			while (i && isWhitespace(yylval.tzString[i - 1]))
 				i--;
+			/* Empty macro args break their expansion, so prevent that */
+			if (i == 0) {
+				shiftChars(1);
+				return c == EOF ? 0 : c;
+			}
 			yylval.tzString[i] = '\0';
 			return T_STRING;
 
@@ -1518,31 +1582,21 @@
 			c = peek(1);
 			switch (c) {
 			case ',':
-			case '\\': /* Return that character unchanged */
-			case '"':
-			case '{':
-			case '}':
 				shiftChars(1);
 				break;
-			case 'n':
-				c = '\n';
-				shiftChars(1);
-				break;
-			case 'r':
-				c = '\r';
-				shiftChars(1);
-				break;
-			case 't':
-				c = '\t';
-				shiftChars(1);
-				break;
 
+			case ' ':
+			case '\r':
+			case '\n':
+				shiftChars(1); /* Shift the backslash */
+				readLineContinuation();
+				continue;
+
 			case EOF: /* Can't really print that one */
 				error("Illegal character escape at end of input\n");
 				c = '\\';
 				break;
-			default:
-				error("Illegal character escape '%s'\n", print(c));
+			default: /* Pass the rest as-is */
 				c = '\\';
 				break;
 			}
@@ -1622,6 +1676,10 @@
 int yylex(void)
 {
 restart:
+	if (lexerState->atLineStart && lexerStateEOL) {
+		lexer_SetState(lexerStateEOL);
+		lexerStateEOL = NULL;
+	}
 	if (lexerState->atLineStart) {
 		/* Newlines read within an expansion should not increase the line count */
 		if (!lexerState->expansions || lexerState->expansions->distance) {
@@ -1628,10 +1686,6 @@
 			lexerState->lineNo++;
 			lexerState->colNo = 0;
 		}
-		if (lexerStateEOL) {
-			lexer_SetState(lexerStateEOL);
-			lexerStateEOL = NULL;
-		}
 	}
 
 	static int (* const lexerModeFuncs[])(void) = {
@@ -1693,6 +1747,7 @@
 	 */
 	assert(lexerState->atLineStart);
 	for (;;) {
+		lexerState->lineNo++;
 		/* We're at line start, so attempt to match a `REPT` or `ENDR` token */
 		do { /* Discard initial whitespace */
 			c = nextChar();
@@ -1720,7 +1775,6 @@
 				level--;
 			}
 		}
-		lexerState->lineNo++;
 
 		/* Just consume characters until EOL or EOF */
 		for (;;) {
--- a/src/asm/util.c
+++ b/src/asm/util.c
@@ -28,9 +28,12 @@
 	return hash;
 }
 
-char const *print(char c)
+char const *print(int c)
 {
 	static char buf[5]; /* '\xNN' + '\0' */
+
+	if (c == EOF)
+		return "EOF";
 
 	if (isprint(c)) {
 		buf[0] = c;
--- a/test/asm/assert.err
+++ b/test/asm/assert.err
@@ -6,5 +6,5 @@
     Expected constant expression: 'FloatingBase' is not constant at assembly time
 ERROR: assert.asm(18):
     Assertion failed
-ERROR: assert.asm(21):
+FATAL: assert.asm(21):
     Assertion failed
--- a/test/asm/divzero-instr.err
+++ b/test/asm/divzero-instr.err
@@ -1,2 +1,2 @@
-ERROR: divzero-instr.asm(2):
+FATAL: divzero-instr.asm(2):
     Division by zero
--- a/test/asm/divzero-section-bank.err
+++ b/test/asm/divzero-section-bank.err
@@ -1,2 +1,2 @@
-ERROR: divzero-section-bank.asm(1):
+FATAL: divzero-section-bank.asm(1):
     Division by zero
--- a/test/asm/equs-newline.err
+++ b/test/asm/equs-newline.err
@@ -1,8 +1,7 @@
-warning: test/asm/equs-newline.asm(2): [-Wuser]
+warning: equs-newline.asm(3): [-Wuser]
     First
 while expanding symbol "ACT"
-warning: test/asm/equs-newline.asm(3): [-Wuser]
+warning: equs-newline.asm(3): [-Wuser]
     Second
-while expanding symbol "ACT"
-warning: test/asm/equs-newline.asm(4): [-Wuser]
+warning: equs-newline.asm(4): [-Wuser]
     Third
--- a/test/asm/equs-purge.err
+++ b/test/asm/equs-purge.err
@@ -1,3 +1,3 @@
-warning: test/asm/equs-purge.asm(0): [-Wuser]
+warning: equs-purge.asm(2): [-Wuser]
     Crash?
 while expanding symbol "BYE"
--- a/test/asm/equs-recursion.err
+++ b/test/asm/equs-recursion.err
@@ -1,5 +1,6 @@
-ERROR: equs-recursion.asm(2):
+FATAL: equs-recursion.asm(2):
     Recursion limit (64) exceeded
+while expanding symbol "recurse"
 while expanding symbol "recurse"
 while expanding symbol "recurse"
 while expanding symbol "recurse"
--- a/test/asm/garbage_char.asm
+++ b/test/asm/garbage_char.asm
@@ -1,1 +1,1 @@
-x
\ No newline at end of file
+--- a/test/asm/garbage_char.err
+++ b/test/asm/garbage_char.err
@@ -1,2 +1,3 @@
 ERROR: garbage_char.asm(1):
-    Found garbage character: 0xFF
+    Unknown character 0xFF
+error: Assembly aborted (1 errors)!
--- a/test/asm/if@-no-sect.asm
+++ b/test/asm/if@-no-sect.asm
@@ -1,2 +1,2 @@
-if {@}
+if "{@}"
 endc
--- a/test/asm/include-recursion.err
+++ b/test/asm/include-recursion.err
@@ -1,2 +1,2 @@
-ERROR: include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1):
+FATAL: include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1):
     Recursion limit (64) exceeded
--- a/test/asm/line-continuation-rept.asm
+++ b/test/asm/line-continuation-rept.asm
@@ -2,7 +2,9 @@
 ENDM
 
 REPT 1
-	m ENDR
+	m
+ENDR
 
 REPT 1
-	m \ ENDR
+	m \
+ENDR
--- a/test/asm/line-continuation-whitespace.asm
+++ b/test/asm/line-continuation-whitespace.asm
@@ -2,6 +2,7 @@
 ; file doesn't cause a segfault.
 
 bar: MACRO
+	WARN ""
 ENDM
 
-foo: bar baz\ 	
+foo: bar baz\
--- a/test/asm/load-overflow.err
+++ b/test/asm/load-overflow.err
@@ -1,2 +1,2 @@
-ERROR: load-overflow.asm(4):
+FATAL: load-overflow.asm(4):
     Section 'Overflow' grew too big (max size = 0x8000 bytes, reached 0x8001).
--- a/test/asm/local-purge.err
+++ b/test/asm/local-purge.err
@@ -1,3 +1,3 @@
 ERROR: local-purge.asm(8):
-    '.loc' not defined
+    Interpolated symbol ".loc" does not exist
 error: Assembly aborted (1 errors)!
--- a/test/asm/local-ref-without-parent.err
+++ b/test/asm/local-ref-without-parent.err
@@ -1,2 +1,2 @@
-ERROR: local-ref-without-parent.asm(3):
+FATAL: local-ref-without-parent.asm(3):
     Local label reference '.test' in main scope
--- /dev/null
+++ b/test/asm/macro-line-no.asm
@@ -1,0 +1,8 @@
+
+WARN "Line 2"
+m: macro
+	WARN "Line 4"
+endm
+WARN "Line 6"
+	m
+WARN "Line 8"
--- /dev/null
+++ b/test/asm/macro-line-no.err
@@ -1,0 +1,8 @@
+warning: macro-line-no.asm(2): [-Wuser]
+    Line 2
+warning: macro-line-no.asm(6): [-Wuser]
+    Line 6
+warning: macro-line-no.asm(7) -> macro-line-no.asm::m(4): [-Wuser]
+    Line 4
+warning: macro-line-no.asm(8): [-Wuser]
+    Line 8
--- a/test/asm/macro-recursion.err
+++ b/test/asm/macro-recursion.err
@@ -1,2 +1,2 @@
-ERROR: macro-recursion.asm(4) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2):
+FATAL: macro-recursion.asm(4) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2):
     Recursion limit (64) exceeded
--- a/test/asm/nested-brackets.err
+++ b/test/asm/nested-brackets.err
@@ -1,2 +1,3 @@
 ERROR: nested-brackets.asm(5):
     Missing }
+error: Assembly aborted (1 errors)!
--- a/test/asm/nested-brackets.out
+++ b/test/asm/nested-brackets.out
@@ -1,1 +1,2 @@
 OK
+OK
\ No newline at end of file
binary files a/test/asm/null-in-macro.asm b/test/asm/null-in-macro.asm differ
--- a/test/asm/null-in-macro.err
+++ b/test/asm/null-in-macro.err
@@ -1,2 +1,3 @@
-ERROR: null-in-macro.asm(2):
-    Found null character
+ERROR: null-in-macro.asm(4) -> null-in-macro.asm::foo(2):
+    Unknown character 0x00
+error: Assembly aborted (1 errors)!
--- a/test/asm/pops-no-pushed-sections.err
+++ b/test/asm/pops-no-pushed-sections.err
@@ -1,2 +1,2 @@
-ERROR: pops-no-pushed-sections.asm(1):
+FATAL: pops-no-pushed-sections.asm(1):
     No entries in the section stack
--- a/test/asm/pops-restore-no-section.err
+++ b/test/asm/pops-restore-no-section.err
@@ -1,4 +1,4 @@
 ERROR: pops-restore-no-section.asm(9):
     Label "DisallowedContent" created outside of a SECTION
-ERROR: pops-restore-no-section.asm(10):
+FATAL: pops-restore-no-section.asm(10):
     Code generation before SECTION directive
--- a/test/asm/remote-local-noexist.err
+++ b/test/asm/remote-local-noexist.err
@@ -1,2 +1,2 @@
-ERROR: remote-local-noexist.asm(7):
+FATAL: remote-local-noexist.asm(7):
     'Parent.child.fail' is a nonsensical reference to a nested local symbol
--- a/test/asm/rept-shift.err
+++ b/test/asm/rept-shift.err
@@ -1,2 +1,2 @@
-ERROR: rept-shift.asm(17) -> rept-shift.asm::m(14):
+FATAL: rept-shift.asm(17) -> rept-shift.asm::m(14):
     Macro argument '\1' not defined
--- a/test/asm/section-union.err
+++ b/test/asm/section-union.err
@@ -6,5 +6,5 @@
     Section "test" already declared as fixed at $c000
 ERROR: section-union.asm(37):
     Section "test" already declared as aligned to 256 bytes
-ERROR: section-union.asm(37):
+FATAL: section-union.asm(37):
     Cannot create section "test" (3 errors)
--- a/test/asm/sym-collision.err
+++ b/test/asm/sym-collision.err
@@ -1,3 +1,3 @@
 ERROR: sym-collision.asm(26):
-    'dork' not defined
+    Interpolated symbol "dork" does not exist
 error: Assembly aborted (1 errors)!
--- a/test/asm/sym-collision.out
+++ b/test/asm/sym-collision.out
@@ -1,7 +1,7 @@
 aqfj: $FE00
 cxje: $FE01
 dgsd: $FE02
-dork: $0
+dork: 
 lxok: $FE04
 psgp: $FE05
 sfly: $FE06
--- a/test/asm/symbol-invalid-macro-arg.err
+++ b/test/asm/symbol-invalid-macro-arg.err
@@ -1,2 +1,2 @@
-ERROR: symbol-invalid-macro-arg.asm(1):
-    Invalid macro argument '\0' in symbol
+FATAL: symbol-invalid-macro-arg.asm(1):
+    Invalid macro argument '\0'
--- a/test/asm/test.sh
+++ b/test/asm/test.sh
@@ -12,6 +12,7 @@
 bold=$(tput bold)
 resbold=$(tput sgr0)
 red=$(tput setaf 1)
+green=$(tput setaf 2)
 rescolors=$(tput op)
 tryDiff () {
 	diff -u --strip-trailing-cr $1 $2 || (echo "${bold}${red}${i%.asm}${variant}.$3 mismatch!${rescolors}${resbold}"; false)
@@ -36,6 +37,7 @@
 
 for i in *.asm; do
 	for variant in '' '.pipe'; do
+		echo -e "${bold}${green}${i%.asm}${variant}...${rescolors}${resbold}"
 		if [ -z "$variant" ]; then
 			../../rgbasm -Weverything -o $o $i > $output 2> $errput
 			desired_output=${i%.asm}.out
@@ -59,8 +61,8 @@
 			# Escape regex metacharacters
 			subst="$(printf '%s\n' "$i" | sed 's:[][\/.^$*]:\\&:g')"
 			# Replace the file name with a dash to match changed output
-			sed "s/$subst/-/g" ${i%.asm}.out > $desired_output
-			sed "s/$subst/-/g" ${i%.asm}.err > $desired_errput
+			sed "s/$subst/<stdin>/g" ${i%.asm}.out > $desired_output
+			sed "s/$subst/<stdin>/g" ${i%.asm}.err > $desired_errput
 		fi
 
 		tryDiff $desired_output $output out
--- a/test/asm/unique-id.err
+++ b/test/asm/unique-id.err
@@ -1,19 +1,19 @@
 warning: unique-id.asm(12) -> unique-id.asm::m(4): [-Wuser]
-    _0
-warning: unique-id.asm(12) -> unique-id.asm::m(5) -> unique-id.asm::m::REPT~1(6): [-Wuser]
     _1
-warning: unique-id.asm(12) -> unique-id.asm::m(5) -> unique-id.asm::m::REPT~2(6): [-Wuser]
+warning: unique-id.asm(12) -> unique-id.asm::m(5) -> unique-id.asm::m::REPT~1(6): [-Wuser]
     _2
+warning: unique-id.asm(12) -> unique-id.asm::m(5) -> unique-id.asm::m::REPT~2(6): [-Wuser]
+    _3
 warning: unique-id.asm(12) -> unique-id.asm::m(8): [-Wuser]
-    _0
+    _1
 warning: unique-id.asm(14) -> unique-id.asm::m(4): [-Wuser]
-    _3
-warning: unique-id.asm(14) -> unique-id.asm::m(5) -> unique-id.asm::m::REPT~1(6): [-Wuser]
     _4
-warning: unique-id.asm(14) -> unique-id.asm::m(5) -> unique-id.asm::m::REPT~2(6): [-Wuser]
+warning: unique-id.asm(14) -> unique-id.asm::m(5) -> unique-id.asm::m::REPT~1(6): [-Wuser]
     _5
+warning: unique-id.asm(14) -> unique-id.asm::m(5) -> unique-id.asm::m::REPT~2(6): [-Wuser]
+    _6
 warning: unique-id.asm(14) -> unique-id.asm::m(8): [-Wuser]
-    _3
-ERROR: unique-id.asm(15):
+    _4
+FATAL: unique-id.asm(15):
     Macro argument '\@' not defined
 while expanding symbol "print"