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 */
|
size_t captureCapacity; /* Size of the buffer above */
|
||||||
|
|
||||||
bool disableMacroArgs;
|
bool disableMacroArgs;
|
||||||
|
bool disableInterpolation;
|
||||||
size_t macroArgScanDistance; /* Max distance already scanned for macro args */
|
size_t macroArgScanDistance; /* Max distance already scanned for macro args */
|
||||||
bool expandStrings;
|
bool expandStrings;
|
||||||
struct Expansion *expansions;
|
struct Expansion *expansions;
|
||||||
@@ -351,6 +352,7 @@ static void initState(struct LexerState *state)
|
|||||||
state->captureBuf = NULL;
|
state->captureBuf = NULL;
|
||||||
|
|
||||||
state->disableMacroArgs = false;
|
state->disableMacroArgs = false;
|
||||||
|
state->disableInterpolation = false;
|
||||||
state->macroArgScanDistance = 0;
|
state->macroArgScanDistance = 0;
|
||||||
state->expandStrings = true;
|
state->expandStrings = true;
|
||||||
state->expansions = NULL;
|
state->expansions = NULL;
|
||||||
@@ -767,16 +769,23 @@ static int peekInternal(uint8_t distance)
|
|||||||
return (unsigned char)lexerState->buf[(lexerState->index + distance) % LEXER_BUF_SIZE];
|
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)
|
static int peek(uint8_t distance)
|
||||||
{
|
{
|
||||||
int c = peekInternal(distance);
|
int c;
|
||||||
|
|
||||||
|
restart:
|
||||||
|
c = peekInternal(distance);
|
||||||
|
|
||||||
if (distance >= lexerState->macroArgScanDistance) {
|
if (distance >= lexerState->macroArgScanDistance) {
|
||||||
lexerState->macroArgScanDistance = distance + 1; /* Do not consider again */
|
lexerState->macroArgScanDistance = distance + 1; /* Do not consider again */
|
||||||
/* If enabled and character is a backslash, check for a macro arg */
|
if (c == '\\' && !lexerState->disableMacroArgs) {
|
||||||
if (!lexerState->disableMacroArgs && c == '\\') {
|
/* If character is a backslash, check for a macro arg */
|
||||||
distance++;
|
|
||||||
lexerState->macroArgScanDistance++;
|
lexerState->macroArgScanDistance++;
|
||||||
|
distance++;
|
||||||
c = peekInternal(distance);
|
c = peekInternal(distance);
|
||||||
if (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 */
|
||||||
@@ -794,8 +803,19 @@ static int peek(uint8_t distance)
|
|||||||
} else {
|
} else {
|
||||||
c = '\\';
|
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;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -927,6 +947,7 @@ static void discardBlockComment(void)
|
|||||||
{
|
{
|
||||||
dbgPrint("Discarding block comment\n");
|
dbgPrint("Discarding block comment\n");
|
||||||
lexerState->disableMacroArgs = true;
|
lexerState->disableMacroArgs = true;
|
||||||
|
lexerState->disableInterpolation = true;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
switch (nextChar()) {
|
switch (nextChar()) {
|
||||||
case EOF:
|
case EOF:
|
||||||
@@ -950,6 +971,7 @@ static void discardBlockComment(void)
|
|||||||
}
|
}
|
||||||
finish:
|
finish:
|
||||||
lexerState->disableMacroArgs = false;
|
lexerState->disableMacroArgs = false;
|
||||||
|
lexerState->disableInterpolation = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Function to discard all of a line's comments */
|
/* Function to discard all of a line's comments */
|
||||||
@@ -958,6 +980,7 @@ static void discardComment(void)
|
|||||||
{
|
{
|
||||||
dbgPrint("Discarding comment\n");
|
dbgPrint("Discarding comment\n");
|
||||||
lexerState->disableMacroArgs = true;
|
lexerState->disableMacroArgs = true;
|
||||||
|
lexerState->disableInterpolation = true;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
int c = peek(0);
|
int c = peek(0);
|
||||||
|
|
||||||
@@ -966,6 +989,7 @@ static void discardComment(void)
|
|||||||
shiftChars(1);
|
shiftChars(1);
|
||||||
}
|
}
|
||||||
lexerState->disableMacroArgs = false;
|
lexerState->disableMacroArgs = false;
|
||||||
|
lexerState->disableInterpolation = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Function to read a line continuation */
|
/* Function to read a line continuation */
|
||||||
@@ -1267,14 +1291,11 @@ static char const *readInterpolation(void)
|
|||||||
|
|
||||||
if (c == '{') { /* Nested interpolation */
|
if (c == '{') { /* Nested interpolation */
|
||||||
shiftChars(1);
|
shiftChars(1);
|
||||||
char const *inner = readInterpolation();
|
char const *ptr = readInterpolation();
|
||||||
|
|
||||||
if (inner) {
|
if (ptr) {
|
||||||
while (*inner) {
|
beginExpansion(0, 0, ptr, strlen(ptr), ptr);
|
||||||
if (i == sizeof(symName))
|
continue; /* Restart, reading from the new buffer */
|
||||||
break;
|
|
||||||
symName[i++] = *inner++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if (c == EOF || c == '\r' || c == '\n' || c == '"') {
|
} else if (c == EOF || c == '\r' || c == '\n' || c == '"') {
|
||||||
error("Missing }\n");
|
error("Missing }\n");
|
||||||
@@ -1342,6 +1363,7 @@ static void readString(void)
|
|||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
|
|
||||||
dbgPrint("Reading string\n");
|
dbgPrint("Reading string\n");
|
||||||
|
lexerState->disableInterpolation = true;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
int c = peek(0);
|
int c = peek(0);
|
||||||
|
|
||||||
@@ -1354,7 +1376,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);
|
||||||
return;
|
goto finish;
|
||||||
case '\r':
|
case '\r':
|
||||||
case '\n': /* Do not shift these! */
|
case '\n': /* Do not shift these! */
|
||||||
case EOF:
|
case EOF:
|
||||||
@@ -1365,7 +1387,7 @@ static void readString(void)
|
|||||||
yylval.tzString[i] = '\0';
|
yylval.tzString[i] = '\0';
|
||||||
error("Unterminated string\n");
|
error("Unterminated string\n");
|
||||||
dbgPrint("Read string \"%s\"\n", yylval.tzString);
|
dbgPrint("Read string \"%s\"\n", yylval.tzString);
|
||||||
return;
|
goto finish;
|
||||||
|
|
||||||
case '\\': /* Character escape */
|
case '\\': /* Character escape */
|
||||||
c = peek(1);
|
c = peek(1);
|
||||||
@@ -1426,6 +1448,9 @@ static void readString(void)
|
|||||||
yylval.tzString[i++] = c;
|
yylval.tzString[i++] = c;
|
||||||
shiftChars(1);
|
shiftChars(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
finish:
|
||||||
|
lexerState->disableInterpolation = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Function to report one character's worth of garbage bytes */
|
/* Function to report one character's worth of garbage bytes */
|
||||||
@@ -1812,19 +1837,6 @@ static int yylex_RAW(void)
|
|||||||
}
|
}
|
||||||
break;
|
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 */
|
/* Regular characters will just get copied */
|
||||||
}
|
}
|
||||||
if (i < sizeof(yylval.tzString)) /* Copy one extra to flag overflow */
|
if (i < sizeof(yylval.tzString)) /* Copy one extra to flag overflow */
|
||||||
@@ -1848,8 +1860,9 @@ static int skipIfBlock(bool toEndc)
|
|||||||
int token;
|
int token;
|
||||||
bool atLineStart = lexerState->atLineStart;
|
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->disableMacroArgs = true;
|
||||||
|
lexerState->disableInterpolation = true;
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (atLineStart) {
|
if (atLineStart) {
|
||||||
@@ -1910,6 +1923,7 @@ static int skipIfBlock(bool toEndc)
|
|||||||
finish:
|
finish:
|
||||||
|
|
||||||
lexerState->disableMacroArgs = false;
|
lexerState->disableMacroArgs = false;
|
||||||
|
lexerState->disableInterpolation = false;
|
||||||
lexerState->atLineStart = false;
|
lexerState->atLineStart = false;
|
||||||
|
|
||||||
return token;
|
return token;
|
||||||
@@ -1978,6 +1992,7 @@ static char *startCapture(void)
|
|||||||
lexerState->capturing = true;
|
lexerState->capturing = true;
|
||||||
lexerState->captureSize = 0;
|
lexerState->captureSize = 0;
|
||||||
lexerState->disableMacroArgs = true;
|
lexerState->disableMacroArgs = true;
|
||||||
|
lexerState->disableInterpolation = true;
|
||||||
|
|
||||||
if (lexerState->isMmapped && !lexerState->expansions) {
|
if (lexerState->isMmapped && !lexerState->expansions) {
|
||||||
return &lexerState->ptr[lexerState->offset];
|
return &lexerState->ptr[lexerState->offset];
|
||||||
@@ -2051,6 +2066,7 @@ finish:
|
|||||||
*size = lexerState->captureSize - strlen("ENDR");
|
*size = lexerState->captureSize - strlen("ENDR");
|
||||||
lexerState->captureBuf = NULL;
|
lexerState->captureBuf = NULL;
|
||||||
lexerState->disableMacroArgs = false;
|
lexerState->disableMacroArgs = false;
|
||||||
|
lexerState->disableInterpolation = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void lexer_CaptureMacroBody(char **capture, size_t *size)
|
void lexer_CaptureMacroBody(char **capture, size_t *size)
|
||||||
@@ -2116,4 +2132,5 @@ finish:
|
|||||||
*size = lexerState->captureSize - strlen("ENDM");
|
*size = lexerState->captureSize - strlen("ENDM");
|
||||||
lexerState->captureBuf = NULL;
|
lexerState->captureBuf = NULL;
|
||||||
lexerState->disableMacroArgs = false;
|
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 $
|
.Sq $
|
||||||
prepended.
|
prepended.
|
||||||
.Bd -literal -offset indent
|
.Bd -literal -offset indent
|
||||||
TOPIC equs "life, the universe, and everything"
|
TOPIC equs "life, the universe, and \[rs]"everything\[rs]""
|
||||||
ANSWER = 42
|
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"
|
PRINTT "The answer to {TOPIC} is {ANSWER}\[rs]n"
|
||||||
.Ed
|
.Ed
|
||||||
.Pp
|
.Pp
|
||||||
@@ -276,6 +276,20 @@ HINT: The
|
|||||||
.Ic {symbol}
|
.Ic {symbol}
|
||||||
construct can also be used outside strings.
|
construct can also be used outside strings.
|
||||||
The symbol's value is again inserted directly.
|
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
|
.Pp
|
||||||
The following functions operate on string expressions.
|
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!
|
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