mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 18:22:07 +00:00
Add EQUS expansion
This commit is contained in:
131
src/asm/lexer.c
131
src/asm/lexer.c
@@ -228,10 +228,12 @@ static struct KeywordMapping {
|
|||||||
static_assert(LEXER_BUF_SIZE <= SSIZE_MAX);
|
static_assert(LEXER_BUF_SIZE <= SSIZE_MAX);
|
||||||
|
|
||||||
struct Expansion {
|
struct Expansion {
|
||||||
uint8_t distance; /* How far the expansion's beginning is from the current position */
|
struct Expansion *firstChild;
|
||||||
|
struct Expansion *next;
|
||||||
char const *contents;
|
char const *contents;
|
||||||
size_t len;
|
size_t len;
|
||||||
struct Expansion *parent;
|
uint8_t distance; /* Distance between the beginning of this expansion and of its parent */
|
||||||
|
uint8_t skip; /* How many extra characters to skip after the expansion is over */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct LexerState {
|
struct LexerState {
|
||||||
@@ -266,7 +268,8 @@ struct LexerState {
|
|||||||
|
|
||||||
size_t nbChars; /* Number of chars of lookahead, for processing expansions */
|
size_t nbChars; /* Number of chars of lookahead, for processing expansions */
|
||||||
bool expandStrings;
|
bool expandStrings;
|
||||||
struct Expansion *expansion;
|
struct Expansion *expansions;
|
||||||
|
size_t expansionOfs; /* Offset into the current top-level expansion (negative = before) */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct LexerState *lexerState = NULL;
|
struct LexerState *lexerState = NULL;
|
||||||
@@ -349,7 +352,8 @@ struct LexerState *lexer_OpenFile(char const *path)
|
|||||||
|
|
||||||
state->nbChars = 0;
|
state->nbChars = 0;
|
||||||
state->expandStrings = true;
|
state->expandStrings = true;
|
||||||
state->expansion = NULL;
|
state->expansions = NULL;
|
||||||
|
state->expansionOfs = 0;
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -453,6 +457,67 @@ static void reallocCaptureBuf(void)
|
|||||||
fatalerror("realloc error while resizing capture buffer: %s\n", strerror(errno));
|
fatalerror("realloc error while resizing capture buffer: %s\n", strerror(errno));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct Expansion *getExpansionAtDistance(size_t *distance)
|
||||||
|
{
|
||||||
|
struct Expansion *expansion = lexerState->expansions;
|
||||||
|
struct Expansion *prevLevel = NULL; /* Top level has no "previous" level */
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
/* Find the closest expansion whose end is after the target */
|
||||||
|
while (expansion && expansion->len - expansion->distance <= *distance) {
|
||||||
|
*distance -= expansion->skip;
|
||||||
|
expansion = expansion->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If there is none, or it begins after the target, return the previous level */
|
||||||
|
if (!expansion || expansion->distance > *distance)
|
||||||
|
return prevLevel;
|
||||||
|
|
||||||
|
/* We know we are inside of that expansion */
|
||||||
|
*distance -= expansion->distance; /* Distances are relative to their parent */
|
||||||
|
|
||||||
|
if (!expansion->firstChild) /* If there are no children, this is it */
|
||||||
|
return expansion;
|
||||||
|
/* Otherwise, register this expansion and repeat the process */
|
||||||
|
prevLevel = expansion;
|
||||||
|
expansion = expansion->firstChild;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void beginExpansion(size_t distance, uint8_t skip, char const *str, size_t size)
|
||||||
|
{
|
||||||
|
struct Expansion *parent = getExpansionAtDistance(&distance);
|
||||||
|
struct Expansion **insertPoint = parent ? &parent->firstChild : &lexerState->expansions;
|
||||||
|
|
||||||
|
/* We cannot be *inside* of any of these expansions, so just keep the list sorted */
|
||||||
|
while (*insertPoint && (*insertPoint)->distance < distance)
|
||||||
|
insertPoint = &(*insertPoint)->next;
|
||||||
|
|
||||||
|
*insertPoint = malloc(sizeof(**insertPoint));
|
||||||
|
if (!*insertPoint)
|
||||||
|
fatalerror("Unable to allocate new expansion: %s", strerror(errno));
|
||||||
|
(*insertPoint)->firstChild = NULL;
|
||||||
|
(*insertPoint)->next = NULL; /* Expansions are always performed left to right */
|
||||||
|
(*insertPoint)->contents = str;
|
||||||
|
(*insertPoint)->len = size;
|
||||||
|
(*insertPoint)->distance = distance;
|
||||||
|
(*insertPoint)->skip = skip;
|
||||||
|
|
||||||
|
/* If expansion is the new closest one, update offset */
|
||||||
|
if (insertPoint == &lexerState->expansions)
|
||||||
|
lexerState->expansionOfs = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void freeExpansion(struct Expansion *expansion)
|
||||||
|
{
|
||||||
|
do {
|
||||||
|
struct Expansion *next = expansion->next;
|
||||||
|
|
||||||
|
free(expansion);
|
||||||
|
expansion = next;
|
||||||
|
} while (expansion);
|
||||||
|
}
|
||||||
|
|
||||||
/* If at any point we need more than 255 characters of lookahead, something went VERY wrong. */
|
/* If at any point we need more than 255 characters of lookahead, something went VERY wrong. */
|
||||||
static int peek(uint8_t distance)
|
static int peek(uint8_t distance)
|
||||||
{
|
{
|
||||||
@@ -460,6 +525,16 @@ static int peek(uint8_t distance)
|
|||||||
fatalerror("Internal lexer error: buffer has insufficient size for peeking (%u >= %u)\n",
|
fatalerror("Internal lexer error: buffer has insufficient size for peeking (%u >= %u)\n",
|
||||||
distance, LEXER_BUF_SIZE);
|
distance, LEXER_BUF_SIZE);
|
||||||
|
|
||||||
|
size_t ofs = lexerState->expansionOfs + distance;
|
||||||
|
struct Expansion const *expansion = getExpansionAtDistance(&ofs);
|
||||||
|
|
||||||
|
if (expansion) {
|
||||||
|
assert(distance < expansion->len);
|
||||||
|
return expansion->contents[ofs];
|
||||||
|
}
|
||||||
|
|
||||||
|
distance = ofs - lexerState->expansionOfs;
|
||||||
|
|
||||||
if (lexerState->isMmapped) {
|
if (lexerState->isMmapped) {
|
||||||
if (lexerState->offset + distance >= lexerState->size)
|
if (lexerState->offset + distance >= lexerState->size)
|
||||||
return EOF;
|
return EOF;
|
||||||
@@ -579,6 +654,42 @@ static void shiftChars(uint8_t distance)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The logic is as follows:
|
||||||
|
* - Any characters up to the expansion need to be consumed in the file
|
||||||
|
* - If some remain after that, advance the offset within the expansion
|
||||||
|
* - If that goes *past* the expansion, then leftovers shall be consumed in the file
|
||||||
|
* - If we went past the expansion, we're back to square one, and should re-do all
|
||||||
|
*/
|
||||||
|
nextExpansion:
|
||||||
|
if (lexerState->expansions) {
|
||||||
|
/* If the read cursor reaches into the expansion, update offset */
|
||||||
|
if (distance > lexerState->expansions->distance) {
|
||||||
|
/* distance = <file chars (expansion distance)> + <expansion chars> */
|
||||||
|
lexerState->expansionOfs += distance - lexerState->expansions->distance;
|
||||||
|
distance = lexerState->expansions->distance; /* Nb chars to read in file */
|
||||||
|
/* Now, check if the expansion finished being read */
|
||||||
|
if (lexerState->expansionOfs >= lexerState->expansions->len) {
|
||||||
|
/* Add the leftovers to the distance */
|
||||||
|
distance += lexerState->expansionOfs - lexerState->expansions->len;
|
||||||
|
/* Also add in the post-expansion skip */
|
||||||
|
distance += lexerState->expansions->skip;
|
||||||
|
/* Move on to the next expansion */
|
||||||
|
struct Expansion *next = lexerState->expansions->next;
|
||||||
|
|
||||||
|
freeExpansion(lexerState->expansions);
|
||||||
|
lexerState->expansions = next;
|
||||||
|
/* Reset the offset for the next expansion */
|
||||||
|
lexerState->expansionOfs = 0;
|
||||||
|
/* And repeat, in case we also go into or over the next expansion */
|
||||||
|
goto nextExpansion;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Getting closer to the expansion */
|
||||||
|
lexerState->expansions->distance -= distance;
|
||||||
|
/* Now, `distance` is how many bytes to move forward **in the file** */
|
||||||
|
}
|
||||||
|
|
||||||
if (lexerState->isMmapped) {
|
if (lexerState->isMmapped) {
|
||||||
lexerState->offset += distance;
|
lexerState->offset += distance;
|
||||||
} else {
|
} else {
|
||||||
@@ -1261,7 +1372,17 @@ static int yylex_NORMAL(void)
|
|||||||
if (tokenType != T_ID && tokenType != T_LOCAL_ID)
|
if (tokenType != T_ID && tokenType != T_LOCAL_ID)
|
||||||
return tokenType;
|
return tokenType;
|
||||||
|
|
||||||
/* TODO: attempt string expansion */
|
if (lexerState->expandStrings) {
|
||||||
|
/* Attempt string expansion */
|
||||||
|
struct Symbol const *sym = sym_FindSymbol(yylval.tzSym);
|
||||||
|
|
||||||
|
if (sym && sym->type == SYM_EQUS) {
|
||||||
|
char const *s = sym_GetStringValue(sym);
|
||||||
|
|
||||||
|
beginExpansion(0, 0, s, strlen(s));
|
||||||
|
continue; /* Restart, reading from the new buffer */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (tokenType == T_ID && lexerState->atLineStart)
|
if (tokenType == T_ID && lexerState->atLineStart)
|
||||||
return T_LABEL;
|
return T_LABEL;
|
||||||
|
|||||||
Reference in New Issue
Block a user