Skip to content

Commit

Permalink
Allow {symbol} interpolation outside of strings
Browse files Browse the repository at this point in the history
Fixes #629

Closes #631
  • Loading branch information
Rangi42 committed Dec 12, 2020
1 parent 861cb55 commit cc37a0c
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 29 deletions.
72 changes: 43 additions & 29 deletions src/asm/lexer.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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 */
Expand All @@ -794,8 +803,18 @@ 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;
}

Expand Down Expand Up @@ -927,6 +946,7 @@ static void discardBlockComment(void)
{
dbgPrint("Discarding block comment\n");
lexerState->disableMacroArgs = true;
lexerState->disableInterpolation = true;
for (;;) {
switch (nextChar()) {
case EOF:
Expand All @@ -950,6 +970,7 @@ static void discardBlockComment(void)
}
finish:
lexerState->disableMacroArgs = false;
lexerState->disableInterpolation = false;
}

/* Function to discard all of a line's comments */
Expand All @@ -958,6 +979,7 @@ static void discardComment(void)
{
dbgPrint("Discarding comment\n");
lexerState->disableMacroArgs = true;
lexerState->disableInterpolation = true;
for (;;) {
int c = peek(0);

Expand All @@ -966,6 +988,7 @@ static void discardComment(void)
shiftChars(1);
}
lexerState->disableMacroArgs = false;
lexerState->disableInterpolation = false;
}

/* Function to read a line continuation */
Expand Down Expand Up @@ -1267,14 +1290,10 @@ static char const *readInterpolation(void)

if (c == '{') { /* Nested interpolation */
shiftChars(1);
char const *inner = readInterpolation();

if (inner) {
while (*inner) {
if (i == sizeof(symName))
break;
symName[i++] = *inner++;
}
char const *ptr = readInterpolation();
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");
Expand Down Expand Up @@ -1342,6 +1361,7 @@ static void readString(void)
size_t i = 0;

dbgPrint("Reading string\n");
lexerState->disableInterpolation = true;
for (;;) {
int c = peek(0);

Expand All @@ -1354,7 +1374,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:
Expand All @@ -1365,7 +1385,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);
Expand Down Expand Up @@ -1410,7 +1430,6 @@ static void readString(void)
case '{': /* Symbol interpolation */
shiftChars(1);
char const *ptr = readInterpolation();

if (ptr) {
while (*ptr) {
if (i == sizeof(yylval.tzString))
Expand All @@ -1426,6 +1445,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 */
Expand Down Expand Up @@ -1812,19 +1834,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 */
Expand All @@ -1848,8 +1857,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) {
Expand Down Expand Up @@ -1910,6 +1920,7 @@ static int skipIfBlock(bool toEndc)
finish:

lexerState->disableMacroArgs = false;
lexerState->disableInterpolation = false;
lexerState->atLineStart = false;

return token;
Expand Down Expand Up @@ -1978,6 +1989,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];
Expand Down Expand Up @@ -2051,6 +2063,7 @@ void lexer_CaptureRept(char **capture, size_t *size)
*size = lexerState->captureSize - strlen("ENDR");
lexerState->captureBuf = NULL;
lexerState->disableMacroArgs = false;
lexerState->disableInterpolation = false;
}

void lexer_CaptureMacroBody(char **capture, size_t *size)
Expand Down Expand Up @@ -2116,4 +2129,5 @@ void lexer_CaptureMacroBody(char **capture, size_t *size)
*size = lexerState->captureSize - strlen("ENDM");
lexerState->captureBuf = NULL;
lexerState->disableMacroArgs = false;
lexerState->disableInterpolation = false;
}
88 changes: 88 additions & 0 deletions test/asm/interpolation.asm
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
SECTION "Test", ROM0


p EQUS " 2 + 2 "
pq EQUS "p"
pqr EQUS "pq"
ld a, {pqr}


m EQUS "42"
dw 1{m}0, $1{m}0


x = 5
fmt EQUS "x:"
ld a, x * 2
ld a, {x} * 2
bit x, a
bit {x}, a
db $aa, x, $ff
db $aa, {{fmt}x}, $ff


s EQUS "\"HELLO\""
db $aa, s, $ff
db $aa, {s}, $ff


MyLabel:
s_mylabel EQUS "MyLabel"
s_test EQUS "\"Test\""
db BANK(MyLabel)
db BANK({s_mylabel})
db BANK({s_test})


METAFOO EQUS "FOO"
METAFOO EQUS "Hello world"
_TMP EQUS "PURGE {METAFOO}"
_TMP
PURGE _TMP, METAFOO
ASSERT !DEF(FOO) && !DEF(METAFOO)

METAFOO EQUS "FOO"
METAFOO EQUS "Hello world"
PURGE {METAFOO}, METAFOO
ASSERT !DEF(FOO) && !DEF(METAFOO)

METAFOO EQUS "FOO"
{METAFOO} EQUS "Hello world"
PURGE {METAFOO}, METAFOO
ASSERT !DEF(FOO) && !DEF(METAFOO)


TM42_MOVE = $2a
n = 42

MOVE_FOR_TM EQUS "TM{d:n}_MOVE"
db MOVE_FOR_TM
PURGE MOVE_FOR_TM

db {TM{d:n}_MOVE}


STRUCT_NAME EQUS "mystructname"
CUR_FIELD_ID = 1
mystructname_field1_name EQUS "myfieldname"
myfieldname EQUS "\"foobar\""

TMP EQUS "{STRUCT_NAME}_field{d:CUR_FIELD_ID}_name"
CONFLICTING_FIELD_NAME EQUS TMP
PRINTT "CONFLICTING_FIELD_NAME == \"{CONFLICTING_FIELD_NAME}\"\n"
PURGE TMP

CONFLICTING_FIELD_NAME_SYM EQUS {STRUCT_NAME}_field{d:CUR_FIELD_ID}_name
PRINTT "CONFLICTING_FIELD_NAME_SYM == \"{CONFLICTING_FIELD_NAME_SYM}\"\n"


_NUM_WARPS EQUS "_NUM_WARPS_1"
_NUM_WARPS = 5

_WARP_TO_NAME EQUS "_WARP_TO_NUM_{d:{_NUM_WARPS}}"
_WARP_TO_NAME EQUS "warp_to 1, 2, _WARP_TO_WIDTH"
PRINTT "_WARP_TO_NUM_5 == \"{_WARP_TO_NUM_5}\"\n"
PURGE _WARP_TO_NAME

_WARP_TO_NUM_{d:{_NUM_WARPS}}_SYM EQUS "warp_to 1, 2, _WARP_TO_WIDTH"
PRINTT "_WARP_TO_NUM_5_SYM == \"{_WARP_TO_NUM_5}_SYM\"\n"
Empty file added test/asm/interpolation.err
Empty file.
4 changes: 4 additions & 0 deletions test/asm/interpolation.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
CONFLICTING_FIELD_NAME == "foobar"
CONFLICTING_FIELD_NAME_SYM == "foobar"
_WARP_TO_NUM_5 == "warp_to 1, 2, _WARP_TO_WIDTH"
_WARP_TO_NUM_5_SYM == "warp_to 1, 2, _WARP_TO_WIDTH_SYM"
Binary file added test/asm/interpolation.out.bin
Binary file not shown.

0 comments on commit cc37a0c

Please sign in to comment.