Macro arguments within a string literal are read into the string, not expanded

Fixes #643
This commit is contained in:
Rangi
2020-12-14 09:57:45 -05:00
committed by Eldred Habert
parent f31deb5010
commit 1d9cc01ae1
4 changed files with 113 additions and 21 deletions

View File

@@ -692,7 +692,7 @@ static void freeExpansion(struct Expansion *expansion)
free(expansion); free(expansion);
} }
static char const *expandMacroArg(char name, size_t distance) static char const *readMacroArg(char name)
{ {
char const *str; char const *str;
@@ -707,11 +707,6 @@ static char const *expandMacroArg(char name, size_t distance)
if (!str) if (!str)
fatalerror("Macro argument '\\%c' not defined\n", name); 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; return str;
} }
@@ -801,18 +796,19 @@ restart:
lexerState->macroArgScanDistance++; lexerState->macroArgScanDistance++;
c = peekInternal(distance + 1); c = peekInternal(distance + 1);
if (c == '@' || c == '#' || (c >= '0' && c <= '9')) { if (c == '@' || c == '#' || (c >= '0' && c <= '9')) {
/* Expand the argument and return its first character */ char const *str = readMacroArg(c);
char const *str = expandMacroArg(c, distance);
/* /*
* 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. * expanded, so skip it and keep peeking.
*/ */
if (!str) { if (!str[0]) {
shiftChars(2); shiftChars(2);
goto restart; goto restart;
} }
beginExpansion(distance, 2, str, strlen(str), c == '#', NULL);
/* /*
* Assuming macro args can't be recursive (I'll be damned if a way * 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; * is found...), then we mark the entire macro arg as scanned;
@@ -820,10 +816,7 @@ restart:
* so they shouldn't be counted in the scan distance! * so they shouldn't be counted in the scan distance!
*/ */
lexerState->macroArgScanDistance += strlen(str) - 2; 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]; c = str[0];
} else { } else {
c = '\\'; c = '\\';
@@ -1398,11 +1391,62 @@ static char const *readInterpolation(void)
return NULL; 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) static void readString(void)
{ {
size_t i = 0; size_t i = 0;
dbgPrint("Reading string\n"); dbgPrint("Reading string\n");
lexerState->disableMacroArgs = true;
lexerState->disableInterpolation = true; lexerState->disableInterpolation = true;
for (;;) { for (;;) {
int c = peek(0); int c = peek(0);
@@ -1417,6 +1461,7 @@ static void readString(void)
yylval.tzString[i] = '\0'; yylval.tzString[i] = '\0';
dbgPrint("Read string \"%s\"\n", yylval.tzString); dbgPrint("Read string \"%s\"\n", yylval.tzString);
goto finish; goto finish;
case '\r': case '\r':
case '\n': /* Do not shift these! */ case '\n': /* Do not shift these! */
case EOF: case EOF:
@@ -1429,7 +1474,7 @@ static void readString(void)
dbgPrint("Read string \"%s\"\n", yylval.tzString); dbgPrint("Read string \"%s\"\n", yylval.tzString);
goto finish; goto finish;
case '\\': /* Character escape */ case '\\': /* Character escape or macro arg */
c = peek(1); c = peek(1);
switch (c) { switch (c) {
case '\\': /* Return that character unchanged */ case '\\': /* Return that character unchanged */
@@ -1451,6 +1496,7 @@ static void readString(void)
shiftChars(1); shiftChars(1);
break; break;
/* Line continuation */
case ' ': case ' ':
case '\r': case '\r':
case '\n': case '\n':
@@ -1458,6 +1504,25 @@ static void readString(void)
readLineContinuation(); readLineContinuation();
continue; 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 */ case EOF: /* Can't really print that one */
error("Illegal character escape at end of input\n"); error("Illegal character escape at end of input\n");
c = '\\'; c = '\\';
@@ -1471,15 +1536,13 @@ static void readString(void)
case '{': /* Symbol interpolation */ case '{': /* Symbol interpolation */
shiftChars(1); shiftChars(1);
lexerState->disableMacroArgs = false;
char const *ptr = readInterpolation(); char const *ptr = readInterpolation();
if (ptr) { if (ptr)
while (*ptr) { while (*ptr && i < sizeof(yylval.tzString))
if (i == sizeof(yylval.tzString))
break;
yylval.tzString[i++] = *ptr++; yylval.tzString[i++] = *ptr++;
} lexerState->disableMacroArgs = true;
}
continue; /* Do not copy an additional character */ continue; /* Do not copy an additional character */
/* Regular characters will just get copied */ /* Regular characters will just get copied */
@@ -1490,6 +1553,7 @@ static void readString(void)
} }
finish: finish:
lexerState->disableMacroArgs = false;
lexerState->disableInterpolation = false; lexerState->disableInterpolation = false;
} }

View File

@@ -0,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

View File

@@ -0,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)!

View File

@@ -0,0 +1,6 @@
John "Danger" Smith
\A
B
CD
E\F
hello