shithub: rgbds

Download patch

ref: 9d2d5cfcfe4d647cb0a8a5363653e05b115f9940
parent: 18f3c8ff9aa4e0b7cf4c565dbc585b53f3c5efdb
author: Rangi <[email protected]>
date: Fri Jan 1 09:28:59 EST 2021

Implement `REDEF` to allow redefining `EQUS` string equates

Fixes #677

--- a/include/asm/symbol.h
+++ b/include/asm/symbol.h
@@ -140,6 +140,7 @@
 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);
 
--- a/src/asm/lexer.c
+++ b/src/asm/lexer.c
@@ -270,6 +270,7 @@
 	{"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 @@
 	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)
--- a/src/asm/parser.y
+++ b/src/asm/parser.y
@@ -425,6 +425,7 @@
 %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 @@
 		| warn
 		| assert
 		| purge
+		| redef
 		| pops
 		| pushs
 		| popo
@@ -874,6 +876,15 @@
 			lexer_ToggleStringExpansion(false);
 		} purge_list {
 			lexer_ToggleStringExpansion(true);
+		}
+;
+
+redef		: T_POP_REDEF {
+			lexer_ToggleStringExpansion(false);
+		} scoped_id {
+			lexer_ToggleStringExpansion(true);
+		} T_POP_EQUS string {
+			sym_RedefString($3, $6);
 		}
 ;
 
--- a/src/asm/rgbasm.5
+++ b/src/asm/rgbasm.5
@@ -929,7 +929,7 @@
 .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,7 +1008,22 @@
 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
--- a/src/asm/symbol.c
+++ b/src/asm/symbol.c
@@ -222,6 +222,19 @@
 		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 @@
 		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_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;
 }
--- /dev/null
+++ b/test/asm/redef-equs.asm
@@ -1,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"
--- /dev/null
+++ b/test/asm/redef-equs.err
@@ -1,0 +1,3 @@
+ERROR: redef-equs.asm(23):
+    'N' already defined as non-EQUS at redef-equs.asm(22)
+error: Assembly aborted (1 errors)!
--- /dev/null
+++ b/test/asm/redef-equs.out
@@ -1,0 +1,3 @@
+Hello, world!
+[]
+[1;A;2;B;]