mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 18:22:07 +00:00
@@ -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);
|
||||||
|
|||||||
@@ -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 */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
46
test/asm/macro-#.asm
Normal 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
0
test/asm/macro-#.err
Normal file
6
test/asm/macro-#.out
Normal file
6
test/asm/macro-#.out
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
P
|
||||||
|
Q,R,S,T
|
||||||
|
42,$2a
|
||||||
|
|
||||||
|
A
|
||||||
|
BCD
|
||||||
BIN
test/asm/macro-#.out.bin
Normal file
BIN
test/asm/macro-#.out.bin
Normal file
Binary file not shown.
Reference in New Issue
Block a user