From 9ef2e43bf7ec0921534eabaceb6edf5d283f6044 Mon Sep 17 00:00:00 2001 From: Sylvie <35663410+Rangi42@users.noreply.github.com> Date: Wed, 18 Sep 2024 09:52:30 -0400 Subject: [PATCH] Track local label scope, string equated as `..` (#1504) --- include/asm/symbol.hpp | 8 ++- man/rgbasm.5 | 1 + src/asm/lexer.cpp | 2 +- src/asm/section.cpp | 19 +++--- src/asm/symbol.cpp | 102 +++++++++++++++++++++++--------- test/asm/label-scope.asm | 18 ++++-- test/asm/label-scope.err | 10 +++- test/asm/label-scope.out | 4 +- test/asm/period.asm | 37 ++++++++++-- test/asm/period.err | 5 -- test/asm/undefined-builtins.asm | 14 ++++- test/asm/undefined-builtins.err | 8 ++- test/asm/undefined-builtins.out | 6 +- 13 files changed, 167 insertions(+), 67 deletions(-) delete mode 100644 test/asm/period.err diff --git a/include/asm/symbol.hpp b/include/asm/symbol.hpp index 0b4bfae2..4e53c2e2 100644 --- a/include/asm/symbol.hpp +++ b/include/asm/symbol.hpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include "asm/lexer.hpp" @@ -98,8 +99,9 @@ bool sym_IsPurgedExact(std::string const &symName); bool sym_IsPurgedScoped(std::string const &symName); void sym_Init(time_t now); -// Functions to save and restore the current label scope. -Symbol const *sym_GetCurrentLabelScope(); -void sym_SetCurrentLabelScope(Symbol const *newScope); +// Functions to save and restore the current label scopes. +std::pair sym_GetCurrentLabelScopes(); +void sym_SetCurrentLabelScopes(std::pair newScopes); +void sym_ResetCurrentLabelScopes(); #endif // RGBDS_ASM_SYMBOL_HPP diff --git a/man/rgbasm.5 b/man/rgbasm.5 index dcaa611f..b0d3028e 100644 --- a/man/rgbasm.5 +++ b/man/rgbasm.5 @@ -1527,6 +1527,7 @@ The following symbols are defined by the assembler: .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 .. Ta Ic EQUS Ta The current local 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 2619dcbf..71472a86 100644 --- a/src/asm/lexer.cpp +++ b/src/asm/lexer.cpp @@ -1174,7 +1174,7 @@ static Token readIdentifier(char firstChar, bool raw) { return Token(search->second); } - // Label scope `.` is the only nonlocal identifier that starts with a dot + // Label scopes `.` and `..` are the only nonlocal identifiers that start with a dot if (identifier.find_first_not_of('.') == identifier.npos) tokenType = T_(ID); diff --git a/src/asm/section.cpp b/src/asm/section.cpp index b73505fe..cd7950bb 100644 --- a/src/asm/section.cpp +++ b/src/asm/section.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include "helpers.hpp" @@ -31,7 +32,7 @@ struct UnionStackEntry { struct SectionStackEntry { Section *section; Section *loadSection; - Symbol const *scope; + std::pair labelScopes; uint32_t offset; int32_t loadOffset; std::stack unionStack; @@ -44,7 +45,7 @@ std::unordered_map sectionMap; // Indexes into `sectionList uint32_t curOffset; // Offset into the current section (see sect_GetSymbolOffset) Section *currentSection = nullptr; static Section *currentLoadSection = nullptr; -static Symbol const *currentLoadScope = nullptr; +static std::pair currentLoadLabelScopes = {nullptr, nullptr}; int32_t loadOffset; // Offset into the LOAD section's parent (see sect_GetOutputOffset) // A quick check to see if we have an initialized section @@ -395,7 +396,7 @@ static void changeSection() { if (!currentUnionStack.empty()) fatalerror("Cannot change the section within a UNION\n"); - sym_SetCurrentLabelScope(nullptr); + sym_ResetCurrentLabelScopes(); } bool Section::isSizeKnown() const { @@ -473,7 +474,7 @@ void sect_SetLoadSection( Section *sect = getSection(name, type, org, attrs, mod); - currentLoadScope = sym_GetCurrentLabelScope(); + currentLoadLabelScopes = sym_GetCurrentLabelScopes(); changeSection(); loadOffset = curOffset - (mod == SECTION_UNION ? 0 : sect->size); curOffset -= loadOffset; @@ -490,7 +491,7 @@ void sect_EndLoadSection() { curOffset += loadOffset; loadOffset = 0; currentLoadSection = nullptr; - sym_SetCurrentLabelScope(currentLoadScope); + sym_SetCurrentLabelScopes(currentLoadLabelScopes); } Section *sect_GetSymbolSection() { @@ -935,7 +936,7 @@ void sect_PushSection() { sectionStack.push_front({ .section = currentSection, .loadSection = currentLoadSection, - .scope = sym_GetCurrentLabelScope(), + .labelScopes = sym_GetCurrentLabelScopes(), .offset = curOffset, .loadOffset = loadOffset, .unionStack = {}, @@ -944,7 +945,7 @@ void sect_PushSection() { // Reset the section scope currentSection = nullptr; currentLoadSection = nullptr; - sym_SetCurrentLabelScope(nullptr); + sym_ResetCurrentLabelScopes(); std::swap(currentUnionStack, sectionStack.front().unionStack); } @@ -961,7 +962,7 @@ void sect_PopSection() { changeSection(); currentSection = entry.section; currentLoadSection = entry.loadSection; - sym_SetCurrentLabelScope(entry.scope); + sym_SetCurrentLabelScopes(entry.labelScopes); curOffset = entry.offset; loadOffset = entry.loadOffset; std::swap(currentUnionStack, entry.unionStack); @@ -979,5 +980,5 @@ void sect_EndSection() { // Reset the section scope currentSection = nullptr; - sym_SetCurrentLabelScope(nullptr); + sym_ResetCurrentLabelScopes(); } diff --git a/src/asm/symbol.cpp b/src/asm/symbol.cpp index 02582b6f..28144bd9 100644 --- a/src/asm/symbol.cpp +++ b/src/asm/symbol.cpp @@ -22,10 +22,12 @@ using namespace std::literals; std::unordered_map symbols; std::unordered_set purgedSymbols; -static Symbol const *labelScope = nullptr; // Current section's label scope +static Symbol const *globalScope = nullptr; // Current section's global label scope +static Symbol const *localScope = nullptr; // Current section's local label scope static Symbol *PCSymbol; static Symbol *NARGSymbol; -static Symbol *labelScopeSymbol; +static Symbol *globalScopeSymbol; +static Symbol *localScopeSymbol; static Symbol *RSSymbol; static char savedTIME[256]; static char savedDATE[256]; @@ -51,12 +53,20 @@ static int32_t NARGCallback() { } } -static std::shared_ptr labelScopeCallback() { - if (!labelScope) { +static std::shared_ptr globalScopeCallback() { + if (!globalScope) { error("\".\" has no value outside of a label scope\n"); return std::make_shared(""); } - return std::make_shared(labelScope->name); + return std::make_shared(globalScope->name); +} + +static std::shared_ptr localScopeCallback() { + if (!localScope) { + error("\"..\" has no value outside of a local label scope\n"); + return std::make_shared(""); + } + return std::make_shared(localScope->name); } static int32_t PCCallback() { @@ -141,7 +151,7 @@ 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 `.`), + // Either the symbol name is `Global.local` or entirely '.'s (for scopes `.` and `..`), // but cannot be unqualified `.local` assume(!symName.starts_with('.') || symName.find_first_not_of('.') == symName.npos); } @@ -166,8 +176,10 @@ static Symbol &createSymbol(std::string const &symName) { } static bool isAutoScoped(std::string const &symName) { - // `labelScope` should be global if it's defined - assume(!labelScope || labelScope->name.find('.') == std::string::npos); + // `globalScope` should be global if it's defined + assume(!globalScope || globalScope->name.find('.') == std::string::npos); + // `localScope` should be qualified local if it's defined + assume(!localScope || localScope->name.find('.') != std::string::npos); size_t dotPos = symName.find('.'); @@ -175,7 +187,7 @@ 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 + // Label scopes `.` and `..` are the only nonlocal identifiers that start with a dot if (dotPos == 0 && symName.find_first_not_of('.') == symName.npos) return false; @@ -192,7 +204,7 @@ static bool isAutoScoped(std::string const &symName) { return false; // Check for unqualifiable local label - if (!labelScope) + if (!globalScope) fatalerror("Unqualified local label '%s' in main scope\n", symName.c_str()); return true; @@ -206,7 +218,7 @@ Symbol *sym_FindExactSymbol(std::string const &symName) { } Symbol *sym_FindScopedSymbol(std::string const &symName) { - return sym_FindExactSymbol(isAutoScoped(symName) ? labelScope->name + symName : symName); + return sym_FindExactSymbol(isAutoScoped(symName) ? globalScope->name + symName : symName); } Symbol *sym_FindScopedValidSymbol(std::string const &symName) { @@ -220,8 +232,12 @@ 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) { + // `.` has no value outside of a global label scope + if (sym == globalScopeSymbol && !globalScope) { + return nullptr; + } + // `..` has no value outside of a local label scope + if (sym == localScopeSymbol && !localScope) { return nullptr; } @@ -250,8 +266,10 @@ void sym_Purge(std::string const &symName) { else if (sym->isLabel()) warning(WARNING_PURGE_2, "Purging a label \"%s\"\n", symName.c_str()); // Do not keep a reference to the label after purging it - if (labelScope == sym) - labelScope = nullptr; + if (sym == globalScope) + globalScope = nullptr; + if (sym == localScope) + localScope = nullptr; purgedSymbols.emplace(sym->name); symbols.erase(sym->name); } @@ -264,7 +282,7 @@ bool sym_IsPurgedExact(std::string const &symName) { } bool sym_IsPurgedScoped(std::string const &symName) { - return sym_IsPurgedExact(isAutoScoped(symName) ? labelScope->name + symName : symName); + return sym_IsPurgedExact(isAutoScoped(symName) ? globalScope->name + symName : symName); } int32_t sym_GetRSValue() { @@ -302,12 +320,23 @@ uint32_t sym_GetConstantValue(std::string const &symName) { return 0; } -Symbol const *sym_GetCurrentLabelScope() { - return labelScope; +std::pair sym_GetCurrentLabelScopes() { + return {globalScope, localScope}; } -void sym_SetCurrentLabelScope(Symbol const *newScope) { - labelScope = newScope; +void sym_SetCurrentLabelScopes(std::pair newScopes) { + globalScope = std::get<0>(newScopes); + localScope = std::get<1>(newScopes); + + // `globalScope` should be global if it's defined + assume(!globalScope || globalScope->name.find('.') == std::string::npos); + // `localScope` should be qualified local if it's defined + assume(!localScope || localScope->name.find('.') != std::string::npos); +} + +void sym_ResetCurrentLabelScopes() { + globalScope = nullptr; + localScope = nullptr; } static Symbol *createNonrelocSymbol(std::string const &symName, bool numeric) { @@ -447,15 +476,25 @@ Symbol *sym_AddLocalLabel(std::string const &symName) { // The symbol name should be local, qualified or not assume(symName.find('.') != std::string::npos); - return addLabel(isAutoScoped(symName) ? labelScope->name + symName : symName); + Symbol *sym = addLabel(isAutoScoped(symName) ? globalScope->name + symName : symName); + + if (sym) + localScope = sym; + + return sym; } Symbol *sym_AddLabel(std::string const &symName) { + // The symbol name should be global + assume(symName.find('.') == std::string::npos); + Symbol *sym = addLabel(symName); - // Set the symbol as the new scope - if (sym) - labelScope = sym; + if (sym) { + globalScope = sym; + // A new global scope resets the local scope + localScope = nullptr; + } return sym; } @@ -542,7 +581,7 @@ Symbol *sym_Ref(std::string const &symName) { Symbol *sym = sym_FindScopedSymbol(symName); if (!sym) { - sym = &createSymbol(isAutoScoped(symName) ? labelScope->name + symName : symName); + sym = &createSymbol(isAutoScoped(symName) ? globalScope->name + symName : symName); sym->type = SYM_REF; } @@ -566,10 +605,15 @@ 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; + globalScopeSymbol = &createSymbol("."s); + globalScopeSymbol->type = SYM_EQUS; + globalScopeSymbol->data = globalScopeCallback; + globalScopeSymbol->isBuiltin = true; + + localScopeSymbol = &createSymbol(".."s); + localScopeSymbol->type = SYM_EQUS; + localScopeSymbol->data = localScopeCallback; + localScopeSymbol->isBuiltin = true; RSSymbol = sym_AddVar("_RS"s, 0); RSSymbol->isBuiltin = true; diff --git a/test/asm/label-scope.asm b/test/asm/label-scope.asm index d2b56893..6ea225ec 100644 --- a/test/asm/label-scope.asm +++ b/test/asm/label-scope.asm @@ -1,12 +1,18 @@ -ASSERT !DEF(@) && !DEF(.) +ASSERT !DEF(@) && !DEF(.) && !DEF(..) -PURGE @, . +PURGE @, ., .. SECTION "test", ROM0[42] -Foobar: +db 1 +Foo: +db 2 +.bar +db 3 -PURGE @, . +PURGE @, ., .. -ASSERT DEF(@) && DEF(.) && DEF(Foobar) +ASSERT DEF(@) && DEF(.) && DEF(..) && DEF(Foo) && DEF(.bar) -PRINTLN "PC: {#05X:@}; label scope: \"{.}\"; {.}: {#05X:{.}}" +PRINTLN "PC: {#05X:@}" +PRINTLN "global scope: \"{.}\" ({#05X:{.}})" +PRINTLN "local scope: \"{..}\" ({#05X:{..}})" diff --git a/test/asm/label-scope.err b/test/asm/label-scope.err index a0822c24..59a09d77 100644 --- a/test/asm/label-scope.err +++ b/test/asm/label-scope.err @@ -2,8 +2,12 @@ error: label-scope.asm(3): '@' not defined error: label-scope.asm(3): '.' not defined -error: label-scope.asm(8): +error: label-scope.asm(3): + '..' not defined +error: label-scope.asm(12): Built-in symbol '@' cannot be purged -error: label-scope.asm(8): +error: label-scope.asm(12): Built-in symbol '.' cannot be purged -error: Assembly aborted (4 errors)! +error: label-scope.asm(12): + Built-in symbol '..' cannot be purged +error: Assembly aborted (6 errors)! diff --git a/test/asm/label-scope.out b/test/asm/label-scope.out index 2e0e2071..26cfcd67 100644 --- a/test/asm/label-scope.out +++ b/test/asm/label-scope.out @@ -1 +1,3 @@ -PC: $002A; label scope: "Foobar"; Foobar: $002A +PC: $002D +global scope: "Foo" ($002B) +local scope: "Foo.bar" ($002C) diff --git a/test/asm/period.asm b/test/asm/period.asm index 2350acd9..040e2c27 100644 --- a/test/asm/period.asm +++ b/test/asm/period.asm @@ -1,6 +1,33 @@ -section "period", rom0 +SECTION "period", ROM0 -global1: db 1 -.local db 2 -. db 3 ; this... -global1 db 4 ; ...expands to this +assert !def(.) && !def(..) + +global1: +assert !strcmp("{.}", "global1") && !def(..) + +.local1: +assert !strcmp("{.}", "global1") && !strcmp("{..}", "global1.local1") + +global1.local2: +assert !strcmp("{.}", "global1") && !strcmp("{..}", "global1.local2") + +global2: +assert !strcmp("{.}", "global2") && !def(..) + +.local1: +assert !strcmp("{.}", "global2") && !strcmp("{..}", "global2.local1") + +LOAD "load", WRAM0 +assert !def(.) && !def(..) + +wGlobal1: +assert !strcmp("{.}", "wGlobal1") && !def(..) + +.wLocal1: +assert !strcmp("{.}", "wGlobal1") && !strcmp("{..}", "wGlobal1.wLocal1") + +wGlobal2: +assert !strcmp("{.}", "wGlobal2") && !def(..) + +ENDL +assert !strcmp("{.}", "global2") && !strcmp("{..}", "global2.local1") diff --git a/test/asm/period.err b/test/asm/period.err deleted file mode 100644 index caee518c..00000000 --- a/test/asm/period.err +++ /dev/null @@ -1,5 +0,0 @@ -error: period.asm(5): - 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 d31ee57a..fc40cacb 100644 --- a/test/asm/undefined-builtins.asm +++ b/test/asm/undefined-builtins.asm @@ -3,11 +3,16 @@ assert !DEF(@) println @ println "{@}?" -; not inside a label scope +; not inside a global scope assert !DEF(.) println . println "{.}?" +; not inside a local scope +assert !DEF(..) +println .. +println "{..}?" + ; not inside a macro assert !DEF(_NARG) println _NARG @@ -18,11 +23,16 @@ assert DEF(@) println @ println "{@}!" -LabelScope: +GlobalScope: assert DEF(.) println . println "{.}!" +.localScope: +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 9eb1569e..c724ad86 100644 --- a/test/asm/undefined-builtins.err +++ b/test/asm/undefined-builtins.err @@ -7,7 +7,11 @@ error: undefined-builtins.asm(8): error: undefined-builtins.asm(9): Interpolated symbol "." does not exist error: undefined-builtins.asm(13): - _NARG has no value outside of a macro + ".." has no value outside of a local label scope error: undefined-builtins.asm(14): + Interpolated symbol ".." does not exist +error: undefined-builtins.asm(18): + _NARG has no value outside of a macro +error: undefined-builtins.asm(19): Interpolated symbol "_NARG" does not exist -error: Assembly aborted (6 errors)! +error: Assembly aborted (8 errors)! diff --git a/test/asm/undefined-builtins.out b/test/asm/undefined-builtins.out index 38a56b83..417e1179 100644 --- a/test/asm/undefined-builtins.out +++ b/test/asm/undefined-builtins.out @@ -1,12 +1,16 @@ $0 ? +? + ? $0 ? $42 $42! $42 -LabelScope! +GlobalScope! +$42 +GlobalScope.localScope! $3 $3!