Implement \# to expand to all unshifted macro arguments

Fixes #596
This commit is contained in:
Rangi
2020-12-12 18:23:26 -05:00
committed by Eldred Habert
parent ce58f6d6be
commit 0e40543757
7 changed files with 127 additions and 13 deletions

View File

@@ -24,6 +24,7 @@ void macro_AppendArg(struct MacroArgs **args, char *s);
void macro_UseNewArgs(struct MacroArgs *args); void macro_UseNewArgs(struct MacroArgs *args);
void macro_FreeArgs(struct MacroArgs *args); void macro_FreeArgs(struct MacroArgs *args);
char const *macro_GetArg(uint32_t i); char const *macro_GetArg(uint32_t i);
char *macro_GetAllArgs(void);
uint32_t macro_GetUniqueID(void); uint32_t macro_GetUniqueID(void);
char const *macro_GetUniqueIDStr(void); char const *macro_GetUniqueIDStr(void);

View File

@@ -290,11 +290,15 @@ struct Expansion {
struct Expansion *firstChild; struct Expansion *firstChild;
struct Expansion *next; struct Expansion *next;
char *name; char *name;
char const *contents; union {
char const *unowned;
char *owned;
} contents;
size_t len; size_t len;
size_t totalLen; size_t totalLen;
size_t distance; /* Distance between the beginning of this expansion and of its parent */ size_t distance; /* Distance between the beginning of this expansion and of its parent */
uint8_t skip; /* How many extra characters to skip after the expansion is over */ uint8_t skip; /* How many extra characters to skip after the expansion is over */
bool owned; /* Whether or not to free contents when this expansion is freed */
}; };
struct LexerState { struct LexerState {
@@ -632,7 +636,8 @@ static struct Expansion *getExpansionAtDistance(size_t *distance)
} }
static void beginExpansion(size_t distance, uint8_t skip, static void beginExpansion(size_t distance, uint8_t skip,
char const *str, size_t size, char const *name) char const *str, size_t size, bool owned,
char const *name)
{ {
distance += lexerState->expansionOfs; /* Distance argument is relative to read offset! */ distance += lexerState->expansionOfs; /* Distance argument is relative to read offset! */
/* Increase the total length of all parents, and return the topmost one */ /* Increase the total length of all parents, and return the topmost one */
@@ -659,11 +664,12 @@ static void beginExpansion(size_t distance, uint8_t skip,
(*insertPoint)->firstChild = NULL; (*insertPoint)->firstChild = NULL;
(*insertPoint)->next = NULL; /* Expansions are always performed left to right */ (*insertPoint)->next = NULL; /* Expansions are always performed left to right */
(*insertPoint)->name = name ? strdup(name) : NULL; (*insertPoint)->name = name ? strdup(name) : NULL;
(*insertPoint)->contents = str; (*insertPoint)->contents.unowned = str;
(*insertPoint)->len = size; (*insertPoint)->len = size;
(*insertPoint)->totalLen = size; (*insertPoint)->totalLen = size;
(*insertPoint)->distance = distance; (*insertPoint)->distance = distance;
(*insertPoint)->skip = skip; (*insertPoint)->skip = skip;
(*insertPoint)->owned = owned;
/* If expansion is the new closest one, update offset */ /* If expansion is the new closest one, update offset */
if (insertPoint == &lexerState->expansions) if (insertPoint == &lexerState->expansions)
@@ -681,6 +687,8 @@ static void freeExpansion(struct Expansion *expansion)
child = next; child = next;
} }
free(expansion->name); free(expansion->name);
if (expansion->owned)
free(expansion->contents.owned);
free(expansion); free(expansion);
} }
@@ -690,6 +698,8 @@ static char const *expandMacroArg(char name, size_t distance)
if (name == '@') if (name == '@')
str = macro_GetUniqueIDStr(); str = macro_GetUniqueIDStr();
else if (name == '#')
str = macro_GetAllArgs();
else if (name == '0') else if (name == '0')
fatalerror("Invalid macro argument '\\0'\n"); fatalerror("Invalid macro argument '\\0'\n");
else else
@@ -697,7 +707,11 @@ 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);
beginExpansion(distance, 2, str, strlen(str), NULL); /* Cannot expand an empty string */
if (!str[0])
return NULL;
beginExpansion(distance, 2, str, strlen(str), name == '#', NULL);
return str; return str;
} }
@@ -713,7 +727,7 @@ static int peekInternal(uint8_t distance)
if (expansion) { if (expansion) {
assert(ofs < expansion->len); assert(ofs < expansion->len);
return expansion->contents[ofs]; return expansion->contents.unowned[ofs];
} }
distance = ofs; distance = ofs;
@@ -785,11 +799,19 @@ restart:
if (c == '\\' && !lexerState->disableMacroArgs) { if (c == '\\' && !lexerState->disableMacroArgs) {
/* If character is a backslash, check for a macro arg */ /* If character is a backslash, check for a macro arg */
lexerState->macroArgScanDistance++; lexerState->macroArgScanDistance++;
distance++; c = peekInternal(distance + 1);
c = peekInternal(distance); if (c == '@' || c == '#' || (c >= '0' && c <= '9')) {
if (c == '@' || (c >= '0' && c <= '9')) {
/* Expand the argument and return its first character */ /* Expand the argument and return its first character */
char const *str = expandMacroArg(c, distance - 1); char const *str = expandMacroArg(c, distance);
/*
* If the argument is an empty string, nothing was
* expanded, so skip it and keep peeking.
*/
if (!str) {
shiftChars(2);
goto restart;
}
/* /*
* 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
@@ -798,7 +820,10 @@ 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;
/* WARNING: this assumes macro args can't be empty!! */ /*
* 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 = '\\';
@@ -810,7 +835,7 @@ restart:
char const *ptr = readInterpolation(); char const *ptr = readInterpolation();
if (ptr) { if (ptr) {
beginExpansion(distance, 0, ptr, strlen(ptr), ptr); beginExpansion(distance, 0, ptr, strlen(ptr), false, ptr);
goto restart; goto restart;
} }
} }
@@ -1294,7 +1319,7 @@ static char const *readInterpolation(void)
char const *ptr = readInterpolation(); char const *ptr = readInterpolation();
if (ptr) { if (ptr) {
beginExpansion(0, 0, ptr, strlen(ptr), ptr); beginExpansion(0, 0, ptr, strlen(ptr), false, ptr);
continue; /* Restart, reading from the new buffer */ continue; /* Restart, reading from the new buffer */
} }
} else if (c == EOF || c == '\r' || c == '\n' || c == '"') { } else if (c == EOF || c == '\r' || c == '\n' || c == '"') {
@@ -1739,7 +1764,8 @@ static int yylex_NORMAL(void)
if (sym && sym->type == SYM_EQUS) { if (sym && sym->type == SYM_EQUS) {
char const *s = sym_GetStringValue(sym); char const *s = sym_GetStringValue(sym);
beginExpansion(0, 0, s, strlen(s), sym->name); beginExpansion(0, 0, s, strlen(s), false,
sym->name);
continue; /* Restart, reading from the new buffer */ continue; /* Restart, reading from the new buffer */
} }
} }

View File

@@ -98,6 +98,41 @@ char const *macro_GetArg(uint32_t i)
: macroArgs->args[realIndex]; : macroArgs->args[realIndex];
} }
char *macro_GetAllArgs(void)
{
if (!macroArgs)
return NULL;
if (macroArgs->shift >= macroArgs->nbArgs)
return "";
size_t len = strlen(macroArgs->args[macroArgs->shift]);
for (uint32_t i = macroArgs->shift + 1; i < macroArgs->nbArgs; i++)
len += 1 + strlen(macroArgs->args[i]);
char *str = malloc(len + 1);
if (!str)
fatalerror("Failed to allocate memory for expanding '\\#': %s\n", strerror(errno));
char *ptr = str;
size_t n = strlen(macroArgs->args[macroArgs->shift]);
memcpy(ptr, macroArgs->args[macroArgs->shift], n);
ptr += n;
for (uint32_t i = macroArgs->shift + 1; i < macroArgs->nbArgs; i++) {
*ptr++ = ','; /* no space after comma */
n = strlen(macroArgs->args[i]);
memcpy(ptr, macroArgs->args[i], n);
ptr += n;
}
*ptr = '\0';
return str;
}
uint32_t macro_GetUniqueID(void) uint32_t macro_GetUniqueID(void)
{ {
return uniqueID; return uniqueID;

46
test/asm/macro-#.asm Normal file
View File

@@ -0,0 +1,46 @@
SECTION "Test", ROM0
list: MACRO
db _NARG
if _NARG > 0
db \#
endc
db -1
ENDM
list
list 42
list $aa, $bb, $cc, $dd, $ee
person: MACRO
db \1, \2, \3, \4, \5
ENDM
object: MACRO
x = \1
y = \2
shift 2
person y, x, \#
ENDM
person 5, 10, $33, $44, $55
object 12, 6, $66, $77, $88
echo: MACRO
printt "\#\n"
ENDM
R EQUS "S"
echo P
echo Q,R, {R}, T
echo 42,$2a
print: MACRO
printt STRCAT(\#)
printt "\n"
ENDM
print
print "A"
print "B", "C", "D"

0
test/asm/macro-#.err Normal file
View File

6
test/asm/macro-#.out Normal file
View File

@@ -0,0 +1,6 @@
P
Q,R,S,T
42,$2a
A
BCD

BIN
test/asm/macro-#.out.bin Normal file

Binary file not shown.