mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 10:12:06 +00:00
@@ -332,6 +332,7 @@ struct LexerState {
|
||||
size_t captureCapacity; /* Size of the buffer above */
|
||||
|
||||
bool disableMacroArgs;
|
||||
bool disableInterpolation;
|
||||
size_t macroArgScanDistance; /* Max distance already scanned for macro args */
|
||||
bool expandStrings;
|
||||
struct Expansion *expansions;
|
||||
@@ -351,6 +352,7 @@ static void initState(struct LexerState *state)
|
||||
state->captureBuf = NULL;
|
||||
|
||||
state->disableMacroArgs = false;
|
||||
state->disableInterpolation = false;
|
||||
state->macroArgScanDistance = 0;
|
||||
state->expandStrings = true;
|
||||
state->expansions = NULL;
|
||||
@@ -767,16 +769,23 @@ static int peekInternal(uint8_t distance)
|
||||
return (unsigned char)lexerState->buf[(lexerState->index + distance) % LEXER_BUF_SIZE];
|
||||
}
|
||||
|
||||
/* forward declarations for peek */
|
||||
static void shiftChars(uint8_t distance);
|
||||
static char const *readInterpolation(void);
|
||||
|
||||
static int peek(uint8_t distance)
|
||||
{
|
||||
int c = peekInternal(distance);
|
||||
int c;
|
||||
|
||||
restart:
|
||||
c = peekInternal(distance);
|
||||
|
||||
if (distance >= lexerState->macroArgScanDistance) {
|
||||
lexerState->macroArgScanDistance = distance + 1; /* Do not consider again */
|
||||
/* If enabled and character is a backslash, check for a macro arg */
|
||||
if (!lexerState->disableMacroArgs && c == '\\') {
|
||||
distance++;
|
||||
if (c == '\\' && !lexerState->disableMacroArgs) {
|
||||
/* If character is a backslash, check for a macro arg */
|
||||
lexerState->macroArgScanDistance++;
|
||||
distance++;
|
||||
c = peekInternal(distance);
|
||||
if (c == '@' || (c >= '0' && c <= '9')) {
|
||||
/* Expand the argument and return its first character */
|
||||
@@ -794,8 +803,19 @@ static int peek(uint8_t distance)
|
||||
} else {
|
||||
c = '\\';
|
||||
}
|
||||
} else if (c == '{' && !lexerState->disableInterpolation) {
|
||||
/* If character is an open brace, do symbol interpolation */
|
||||
lexerState->macroArgScanDistance++;
|
||||
shiftChars(1);
|
||||
char const *ptr = readInterpolation();
|
||||
|
||||
if (ptr) {
|
||||
beginExpansion(distance, 0, ptr, strlen(ptr), ptr);
|
||||
goto restart;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
@@ -927,6 +947,7 @@ static void discardBlockComment(void)
|
||||
{
|
||||
dbgPrint("Discarding block comment\n");
|
||||
lexerState->disableMacroArgs = true;
|
||||
lexerState->disableInterpolation = true;
|
||||
for (;;) {
|
||||
switch (nextChar()) {
|
||||
case EOF:
|
||||
@@ -950,6 +971,7 @@ static void discardBlockComment(void)
|
||||
}
|
||||
finish:
|
||||
lexerState->disableMacroArgs = false;
|
||||
lexerState->disableInterpolation = false;
|
||||
}
|
||||
|
||||
/* Function to discard all of a line's comments */
|
||||
@@ -958,6 +980,7 @@ static void discardComment(void)
|
||||
{
|
||||
dbgPrint("Discarding comment\n");
|
||||
lexerState->disableMacroArgs = true;
|
||||
lexerState->disableInterpolation = true;
|
||||
for (;;) {
|
||||
int c = peek(0);
|
||||
|
||||
@@ -966,6 +989,7 @@ static void discardComment(void)
|
||||
shiftChars(1);
|
||||
}
|
||||
lexerState->disableMacroArgs = false;
|
||||
lexerState->disableInterpolation = false;
|
||||
}
|
||||
|
||||
/* Function to read a line continuation */
|
||||
@@ -1267,14 +1291,11 @@ static char const *readInterpolation(void)
|
||||
|
||||
if (c == '{') { /* Nested interpolation */
|
||||
shiftChars(1);
|
||||
char const *inner = readInterpolation();
|
||||
char const *ptr = readInterpolation();
|
||||
|
||||
if (inner) {
|
||||
while (*inner) {
|
||||
if (i == sizeof(symName))
|
||||
break;
|
||||
symName[i++] = *inner++;
|
||||
}
|
||||
if (ptr) {
|
||||
beginExpansion(0, 0, ptr, strlen(ptr), ptr);
|
||||
continue; /* Restart, reading from the new buffer */
|
||||
}
|
||||
} else if (c == EOF || c == '\r' || c == '\n' || c == '"') {
|
||||
error("Missing }\n");
|
||||
@@ -1342,6 +1363,7 @@ static void readString(void)
|
||||
size_t i = 0;
|
||||
|
||||
dbgPrint("Reading string\n");
|
||||
lexerState->disableInterpolation = true;
|
||||
for (;;) {
|
||||
int c = peek(0);
|
||||
|
||||
@@ -1354,7 +1376,7 @@ static void readString(void)
|
||||
}
|
||||
yylval.tzString[i] = '\0';
|
||||
dbgPrint("Read string \"%s\"\n", yylval.tzString);
|
||||
return;
|
||||
goto finish;
|
||||
case '\r':
|
||||
case '\n': /* Do not shift these! */
|
||||
case EOF:
|
||||
@@ -1365,7 +1387,7 @@ static void readString(void)
|
||||
yylval.tzString[i] = '\0';
|
||||
error("Unterminated string\n");
|
||||
dbgPrint("Read string \"%s\"\n", yylval.tzString);
|
||||
return;
|
||||
goto finish;
|
||||
|
||||
case '\\': /* Character escape */
|
||||
c = peek(1);
|
||||
@@ -1426,6 +1448,9 @@ static void readString(void)
|
||||
yylval.tzString[i++] = c;
|
||||
shiftChars(1);
|
||||
}
|
||||
|
||||
finish:
|
||||
lexerState->disableInterpolation = false;
|
||||
}
|
||||
|
||||
/* Function to report one character's worth of garbage bytes */
|
||||
@@ -1812,19 +1837,6 @@ static int yylex_RAW(void)
|
||||
}
|
||||
break;
|
||||
|
||||
case '{': /* Symbol interpolation */
|
||||
shiftChars(1);
|
||||
char const *ptr = readInterpolation();
|
||||
|
||||
if (ptr) {
|
||||
while (*ptr) {
|
||||
if (i == sizeof(yylval.tzString))
|
||||
break;
|
||||
yylval.tzString[i++] = *ptr++;
|
||||
}
|
||||
}
|
||||
continue; /* Do not copy an additional character */
|
||||
|
||||
/* Regular characters will just get copied */
|
||||
}
|
||||
if (i < sizeof(yylval.tzString)) /* Copy one extra to flag overflow */
|
||||
@@ -1848,8 +1860,9 @@ static int skipIfBlock(bool toEndc)
|
||||
int token;
|
||||
bool atLineStart = lexerState->atLineStart;
|
||||
|
||||
/* Prevent expanding macro args in this state */
|
||||
/* Prevent expanding macro args and symbol interpolation in this state */
|
||||
lexerState->disableMacroArgs = true;
|
||||
lexerState->disableInterpolation = true;
|
||||
|
||||
for (;;) {
|
||||
if (atLineStart) {
|
||||
@@ -1910,6 +1923,7 @@ static int skipIfBlock(bool toEndc)
|
||||
finish:
|
||||
|
||||
lexerState->disableMacroArgs = false;
|
||||
lexerState->disableInterpolation = false;
|
||||
lexerState->atLineStart = false;
|
||||
|
||||
return token;
|
||||
@@ -1978,6 +1992,7 @@ static char *startCapture(void)
|
||||
lexerState->capturing = true;
|
||||
lexerState->captureSize = 0;
|
||||
lexerState->disableMacroArgs = true;
|
||||
lexerState->disableInterpolation = true;
|
||||
|
||||
if (lexerState->isMmapped && !lexerState->expansions) {
|
||||
return &lexerState->ptr[lexerState->offset];
|
||||
@@ -2051,6 +2066,7 @@ finish:
|
||||
*size = lexerState->captureSize - strlen("ENDR");
|
||||
lexerState->captureBuf = NULL;
|
||||
lexerState->disableMacroArgs = false;
|
||||
lexerState->disableInterpolation = false;
|
||||
}
|
||||
|
||||
void lexer_CaptureMacroBody(char **capture, size_t *size)
|
||||
@@ -2116,4 +2132,5 @@ finish:
|
||||
*size = lexerState->captureSize - strlen("ENDM");
|
||||
lexerState->captureBuf = NULL;
|
||||
lexerState->disableMacroArgs = false;
|
||||
lexerState->disableInterpolation = false;
|
||||
}
|
||||
|
||||
@@ -251,9 +251,9 @@ If it's a numeric symbol, its value is converted to hexadecimal notation with a
|
||||
.Sq $
|
||||
prepended.
|
||||
.Bd -literal -offset indent
|
||||
TOPIC equs "life, the universe, and everything"
|
||||
TOPIC equs "life, the universe, and \[rs]"everything\[rs]""
|
||||
ANSWER = 42
|
||||
;\ Prints "The answer to life, the universe, and everything is $2A"
|
||||
;\ Prints "The answer to life, the universe, and "everything" is $2A"
|
||||
PRINTT "The answer to {TOPIC} is {ANSWER}\[rs]n"
|
||||
.Ed
|
||||
.Pp
|
||||
@@ -276,6 +276,20 @@ HINT: The
|
||||
.Ic {symbol}
|
||||
construct can also be used outside strings.
|
||||
The symbol's value is again inserted directly.
|
||||
.Bd -literal -offset indent
|
||||
NAME equs "ITEM"
|
||||
FMT equs "d"
|
||||
ZERO_NUM equ 0
|
||||
ZERO_STR equs "0"
|
||||
;\ Defines INDEX as 100
|
||||
INDEX = 1{ZERO_STR}{{FMT}:ZERO_NUM}
|
||||
;\ Defines ITEM_100 as "\[rs]"hundredth\[rs]""
|
||||
{NAME}_{d:INDEX} equs "\[rs]"hundredth\[rs]""
|
||||
;\ Prints "ITEM_100 is hundredth"
|
||||
PRINTT STRCAT("{NAME}_{d:INDEX} is ", {NAME}_{d:INDEX})
|
||||
;\ Purges ITEM_100
|
||||
PURGE {NAME}_{d:INDEX}
|
||||
.Ed
|
||||
.Pp
|
||||
The following functions operate on string expressions.
|
||||
Most of them return a string, however some of these functions actually return an integer and can be used as part of an integer expression!
|
||||
|
||||
15
test/asm/interpolation.asm
Normal file
15
test/asm/interpolation.asm
Normal file
@@ -0,0 +1,15 @@
|
||||
SECTION "Test", ROM0
|
||||
|
||||
NAME equs "ITEM"
|
||||
FMT equs "d"
|
||||
ZERO_NUM equ 0
|
||||
ZERO_STR equs "0"
|
||||
; Defines INDEX as 100
|
||||
INDEX = 1{ZERO_STR}{{FMT}:ZERO_NUM}
|
||||
; Defines ITEM_100 as "\"hundredth\""
|
||||
{NAME}_{d:INDEX} equs "\"hundredth\""
|
||||
; Prints "ITEM_100 is hundredth"
|
||||
PRINTT STRCAT("{NAME}_{d:INDEX} is ", {NAME}_{d:INDEX}, "\n")
|
||||
; Purges ITEM_100
|
||||
PURGE {NAME}_{d:INDEX}
|
||||
ASSERT !DEF({NAME}_{d:INDEX})
|
||||
0
test/asm/interpolation.err
Normal file
0
test/asm/interpolation.err
Normal file
1
test/asm/interpolation.out
Normal file
1
test/asm/interpolation.out
Normal file
@@ -0,0 +1 @@
|
||||
ITEM_100 is hundredth
|
||||
Reference in New Issue
Block a user