From 944c5f0cd0a677f18390fa9367cb44d6aa680d2f Mon Sep 17 00:00:00 2001 From: Quinn <3379314+quinnyo@users.noreply.github.com> Date: Thu, 26 Oct 2023 06:34:56 +1000 Subject: [PATCH] Implement order-independent purge (#1173) Each symbol passed to purge is collected in a list before mass removal. Fixes the issue described in gbdev/rgbds issue #1152. --------- Co-authored-by: Rangi42 --- include/asm/lexer.h | 7 ++++++ src/asm/parser.y | 50 ++++++++++++++++++++++++++++++++----- test/asm/purge-deferred.asm | 11 ++++++++ test/asm/purge-deferred.err | 0 test/asm/purge-deferred.out | 0 5 files changed, 62 insertions(+), 6 deletions(-) create mode 100644 test/asm/purge-deferred.asm create mode 100644 test/asm/purge-deferred.err create mode 100644 test/asm/purge-deferred.out diff --git a/include/asm/lexer.h b/include/asm/lexer.h index a88b66bd8..91bb0a8ec 100644 --- a/include/asm/lexer.h +++ b/include/asm/lexer.h @@ -97,4 +97,11 @@ struct DsArgList { struct Expression *args; }; +#define INITIAL_PURGE_ARG_SIZE 2 +struct PurgeArgList { + size_t nbArgs; + size_t capacity; + char **args; +}; + #endif // RGBDS_ASM_LEXER_H diff --git a/src/asm/parser.y b/src/asm/parser.y index 21d6a5362..89d6164a1 100644 --- a/src/asm/parser.y +++ b/src/asm/parser.y @@ -398,6 +398,35 @@ static void freeDsArgList(struct DsArgList *args) free(args->args); } +static void initPurgeArgList(struct PurgeArgList *args) +{ + args->nbArgs = 0; + args->capacity = INITIAL_PURGE_ARG_SIZE; + args->args = malloc(args->capacity * sizeof(*args->args)); + if (!args->args) + fatalerror("Failed to allocate memory for purge arg list: %s\n", + strerror(errno)); +} + +static void appendPurgeArgList(struct PurgeArgList *args, char *arg) +{ + if (args->nbArgs == args->capacity) { + args->capacity = (args->capacity + 1) * 2; + args->args = realloc(args->args, args->capacity * sizeof(*args->args)); + if (!args->args) + fatalerror("realloc error while resizing purge arg list: %s\n", + strerror(errno)); + } + args->args[args->nbArgs++] = arg; +} + +static void freePurgeArgList(struct PurgeArgList *args) +{ + for (size_t i = 0; i < args->nbArgs; i++) + free(args->args[i]); + free(args->args); +} + static void failAssert(enum AssertionType type) { switch (type) { @@ -481,6 +510,7 @@ enum { struct MacroArgs *macroArg; enum AssertionType assertType; struct DsArgList dsArgs; + struct PurgeArgList purgeArgs; struct { int32_t start; int32_t stop; @@ -636,6 +666,8 @@ enum { %type ds_args +%type purge_args + %type for_args %token T_Z80_ADC "adc" T_Z80_ADD "add" T_Z80_AND "and" @@ -1199,16 +1231,22 @@ redef_equs : redef_id T_POP_EQUS string { sym_RedefString($1, $3); } purge : T_POP_PURGE { lexer_ToggleStringExpansion(false); - } purge_list trailing_comma { + } purge_args trailing_comma { + for (uint32_t i = 0; i < $3.nbArgs; i++) + sym_Purge($3.args[i]); + freePurgeArgList(&$3); lexer_ToggleStringExpansion(true); } ; -purge_list : purge_list_entry - | purge_list T_COMMA purge_list_entry -; - -purge_list_entry : scoped_id { sym_Purge($1); } +purge_args : scoped_id { + initPurgeArgList(&$$); + appendPurgeArgList(&$$, strdup($1)); + } + | purge_args T_COMMA scoped_id { + appendPurgeArgList(&$1, strdup($3)); + $$ = $1; + } ; export : T_POP_EXPORT export_list trailing_comma diff --git a/test/asm/purge-deferred.asm b/test/asm/purge-deferred.asm new file mode 100644 index 000000000..07ee0c012 --- /dev/null +++ b/test/asm/purge-deferred.asm @@ -0,0 +1,11 @@ +DEF prefix EQUS "cool" +DEF {prefix}banana EQU 1 + +ASSERT DEF(prefix) +ASSERT DEF(coolbanana) + +; purging `prefix` should not prevent expanding it to purge `coolbanana` +PURGE prefix, {prefix}banana + +ASSERT !DEF(prefix) +ASSERT !DEF(coolbanana) diff --git a/test/asm/purge-deferred.err b/test/asm/purge-deferred.err new file mode 100644 index 000000000..e69de29bb diff --git a/test/asm/purge-deferred.out b/test/asm/purge-deferred.out new file mode 100644 index 000000000..e69de29bb