ref: 1d9cc01ae1f1c0493f53fb9fb018b7a924455dd7
parent: f31deb5010b73b337b2611e2555a871ddac3cd28
author: Rangi <remy.oukaour+rangi42@gmail.com>
date: Mon Dec 14 04:57:45 EST 2020
Macro arguments within a string literal are read into the string, not expanded Fixes #643
--- a/src/asm/lexer.c
+++ b/src/asm/lexer.c
@@ -692,7 +692,7 @@
free(expansion);
}
-static char const *expandMacroArg(char name, size_t distance)
+static char const *readMacroArg(char name)
{
char const *str;
@@ -707,11 +707,6 @@
if (!str)
fatalerror("Macro argument '\\%c' not defined\n", name);
- /* Cannot expand an empty string */
- if (!str[0])
- return NULL;
-
- beginExpansion(distance, 2, str, strlen(str), name == '#', NULL);
return str;
}
@@ -801,18 +796,19 @@
lexerState->macroArgScanDistance++;
c = peekInternal(distance + 1);
if (c == '@' || c == '#' || (c >= '0' && c <= '9')) {
- /* Expand the argument and return its first character */
- char const *str = expandMacroArg(c, distance);
+ char const *str = readMacroArg(c);
/*
- * If the argument is an empty string, nothing was
+ * If the argument is an empty string, it cannot be
* expanded, so skip it and keep peeking.
*/
- if (!str) {
+ if (!str[0]) {
shiftChars(2);
goto restart;
}
+ beginExpansion(distance, 2, str, strlen(str), c == '#', NULL);
+
/*
* Assuming macro args can't be recursive (I'll be damned if a way
* is found...), then we mark the entire macro arg as scanned;
@@ -820,10 +816,7 @@
* so they shouldn't be counted in the scan distance!
*/
lexerState->macroArgScanDistance += strlen(str) - 2;
- /*
- * This assumes macro args can't be empty, since expandMacroArg
- * returns NULL instead of an empty string.
- */
+
c = str[0];
} else {
c = '\\';
@@ -1398,11 +1391,62 @@
return NULL;
}
+static int appendMacroArg(char const *str, int i)
+{
+ while (*str && i < sizeof(yylval.tzString)) {
+ int c = *str++;
+
+ if (c != '\\') {
+ yylval.tzString[i++] = c;
+ continue;
+ }
+
+ c = *str++;
+
+ switch (c) {
+ case '\\': /* Return that character unchanged */
+ case '"':
+ case '{':
+ case '}':
+ break;
+ case 'n':
+ c = '\n';
+ break;
+ case 'r':
+ c = '\r';
+ break;
+ case 't':
+ c = '\t';
+ break;
+
+ case '\0': /* Can't really print that one */
+ error("Illegal character escape at end of macro arg\n");
+ yylval.tzString[i++] = '\\';
+ break;
+
+ /*
+ * Line continuations and macro args were already
+ * handled while reading the macro args, so '\@',
+ * '\#', and '\0'-'\9' should not occur here.
+ */
+
+ default:
+ error("Illegal character escape '%s'\n", print(c));
+ c = '\\';
+ break;
+ }
+ yylval.tzString[i++] = c;
+ }
+
+ return i;
+}
+
static void readString(void)
{
size_t i = 0;
dbgPrint("Reading string\n");
+ lexerState->disableMacroArgs = true;
lexerState->disableInterpolation = true;
for (;;) {
int c = peek(0);
@@ -1417,6 +1461,7 @@
yylval.tzString[i] = '\0';
dbgPrint("Read string \"%s\"\n", yylval.tzString);
goto finish;
+
case '\r':
case '\n': /* Do not shift these! */
case EOF:
@@ -1429,7 +1474,7 @@
dbgPrint("Read string \"%s\"\n", yylval.tzString);
goto finish;
- case '\\': /* Character escape */
+ case '\\': /* Character escape or macro arg */
c = peek(1);
switch (c) {
case '\\': /* Return that character unchanged */
@@ -1451,6 +1496,7 @@
shiftChars(1);
break;
+ /* Line continuation */
case ' ':
case '\r':
case '\n':
@@ -1458,6 +1504,25 @@
readLineContinuation();
continue;
+ /* Macro arg */
+ case '@':
+ case '#':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ shiftChars(2);
+ char const *str = readMacroArg(c);
+
+ i = appendMacroArg(str, i);
+ continue; /* Do not copy an additional character */
+
case EOF: /* Can't really print that one */
error("Illegal character escape at end of input\n");
c = '\\';
@@ -1471,15 +1536,13 @@
case '{': /* Symbol interpolation */
shiftChars(1);
+ lexerState->disableMacroArgs = false;
char const *ptr = readInterpolation();
- if (ptr) {
- while (*ptr) {
- if (i == sizeof(yylval.tzString))
- break;
+ if (ptr)
+ while (*ptr && i < sizeof(yylval.tzString))
yylval.tzString[i++] = *ptr++;
- }
- }
+ lexerState->disableMacroArgs = true;
continue; /* Do not copy an additional character */
/* Regular characters will just get copied */
@@ -1490,6 +1553,7 @@
}
finish:
+ lexerState->disableMacroArgs = false;
lexerState->disableInterpolation = false;
}
--- /dev/null
+++ b/test/asm/macro-arg-in-string.asm
@@ -1,0 +1,19 @@
+print: MACRO
+ PRINTT "\1"
+ PRINTT "\n"
+ENDM
+
+ print John "Danger" Smith
+ print \\A\nB
+ print C\
+D
+ print E\!F ; illegal character escape
+
+
+iprint: MACRO
+ PRINTT "{\1}"
+ PRINTT "\n"
+ENDM
+
+s EQUS "hello"
+ iprint s
--- /dev/null
+++ b/test/asm/macro-arg-in-string.err
@@ -1,0 +1,3 @@
+ERROR: macro-arg-in-string.asm(10) -> macro-arg-in-string.asm::print(2):
+ Illegal character escape '!'
+error: Assembly aborted (1 errors)!
--- /dev/null
+++ b/test/asm/macro-arg-in-string.out
@@ -1,0 +1,6 @@
+John "Danger" Smith
+\A
+B
+CD
+E\F
+hello