ref: ce58f6d6be1ea540930211a4d80de157a6e1f943
parent: 5aabb915ecdb8dff5fc38489b9d4d84c615d73d6
author: Rangi <remy.oukaour+rangi42@gmail.com>
date: Fri Dec 11 09:18:58 EST 2020
Allow {symbol} interpolation outside of strings Fixes #629 Closes #631
--- a/src/asm/lexer.c
+++ b/src/asm/lexer.c
@@ -332,6 +332,7 @@
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 @@
state->captureBuf = NULL;
state->disableMacroArgs = false;
+ state->disableInterpolation = false;
state->macroArgScanDistance = 0;
state->expandStrings = true;
state->expansions = NULL;
@@ -767,16 +769,23 @@
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 @@
} 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 @@
{
dbgPrint("Discarding block comment\n");
lexerState->disableMacroArgs = true;
+ lexerState->disableInterpolation = true;
for (;;) {
switch (nextChar()) {
case EOF:
@@ -950,6 +971,7 @@
}
finish:
lexerState->disableMacroArgs = false;
+ lexerState->disableInterpolation = false;
}
/* Function to discard all of a line's comments */
@@ -958,6 +980,7 @@
{
dbgPrint("Discarding comment\n");
lexerState->disableMacroArgs = true;
+ lexerState->disableInterpolation = true;
for (;;) {
int c = peek(0);
@@ -966,6 +989,7 @@
shiftChars(1);
}
lexerState->disableMacroArgs = false;
+ lexerState->disableInterpolation = false;
}
/* Function to read a line continuation */
@@ -1267,14 +1291,11 @@
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 @@
size_t i = 0;
dbgPrint("Reading string\n");
+ lexerState->disableInterpolation = true;
for (;;) {
int c = peek(0);
@@ -1354,7 +1376,7 @@
}
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 @@
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 @@
yylval.tzString[i++] = c;
shiftChars(1);
}
+
+finish:
+ lexerState->disableInterpolation = false;
}
/* Function to report one character's worth of garbage bytes */
@@ -1812,19 +1837,6 @@
}
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 @@
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 @@
finish:
lexerState->disableMacroArgs = false;
+ lexerState->disableInterpolation = false;
lexerState->atLineStart = false;
return token;
@@ -1978,6 +1992,7 @@
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 @@
*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 @@
*size = lexerState->captureSize - strlen("ENDM");
lexerState->captureBuf = NULL;
lexerState->disableMacroArgs = false;
+ lexerState->disableInterpolation = false;
}
--- a/src/asm/rgbasm.5
+++ b/src/asm/rgbasm.5
@@ -251,9 +251,9 @@
.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 @@
.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!
--- /dev/null
+++ b/test/asm/interpolation.asm
@@ -1,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})
--- /dev/null
+++ b/test/asm/interpolation.out
@@ -1,0 +1,1 @@
+ITEM_100 is hundredth