From 9d2d5cfcfe4d647cb0a8a5363653e05b115f9940 Mon Sep 17 00:00:00 2001 From: Rangi Date: Fri, 1 Jan 2021 14:28:59 -0500 Subject: [PATCH] Implement `REDEF` to allow redefining `EQUS` string equates Fixes #677 --- include/asm/symbol.h | 1 + src/asm/lexer.c | 3 ++- src/asm/parser.y | 11 +++++++++ src/asm/rgbasm.5 | 17 +++++++++++++- src/asm/symbol.c | 49 +++++++++++++++++++++++++++++++++-------- test/asm/redef-equs.asm | 23 +++++++++++++++++++ test/asm/redef-equs.err | 3 +++ test/asm/redef-equs.out | 3 +++ 8 files changed, 99 insertions(+), 11 deletions(-) create mode 100644 test/asm/redef-equs.asm create mode 100644 test/asm/redef-equs.err create mode 100644 test/asm/redef-equs.out diff --git a/include/asm/symbol.h b/include/asm/symbol.h index 63578234..60248284 100644 --- a/include/asm/symbol.h +++ b/include/asm/symbol.h @@ -140,6 +140,7 @@ struct Symbol const *sym_GetPC(void); struct Symbol *sym_AddMacro(char const *symName, int32_t defLineNo, char *body, size_t size); struct Symbol *sym_Ref(char const *symName); struct Symbol *sym_AddString(char const *symName, char const *value); +struct Symbol *sym_RedefString(char const *symName, char const *value); void sym_Purge(char const *symName); void sym_Init(time_t now); diff --git a/src/asm/lexer.c b/src/asm/lexer.c index 9ac85c2a..70650cd3 100644 --- a/src/asm/lexer.c +++ b/src/asm/lexer.c @@ -270,6 +270,7 @@ static struct KeywordMapping { {"RW", T_POP_RW}, {"EQU", T_POP_EQU}, {"EQUS", T_POP_EQUS}, + {"REDEF", T_POP_REDEF}, /* Handled before in list of CPU instructions */ /* {"SET", T_POP_SET}, */ @@ -490,7 +491,7 @@ struct KeywordDictNode { uint16_t children[0x60 - ' ']; struct KeywordMapping const *keyword; /* Since the keyword structure is invariant, the min number of nodes is known at compile time */ -} keywordDict[352] = {0}; /* Make sure to keep this correct when adding keywords! */ +} keywordDict[356] = {0}; /* Make sure to keep this correct when adding keywords! */ /* Convert a char into its index into the dict */ static inline uint8_t dictIndex(char c) diff --git a/src/asm/parser.y b/src/asm/parser.y index 5d59e876..db41f3bd 100644 --- a/src/asm/parser.y +++ b/src/asm/parser.y @@ -425,6 +425,7 @@ static inline void failAssertMsg(enum AssertionType type, char const *msg) %token T_POP_FATAL %token T_POP_ASSERT T_POP_STATIC_ASSERT %token T_POP_PURGE +%token T_POP_REDEF %token T_POP_POPS %token T_POP_PUSHS %token T_POP_POPO @@ -645,6 +646,7 @@ simple_pseudoop : include | warn | assert | purge + | redef | pops | pushs | popo @@ -877,6 +879,15 @@ purge : T_POP_PURGE { } ; +redef : T_POP_REDEF { + lexer_ToggleStringExpansion(false); + } scoped_id { + lexer_ToggleStringExpansion(true); + } T_POP_EQUS string { + sym_RedefString($3, $6); + } +; + purge_list : purge_list_entry | purge_list T_COMMA purge_list_entry ; diff --git a/src/asm/rgbasm.5 b/src/asm/rgbasm.5 index b514f0e1..e71edc48 100644 --- a/src/asm/rgbasm.5 +++ b/src/asm/rgbasm.5 @@ -929,7 +929,7 @@ or its synonym .Ic = , defines constant symbols like .Ic EQU , -but those constants can be re-defined. +but those constants can be redefined. This is useful for variables in macros, for counters, etc. .Bd -literal -offset indent ARRAY_SIZE EQU 4 @@ -1008,8 +1008,23 @@ pusha EQUS "push af\[rs]npush bc\[rs]npush de\[rs]npush hl\[rs]n" Note that colons .Ql \&: following the name are not allowed. +.Pp String equates can't be exported or imported. .Pp +String equates, like +.Ic EQU +constants, cannot be redefined. +However, the +.Ic REDEF +keyword will define or redefine a string symbol. +For example: +.Bd -literal -offset indent +s EQUS "Hello, " +REDEF s EQUS "{s}world!" +; prints "Hello, world!" +PRINTT "{s}\n" +.Ed +.Pp .Sy Important note : An .Ic EQUS diff --git a/src/asm/symbol.c b/src/asm/symbol.c index 3f8262ce..2cc33ce9 100644 --- a/src/asm/symbol.c +++ b/src/asm/symbol.c @@ -222,6 +222,19 @@ static void fullSymbolName(char *output, size_t outputSize, fatalerror("Symbol name is too long: '%s%s'\n", scopeName, localName); } +static void assignStringSymbol(struct Symbol *sym, char const *value) +{ + char *string = strdup(value); + + if (string == NULL) + fatalerror("No memory for string equate: %s\n", strerror(errno)); + + sym->type = SYM_EQUS; + /* TODO: use other fields */ + sym->macro = string; + sym->macroSize = strlen(string); +} + struct Symbol *sym_FindExactSymbol(char const *name) { return hash_GetElement(symbols, name); @@ -283,6 +296,11 @@ void sym_Purge(char const *symName) if (symbol->name == labelScope) labelScope = NULL; + /* + * FIXME: this leaks symbol->macro for SYM_EQUS and SYM_MACRO, but this can't + * free(symbol->macro) because the expansion may be purging itself. + */ + hash_RemoveElement(symbols, symbol->name); /* TODO: ideally, also unref the file stack nodes */ free(symbol); @@ -390,17 +408,30 @@ struct Symbol *sym_AddEqu(char const *symName, int32_t value) struct Symbol *sym_AddString(char const *symName, char const *value) { struct Symbol *sym = createNonrelocSymbol(symName); - size_t len = strlen(value); - char *string = malloc(len + 1); - if (string == NULL) - fatalerror("No memory for string equate: %s\n", strerror(errno)); - strcpy(string, value); + assignStringSymbol(sym, value); - sym->type = SYM_EQUS; - /* TODO: use other fields */ - sym->macroSize = len; - sym->macro = string; + return sym; +} + +struct Symbol *sym_RedefString(char const *symName, char const *value) +{ + struct Symbol *sym = sym_FindExactSymbol(symName); + + if (!sym) { + sym = createsymbol(symName); + } else if (sym->type != SYM_EQUS) { + error("'%s' already defined as non-EQUS at ", symName); + dumpFilename(sym); + putc('\n', stderr); + } + + /* + * FIXME: this leaks the previous sym->macro value, but this can't + * free(sym->macro) because the expansion may be redefining itself. + */ + + assignStringSymbol(sym, value); return sym; } diff --git a/test/asm/redef-equs.asm b/test/asm/redef-equs.asm new file mode 100644 index 00000000..883e520f --- /dev/null +++ b/test/asm/redef-equs.asm @@ -0,0 +1,23 @@ +s EQUS "Hello, " +REDEF s EQUS "{s}world!" +; prints "Hello, world!" +PRINTT "{s}\n" + +list: MACRO +LIST_NAME EQUS "\1" +REDEF {LIST_NAME} EQUS "[" +REPT _NARG - 1 +REDEF {LIST_NAME} EQUS "{{LIST_NAME}}\2;" +SHIFT +ENDR +REDEF {LIST_NAME} EQUS "{{LIST_NAME}}]" +PURGE LIST_NAME +ENDM + + list FOO + PRINTT "{FOO}\n" + list FOO, 1, A, 2, B + PRINTT "{FOO}\n" + +N EQU 42 +REDEF N EQUS "X" diff --git a/test/asm/redef-equs.err b/test/asm/redef-equs.err new file mode 100644 index 00000000..5b18352c --- /dev/null +++ b/test/asm/redef-equs.err @@ -0,0 +1,3 @@ +ERROR: redef-equs.asm(23): + 'N' already defined as non-EQUS at redef-equs.asm(22) +error: Assembly aborted (1 errors)! diff --git a/test/asm/redef-equs.out b/test/asm/redef-equs.out new file mode 100644 index 00000000..32412670 --- /dev/null +++ b/test/asm/redef-equs.out @@ -0,0 +1,3 @@ +Hello, world! +[] +[1;A;2;B;]