Allow {symbol} interpolation outside of strings

Fixes #629

Closes #631
This commit is contained in:
Rangi
2020-12-11 14:18:58 -05:00
committed by Eldred Habert
parent 5aabb915ec
commit ce58f6d6be
5 changed files with 76 additions and 29 deletions

View File

@@ -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;
} }

View File

@@ -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!

View 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})

View File

View File

@@ -0,0 +1 @@
ITEM_100 is hundredth