From 122ef95d9c44d7fa9f6ce67c1bf76eb8ba4008bc Mon Sep 17 00:00:00 2001 From: Sylvie <35663410+Rangi42@users.noreply.github.com> Date: Fri, 13 Sep 2024 15:20:01 -0400 Subject: [PATCH] Implement `.` string constant for the current label scope (#1499) --- include/asm/symbol.hpp | 9 ++++--- man/rgbasm.5 | 1 + src/asm/lexer.cpp | 6 +++-- src/asm/parser.y | 1 - src/asm/symbol.cpp | 48 ++++++++++++++++++++++++++------- test/asm/builtin-reserved.asm | 20 +++++++++++++- test/asm/builtin-reserved.err | 18 ++++++++++++- test/asm/label-scope.asm | 12 +++++++++ test/asm/label-scope.err | 9 +++++++ test/asm/label-scope.out | 1 + test/asm/period.asm | 3 ++- test/asm/period.err | 6 +++-- test/asm/undefined-builtins.asm | 10 +++++++ test/asm/undefined-builtins.err | 8 ++++-- test/asm/undefined-builtins.out | 4 +++ 15 files changed, 133 insertions(+), 23 deletions(-) create mode 100644 test/asm/label-scope.asm create mode 100644 test/asm/label-scope.err create mode 100644 test/asm/label-scope.out diff --git a/include/asm/symbol.hpp b/include/asm/symbol.hpp index fa556d6e2..0b4bfae29 100644 --- a/include/asm/symbol.hpp +++ b/include/asm/symbol.hpp @@ -36,10 +36,11 @@ struct Symbol { uint32_t fileLine; // Line where the symbol was defined std::variant< - int32_t, // If isNumeric() - int32_t (*)(), // If isNumeric() and has a callback - ContentSpan, // For SYM_MACRO - std::shared_ptr // For SYM_EQUS + int32_t, // If isNumeric() + int32_t (*)(), // If isNumeric() via a callback + ContentSpan, // For SYM_MACRO + std::shared_ptr, // For SYM_EQUS + std::shared_ptr (*)() // For SYM_EQUS via a callback > data; diff --git a/man/rgbasm.5 b/man/rgbasm.5 index d59bbdad9..dcaa611f1 100644 --- a/man/rgbasm.5 +++ b/man/rgbasm.5 @@ -1526,6 +1526,7 @@ The following symbols are defined by the assembler: .Bl -column -offset indent "__ISO_8601_LOCAL__" "EQUS" .It Sy Name Ta Sy Type Ta Sy Contents .It Dv @ Ta Ic EQU Ta PC value (essentially, the current memory address) +.It Dv . Ta Ic EQUS Ta The current global label scope .It Dv _RS Ta Ic = Ta _RS Counter .It Dv _NARG Ta Ic EQU Ta Number of arguments passed to macro, updated by Ic SHIFT .It Dv __DATE__ Ta Ic EQUS Ta Today's date diff --git a/src/asm/lexer.cpp b/src/asm/lexer.cpp index 46b20564b..401ae028f 100644 --- a/src/asm/lexer.cpp +++ b/src/asm/lexer.cpp @@ -327,8 +327,6 @@ static std::unordered_map ke {"POPO", T_(POP_POPO) }, {"OPT", T_(POP_OPT) }, - - {".", T_(PERIOD) }, }; static bool isWhitespace(int c) { @@ -1173,6 +1171,10 @@ static Token readIdentifier(char firstChar, bool raw) { return Token(search->second); } + // Label scope `.` is the only nonlocal identifier that starts with a dot + if (identifier.find_first_not_of('.') == identifier.npos) + tokenType = T_(ID); + return Token(tokenType, identifier); } diff --git a/src/asm/parser.y b/src/asm/parser.y index 510d9a4a6..83d6bdaa8 100644 --- a/src/asm/parser.y +++ b/src/asm/parser.y @@ -107,7 +107,6 @@ %token EOB "end of buffer" // General punctuation -%token PERIOD "." %token COMMA "," %token COLON ":" DOUBLE_COLON "::" %token LBRACK "[" RBRACK "]" diff --git a/src/asm/symbol.cpp b/src/asm/symbol.cpp index f2551f3a4..02582b6f5 100644 --- a/src/asm/symbol.cpp +++ b/src/asm/symbol.cpp @@ -25,6 +25,7 @@ std::unordered_set purgedSymbols; static Symbol const *labelScope = nullptr; // Current section's label scope static Symbol *PCSymbol; static Symbol *NARGSymbol; +static Symbol *labelScopeSymbol; static Symbol *RSSymbol; static char savedTIME[256]; static char savedDATE[256]; @@ -50,6 +51,14 @@ static int32_t NARGCallback() { } } +static std::shared_ptr labelScopeCallback() { + if (!labelScope) { + error("\".\" has no value outside of a label scope\n"); + return std::make_shared(""); + } + return std::make_shared(labelScope->name); +} + static int32_t PCCallback() { return sect_GetSymbolSection()->org + sect_GetSymbolOffset(); } @@ -78,7 +87,12 @@ ContentSpan const &Symbol::getMacro() const { } std::shared_ptr Symbol::getEqus() const { - assume(std::holds_alternative>(data)); + assume( + std::holds_alternative>(data) + || std::holds_alternative (*)()>(data) + ); + if (auto *callback = std::get_if (*)()>(&data); callback) + return (*callback)(); return std::get>(data); } @@ -126,9 +140,14 @@ static void redefinedError(Symbol const &sym) { } } +static void assumeAlreadyExpanded(std::string const &symName) { + // Either the symbol name is `Global.local` or entirely '.'s (for global scope `.`), + // but cannot be unqualified `.local` + assume(!symName.starts_with('.') || symName.find_first_not_of('.') == symName.npos); +} + static Symbol &createSymbol(std::string const &symName) { - // The symbol name should have been expanded already - assume(!symName.starts_with('.')); + assumeAlreadyExpanded(symName); static uint32_t nextDefIndex = 0; @@ -156,6 +175,10 @@ static bool isAutoScoped(std::string const &symName) { if (dotPos == std::string::npos) return false; + // Label scope `.` is the only nonlocal identifier that starts with a dot + if (dotPos == 0 && symName.find_first_not_of('.') == symName.npos) + return false; + // Check for nothing after the dot if (dotPos == symName.length() - 1) fatalerror("'%s' is a nonsensical reference to an empty local label\n", symName.c_str()); @@ -176,8 +199,7 @@ static bool isAutoScoped(std::string const &symName) { } Symbol *sym_FindExactSymbol(std::string const &symName) { - // The symbol name should have been expanded already - assume(!symName.starts_with('.')); + assumeAlreadyExpanded(symName); auto search = symbols.find(symName); return search != symbols.end() ? &search->second : nullptr; @@ -198,6 +220,11 @@ Symbol *sym_FindScopedValidSymbol(std::string const &symName) { if (sym == NARGSymbol && !fstk_GetCurrentMacroArgs()) { return nullptr; } + // `.` has no value outside of a label scope + if (sym == labelScopeSymbol && !labelScope) { + return nullptr; + } + return sym; } @@ -231,8 +258,7 @@ void sym_Purge(std::string const &symName) { } bool sym_IsPurgedExact(std::string const &symName) { - // The symbol name should have been expanded already - assume(!symName.starts_with('.')); + assumeAlreadyExpanded(symName); return purgedSymbols.find(symName) != purgedSymbols.end(); } @@ -391,8 +417,7 @@ Symbol *sym_AddVar(std::string const &symName, int32_t value) { } static Symbol *addLabel(std::string const &symName) { - // The symbol name should have been expanded already - assume(!symName.starts_with('.')); + assumeAlreadyExpanded(symName); Symbol *sym = sym_FindExactSymbol(symName); @@ -541,6 +566,11 @@ void sym_Init(time_t now) { NARGSymbol->data = NARGCallback; NARGSymbol->isBuiltin = true; + labelScopeSymbol = &createSymbol("."s); + labelScopeSymbol->type = SYM_EQUS; + labelScopeSymbol->data = labelScopeCallback; + labelScopeSymbol->isBuiltin = true; + RSSymbol = sym_AddVar("_RS"s, 0); RSSymbol->isBuiltin = true; diff --git a/test/asm/builtin-reserved.asm b/test/asm/builtin-reserved.asm index 98f0d6baa..7ccb13167 100644 --- a/test/asm/builtin-reserved.asm +++ b/test/asm/builtin-reserved.asm @@ -11,5 +11,23 @@ REDEF _NARG = 78 DEF _NARG EQUS "hello" REDEF _NARG EQUS "world" -SECTION "test", ROM0 +SECTION "_NARG", ROM0 _NARG: +ENDSECTION + +ASSERT !DEF(.) + +PURGE . + +DEF . EQU 12 +REDEF . EQU 34 + +DEF . = 56 +REDEF . = 78 + +DEF . EQUS "hello" +REDEF . EQUS "world" + +SECTION ".", ROM0 +.: +ENDSECTION diff --git a/test/asm/builtin-reserved.err b/test/asm/builtin-reserved.err index 97ac99316..d51347c33 100644 --- a/test/asm/builtin-reserved.err +++ b/test/asm/builtin-reserved.err @@ -14,4 +14,20 @@ error: builtin-reserved.asm(12): '_NARG' is reserved for a built-in symbol error: builtin-reserved.asm(15): '_NARG' is reserved for a built-in symbol -error: Assembly aborted (8 errors)! +error: builtin-reserved.asm(20): + '.' not defined +error: builtin-reserved.asm(22): + '.' is reserved for a built-in symbol +error: builtin-reserved.asm(23): + '.' is reserved for a built-in symbol +error: builtin-reserved.asm(25): + '.' is reserved for a built-in symbol +error: builtin-reserved.asm(26): + '.' is reserved for a built-in symbol +error: builtin-reserved.asm(28): + '.' is reserved for a built-in symbol +error: builtin-reserved.asm(29): + '.' is reserved for a built-in symbol +error: builtin-reserved.asm(32): + "." has no value outside of a label scope +error: Assembly aborted (16 errors)! diff --git a/test/asm/label-scope.asm b/test/asm/label-scope.asm new file mode 100644 index 000000000..d2b56893c --- /dev/null +++ b/test/asm/label-scope.asm @@ -0,0 +1,12 @@ +ASSERT !DEF(@) && !DEF(.) + +PURGE @, . + +SECTION "test", ROM0[42] +Foobar: + +PURGE @, . + +ASSERT DEF(@) && DEF(.) && DEF(Foobar) + +PRINTLN "PC: {#05X:@}; label scope: \"{.}\"; {.}: {#05X:{.}}" diff --git a/test/asm/label-scope.err b/test/asm/label-scope.err new file mode 100644 index 000000000..a0822c247 --- /dev/null +++ b/test/asm/label-scope.err @@ -0,0 +1,9 @@ +error: label-scope.asm(3): + '@' not defined +error: label-scope.asm(3): + '.' not defined +error: label-scope.asm(8): + Built-in symbol '@' cannot be purged +error: label-scope.asm(8): + Built-in symbol '.' cannot be purged +error: Assembly aborted (4 errors)! diff --git a/test/asm/label-scope.out b/test/asm/label-scope.out new file mode 100644 index 000000000..2e0e2071c --- /dev/null +++ b/test/asm/label-scope.out @@ -0,0 +1 @@ +PC: $002A; label scope: "Foobar"; Foobar: $002A diff --git a/test/asm/period.asm b/test/asm/period.asm index 286a53e36..2350acd94 100644 --- a/test/asm/period.asm +++ b/test/asm/period.asm @@ -2,4 +2,5 @@ section "period", rom0 global1: db 1 .local db 2 -. db 3 +. db 3 ; this... +global1 db 4 ; ...expands to this diff --git a/test/asm/period.err b/test/asm/period.err index 5727b90f0..caee518c8 100644 --- a/test/asm/period.err +++ b/test/asm/period.err @@ -1,3 +1,5 @@ error: period.asm(5): - syntax error, unexpected . -error: Assembly aborted (1 error)! + syntax error, unexpected DB, expecting : or :: +error: period.asm(6): + syntax error, unexpected DB, expecting : or :: +error: Assembly aborted (2 errors)! diff --git a/test/asm/undefined-builtins.asm b/test/asm/undefined-builtins.asm index 9cac1f971..d31ee57a1 100644 --- a/test/asm/undefined-builtins.asm +++ b/test/asm/undefined-builtins.asm @@ -3,6 +3,11 @@ assert !DEF(@) println @ println "{@}?" +; not inside a label scope +assert !DEF(.) +println . +println "{.}?" + ; not inside a macro assert !DEF(_NARG) println _NARG @@ -13,6 +18,11 @@ assert DEF(@) println @ println "{@}!" +LabelScope: +assert DEF(.) +println . +println "{.}!" + MACRO m assert DEF(_NARG) println _NARG diff --git a/test/asm/undefined-builtins.err b/test/asm/undefined-builtins.err index 219b4e0e4..9eb1569ec 100644 --- a/test/asm/undefined-builtins.err +++ b/test/asm/undefined-builtins.err @@ -3,7 +3,11 @@ error: undefined-builtins.asm(3): error: undefined-builtins.asm(4): Interpolated symbol "@" does not exist error: undefined-builtins.asm(8): - _NARG has no value outside of a macro + "." has no value outside of a label scope error: undefined-builtins.asm(9): + Interpolated symbol "." does not exist +error: undefined-builtins.asm(13): + _NARG has no value outside of a macro +error: undefined-builtins.asm(14): Interpolated symbol "_NARG" does not exist -error: Assembly aborted (4 errors)! +error: Assembly aborted (6 errors)! diff --git a/test/asm/undefined-builtins.out b/test/asm/undefined-builtins.out index 42986700c..38a56b832 100644 --- a/test/asm/undefined-builtins.out +++ b/test/asm/undefined-builtins.out @@ -1,8 +1,12 @@ $0 +? + ? $0 ? $42 $42! +$42 +LabelScope! $3 $3!