Fix nested expansions being incorrectly handled

The biggest problem was simply that the length of children expansions was
not accounted for when skipping over the parent... this took a lot of
arduous debugging, but it finally works!
This commit is contained in:
ISSOtm
2020-07-30 19:06:47 +02:00
parent 61b2fd9816
commit fed252bc49
5 changed files with 80 additions and 53 deletions

View File

@@ -234,7 +234,8 @@ struct Expansion {
char *name; char *name;
char const *contents; char const *contents;
size_t len; size_t len;
uint8_t distance; /* Distance between the beginning of this expansion and of its parent */ size_t totalLen;
size_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 */ uint8_t skip; /* How many extra characters to skip after the expansion is over */
}; };
@@ -459,44 +460,77 @@ 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));
} }
/*
* The multiple evaluations of `retvar` causing side effects is INTENTIONAL, and
* required for example by `lexer_dumpStringExpansions`. It is however only
* evaluated once per level, and only then.
*
* This uses the concept of "X macros": you must #define LOOKUP_PRE_NEST and
* LOOKUP_POST_NEST before invoking this (and #undef them right after), and
* those macros will be expanded at the corresponding points in the loop.
* This is necessary because there are at least 3 places which need to iterate
* through iterations while performing custom actions
*/
#define lookupExpansion(retvar, dist) do { \
struct Expansion *exp = lexerState->expansions; \
\
for (;;) { \
/* Find the closest expansion whose end is after the target */ \
while (exp && exp->totalLen + exp->distance <= (dist)) { \
(dist) -= exp->totalLen + exp->skip; \
exp = exp->next; \
} \
\
/* If there is none, or it begins after the target, return the previous level */ \
if (!exp || exp->distance > (dist)) \
break; \
\
/* We know we are inside of that expansion */ \
(dist) -= exp->distance; /* Distances are relative to their parent */ \
\
/* Otherwise, register this expansion and repeat the process */ \
LOOKUP_PRE_NEST(exp); \
(retvar) = exp; \
if (!exp->firstChild) /* If there are no children, this is it */ \
break; \
exp = exp->firstChild; \
\
LOOKUP_POST_NEST(exp); \
} \
} while (0)
static struct Expansion *getExpansionAtDistance(size_t *distance) static struct Expansion *getExpansionAtDistance(size_t *distance)
{ {
struct Expansion *expansion = lexerState->expansions;
struct Expansion *prevLevel = NULL; /* Top level has no "previous" level */
unsigned int depth = 0; unsigned int depth = 0;
struct Expansion *expansion = NULL; /* Top level has no "previous" level */
for (;;) { #define LOOKUP_PRE_NEST(exp)
/* Find the closest expansion whose end is after the target */ #define LOOKUP_POST_NEST(exp) do { \
while (expansion && expansion->len - expansion->distance <= *distance) { if (depth++ > nMaxRecursionDepth) \
*distance -= expansion->skip; fatalerror("Recursion limit (%u) exceeded", nMaxRecursionDepth); \
expansion = expansion->next; } while (0)
} lookupExpansion(expansion, *distance);
#undef LOOKUP_PRE_NEST
#undef LOOKUP_POST_NEST
/* If there is none, or it begins after the target, return the previous level */ return expansion;
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;
if (depth++ > nMaxRecursionDepth)
fatalerror("Recursion limit (%u) exceeded", nMaxRecursionDepth);
}
} }
static void beginExpansion(size_t distance, uint8_t skip, static void beginExpansion(size_t distance, uint8_t skip,
char const *str, size_t size, char const *name) char const *str, size_t size, char const *name)
{ {
struct Expansion *parent = getExpansionAtDistance(&distance); distance += lexerState->expansionOfs; /* Distance argument is relative to read offset! */
/* Increase the total length of all parents, and return the topmost one */
struct Expansion *parent = NULL;
#define LOOKUP_PRE_NEST(exp) (exp)->totalLen += size
#define LOOKUP_POST_NEST(exp)
lookupExpansion(parent, distance);
#undef LOOKUP_PRE_NEST
#undef LOOKUP_POST_NEST
struct Expansion **insertPoint = parent ? &parent->firstChild : &lexerState->expansions; struct Expansion **insertPoint = parent ? &parent->firstChild : &lexerState->expansions;
/* We cannot be *inside* of any of these expansions, so just keep the list sorted */ /* We know we are in none of the children expansions: add ourselves, keeping it sorted */
while (*insertPoint && (*insertPoint)->distance < distance) while (*insertPoint && (*insertPoint)->distance < distance)
insertPoint = &(*insertPoint)->next; insertPoint = &(*insertPoint)->next;
@@ -508,6 +542,7 @@ static void beginExpansion(size_t distance, uint8_t skip,
(*insertPoint)->name = strdup(name); (*insertPoint)->name = strdup(name);
(*insertPoint)->contents = str; (*insertPoint)->contents = str;
(*insertPoint)->len = size; (*insertPoint)->len = size;
(*insertPoint)->totalLen = size;
(*insertPoint)->distance = distance; (*insertPoint)->distance = distance;
(*insertPoint)->skip = skip; (*insertPoint)->skip = skip;
@@ -680,9 +715,10 @@ nextExpansion:
lexerState->expansionOfs += distance - lexerState->expansions->distance; lexerState->expansionOfs += distance - lexerState->expansions->distance;
distance = lexerState->expansions->distance; /* Nb chars to read in file */ distance = lexerState->expansions->distance; /* Nb chars to read in file */
/* Now, check if the expansion finished being read */ /* Now, check if the expansion finished being read */
if (lexerState->expansionOfs >= lexerState->expansions->len) { if (lexerState->expansionOfs >= lexerState->expansions->totalLen) {
/* Add the leftovers to the distance */ /* Add the leftovers to the distance */
distance += lexerState->expansionOfs - lexerState->expansions->len; distance += lexerState->expansionOfs;
distance -= lexerState->expansions->totalLen;
/* Also add in the post-expansion skip */ /* Also add in the post-expansion skip */
distance += lexerState->expansions->skip; distance += lexerState->expansions->skip;
/* Move on to the next expansion */ /* Move on to the next expansion */
@@ -745,32 +781,15 @@ void lexer_DumpStringExpansions(void)
{ {
if (!lexerState) if (!lexerState)
return; return;
/* This is essentially a modified copy-paste of `getExpansionAtDistance(0)` */
struct Expansion *stack[nMaxRecursionDepth]; struct Expansion *stack[nMaxRecursionDepth];
struct Expansion *expansion = lexerState->expansions;
unsigned int depth = 0; unsigned int depth = 0;
size_t distance = lexerState->expansionOfs; size_t distance = lexerState->expansionOfs;
for (;;) { #define LOOKUP_PRE_NEST(exp)
/* Find the closest expansion whose end is after the target */ #define LOOKUP_POST_NEST(exp)
while (expansion && expansion->len - expansion->distance <= distance) { lookupExpansion(stack[depth++], distance);
distance -= expansion->skip; #undef LOOKUP_PRE_NEST
expansion = expansion->next; #undef LOOKUP_POST_NEST
}
/* If there is none, or it begins after the target, return the previous level */
if (!expansion || expansion->distance > distance)
break;
/* We know we are inside of that expansion */
distance -= expansion->distance; /* Distances are relative to their parent */
stack[depth++] = expansion;
if (!expansion->firstChild)
break;
expansion = expansion->firstChild;
}
while (depth--) while (depth--)
fprintf(stderr, "while expanding symbol \"%s\"\n", stack[depth]->name); fprintf(stderr, "while expanding symbol \"%s\"\n", stack[depth]->name);

4
test/asm/equs-nest.asm Normal file
View File

@@ -0,0 +1,4 @@
X1 equs "Y1 equs \"\\\"Success!\\\\n\\\"\""
Y1 equs "Z1"
X1
PRINTT Z1

0
test/asm/equs-nest.err Normal file
View File

0
test/asm/equs-nest.out Normal file
View File

View File

@@ -1,2 +1,6 @@
recurse EQUS "recurse" recurse EQUS "recurse "
recurse recurse
; FIXME: also handle the following:
; recurse EQUS "recurse"
; recurse