Track local label scope, string equated as .. (#1504)

This commit is contained in:
Sylvie
2024-09-18 09:52:30 -04:00
committed by GitHub
parent 197f6cb0ba
commit 9ef2e43bf7
13 changed files with 167 additions and 67 deletions

View File

@@ -9,6 +9,7 @@
#include <string> #include <string>
#include <string_view> #include <string_view>
#include <time.h> #include <time.h>
#include <utility>
#include <variant> #include <variant>
#include "asm/lexer.hpp" #include "asm/lexer.hpp"
@@ -98,8 +99,9 @@ bool sym_IsPurgedExact(std::string const &symName);
bool sym_IsPurgedScoped(std::string const &symName); bool sym_IsPurgedScoped(std::string const &symName);
void sym_Init(time_t now); void sym_Init(time_t now);
// Functions to save and restore the current label scope. // Functions to save and restore the current label scopes.
Symbol const *sym_GetCurrentLabelScope(); std::pair<Symbol const *, Symbol const *> sym_GetCurrentLabelScopes();
void sym_SetCurrentLabelScope(Symbol const *newScope); void sym_SetCurrentLabelScopes(std::pair<Symbol const *, Symbol const *> newScopes);
void sym_ResetCurrentLabelScopes();
#endif // RGBDS_ASM_SYMBOL_HPP #endif // RGBDS_ASM_SYMBOL_HPP

View File

@@ -1527,6 +1527,7 @@ The following symbols are defined by the assembler:
.It Sy Name Ta Sy Type Ta Sy Contents .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 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 global label scope
.It Dv .. Ta Ic EQUS Ta The current local label scope
.It Dv _RS Ta Ic = Ta _RS Counter .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 _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 .It Dv __DATE__ Ta Ic EQUS Ta Today's date

View File

@@ -1174,7 +1174,7 @@ static Token readIdentifier(char firstChar, bool raw) {
return Token(search->second); 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) if (identifier.find_first_not_of('.') == identifier.npos)
tokenType = T_(ID); tokenType = T_(ID);

View File

@@ -10,6 +10,7 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <utility>
#include "helpers.hpp" #include "helpers.hpp"
@@ -31,7 +32,7 @@ struct UnionStackEntry {
struct SectionStackEntry { struct SectionStackEntry {
Section *section; Section *section;
Section *loadSection; Section *loadSection;
Symbol const *scope; std::pair<Symbol const *, Symbol const *> labelScopes;
uint32_t offset; uint32_t offset;
int32_t loadOffset; int32_t loadOffset;
std::stack<UnionStackEntry> unionStack; std::stack<UnionStackEntry> unionStack;
@@ -44,7 +45,7 @@ std::unordered_map<std::string, size_t> sectionMap; // Indexes into `sectionList
uint32_t curOffset; // Offset into the current section (see sect_GetSymbolOffset) uint32_t curOffset; // Offset into the current section (see sect_GetSymbolOffset)
Section *currentSection = nullptr; Section *currentSection = nullptr;
static Section *currentLoadSection = nullptr; static Section *currentLoadSection = nullptr;
static Symbol const *currentLoadScope = nullptr; static std::pair<Symbol const *, Symbol const *> currentLoadLabelScopes = {nullptr, nullptr};
int32_t loadOffset; // Offset into the LOAD section's parent (see sect_GetOutputOffset) int32_t loadOffset; // Offset into the LOAD section's parent (see sect_GetOutputOffset)
// A quick check to see if we have an initialized section // A quick check to see if we have an initialized section
@@ -395,7 +396,7 @@ static void changeSection() {
if (!currentUnionStack.empty()) if (!currentUnionStack.empty())
fatalerror("Cannot change the section within a UNION\n"); fatalerror("Cannot change the section within a UNION\n");
sym_SetCurrentLabelScope(nullptr); sym_ResetCurrentLabelScopes();
} }
bool Section::isSizeKnown() const { bool Section::isSizeKnown() const {
@@ -473,7 +474,7 @@ void sect_SetLoadSection(
Section *sect = getSection(name, type, org, attrs, mod); Section *sect = getSection(name, type, org, attrs, mod);
currentLoadScope = sym_GetCurrentLabelScope(); currentLoadLabelScopes = sym_GetCurrentLabelScopes();
changeSection(); changeSection();
loadOffset = curOffset - (mod == SECTION_UNION ? 0 : sect->size); loadOffset = curOffset - (mod == SECTION_UNION ? 0 : sect->size);
curOffset -= loadOffset; curOffset -= loadOffset;
@@ -490,7 +491,7 @@ void sect_EndLoadSection() {
curOffset += loadOffset; curOffset += loadOffset;
loadOffset = 0; loadOffset = 0;
currentLoadSection = nullptr; currentLoadSection = nullptr;
sym_SetCurrentLabelScope(currentLoadScope); sym_SetCurrentLabelScopes(currentLoadLabelScopes);
} }
Section *sect_GetSymbolSection() { Section *sect_GetSymbolSection() {
@@ -935,7 +936,7 @@ void sect_PushSection() {
sectionStack.push_front({ sectionStack.push_front({
.section = currentSection, .section = currentSection,
.loadSection = currentLoadSection, .loadSection = currentLoadSection,
.scope = sym_GetCurrentLabelScope(), .labelScopes = sym_GetCurrentLabelScopes(),
.offset = curOffset, .offset = curOffset,
.loadOffset = loadOffset, .loadOffset = loadOffset,
.unionStack = {}, .unionStack = {},
@@ -944,7 +945,7 @@ void sect_PushSection() {
// Reset the section scope // Reset the section scope
currentSection = nullptr; currentSection = nullptr;
currentLoadSection = nullptr; currentLoadSection = nullptr;
sym_SetCurrentLabelScope(nullptr); sym_ResetCurrentLabelScopes();
std::swap(currentUnionStack, sectionStack.front().unionStack); std::swap(currentUnionStack, sectionStack.front().unionStack);
} }
@@ -961,7 +962,7 @@ void sect_PopSection() {
changeSection(); changeSection();
currentSection = entry.section; currentSection = entry.section;
currentLoadSection = entry.loadSection; currentLoadSection = entry.loadSection;
sym_SetCurrentLabelScope(entry.scope); sym_SetCurrentLabelScopes(entry.labelScopes);
curOffset = entry.offset; curOffset = entry.offset;
loadOffset = entry.loadOffset; loadOffset = entry.loadOffset;
std::swap(currentUnionStack, entry.unionStack); std::swap(currentUnionStack, entry.unionStack);
@@ -979,5 +980,5 @@ void sect_EndSection() {
// Reset the section scope // Reset the section scope
currentSection = nullptr; currentSection = nullptr;
sym_SetCurrentLabelScope(nullptr); sym_ResetCurrentLabelScopes();
} }

View File

@@ -22,10 +22,12 @@ using namespace std::literals;
std::unordered_map<std::string, Symbol> symbols; std::unordered_map<std::string, Symbol> symbols;
std::unordered_set<std::string> purgedSymbols; std::unordered_set<std::string> 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 *PCSymbol;
static Symbol *NARGSymbol; static Symbol *NARGSymbol;
static Symbol *labelScopeSymbol; static Symbol *globalScopeSymbol;
static Symbol *localScopeSymbol;
static Symbol *RSSymbol; static Symbol *RSSymbol;
static char savedTIME[256]; static char savedTIME[256];
static char savedDATE[256]; static char savedDATE[256];
@@ -51,12 +53,20 @@ static int32_t NARGCallback() {
} }
} }
static std::shared_ptr<std::string> labelScopeCallback() { static std::shared_ptr<std::string> globalScopeCallback() {
if (!labelScope) { if (!globalScope) {
error("\".\" has no value outside of a label scope\n"); error("\".\" has no value outside of a label scope\n");
return std::make_shared<std::string>(""); return std::make_shared<std::string>("");
} }
return std::make_shared<std::string>(labelScope->name); return std::make_shared<std::string>(globalScope->name);
}
static std::shared_ptr<std::string> localScopeCallback() {
if (!localScope) {
error("\"..\" has no value outside of a local label scope\n");
return std::make_shared<std::string>("");
}
return std::make_shared<std::string>(localScope->name);
} }
static int32_t PCCallback() { static int32_t PCCallback() {
@@ -141,7 +151,7 @@ static void redefinedError(Symbol const &sym) {
} }
static void assumeAlreadyExpanded(std::string const &symName) { 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` // but cannot be unqualified `.local`
assume(!symName.starts_with('.') || symName.find_first_not_of('.') == symName.npos); 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) { static bool isAutoScoped(std::string const &symName) {
// `labelScope` should be global if it's defined // `globalScope` should be global if it's defined
assume(!labelScope || labelScope->name.find('.') == std::string::npos); 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('.'); size_t dotPos = symName.find('.');
@@ -175,7 +187,7 @@ static bool isAutoScoped(std::string const &symName) {
if (dotPos == std::string::npos) if (dotPos == std::string::npos)
return false; 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) if (dotPos == 0 && symName.find_first_not_of('.') == symName.npos)
return false; return false;
@@ -192,7 +204,7 @@ static bool isAutoScoped(std::string const &symName) {
return false; return false;
// Check for unqualifiable local label // Check for unqualifiable local label
if (!labelScope) if (!globalScope)
fatalerror("Unqualified local label '%s' in main scope\n", symName.c_str()); fatalerror("Unqualified local label '%s' in main scope\n", symName.c_str());
return true; return true;
@@ -206,7 +218,7 @@ Symbol *sym_FindExactSymbol(std::string const &symName) {
} }
Symbol *sym_FindScopedSymbol(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) { Symbol *sym_FindScopedValidSymbol(std::string const &symName) {
@@ -220,8 +232,12 @@ Symbol *sym_FindScopedValidSymbol(std::string const &symName) {
if (sym == NARGSymbol && !fstk_GetCurrentMacroArgs()) { if (sym == NARGSymbol && !fstk_GetCurrentMacroArgs()) {
return nullptr; return nullptr;
} }
// `.` has no value outside of a label scope // `.` has no value outside of a global label scope
if (sym == labelScopeSymbol && !labelScope) { if (sym == globalScopeSymbol && !globalScope) {
return nullptr;
}
// `..` has no value outside of a local label scope
if (sym == localScopeSymbol && !localScope) {
return nullptr; return nullptr;
} }
@@ -250,8 +266,10 @@ void sym_Purge(std::string const &symName) {
else if (sym->isLabel()) else if (sym->isLabel())
warning(WARNING_PURGE_2, "Purging a label \"%s\"\n", symName.c_str()); warning(WARNING_PURGE_2, "Purging a label \"%s\"\n", symName.c_str());
// Do not keep a reference to the label after purging it // Do not keep a reference to the label after purging it
if (labelScope == sym) if (sym == globalScope)
labelScope = nullptr; globalScope = nullptr;
if (sym == localScope)
localScope = nullptr;
purgedSymbols.emplace(sym->name); purgedSymbols.emplace(sym->name);
symbols.erase(sym->name); symbols.erase(sym->name);
} }
@@ -264,7 +282,7 @@ bool sym_IsPurgedExact(std::string const &symName) {
} }
bool sym_IsPurgedScoped(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() { int32_t sym_GetRSValue() {
@@ -302,12 +320,23 @@ uint32_t sym_GetConstantValue(std::string const &symName) {
return 0; return 0;
} }
Symbol const *sym_GetCurrentLabelScope() { std::pair<Symbol const *, Symbol const *> sym_GetCurrentLabelScopes() {
return labelScope; return {globalScope, localScope};
} }
void sym_SetCurrentLabelScope(Symbol const *newScope) { void sym_SetCurrentLabelScopes(std::pair<Symbol const *, Symbol const *> newScopes) {
labelScope = newScope; 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) { 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 // The symbol name should be local, qualified or not
assume(symName.find('.') != std::string::npos); 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) { Symbol *sym_AddLabel(std::string const &symName) {
// The symbol name should be global
assume(symName.find('.') == std::string::npos);
Symbol *sym = addLabel(symName); Symbol *sym = addLabel(symName);
// Set the symbol as the new scope if (sym) {
if (sym) globalScope = sym;
labelScope = sym; // A new global scope resets the local scope
localScope = nullptr;
}
return sym; return sym;
} }
@@ -542,7 +581,7 @@ Symbol *sym_Ref(std::string const &symName) {
Symbol *sym = sym_FindScopedSymbol(symName); Symbol *sym = sym_FindScopedSymbol(symName);
if (!sym) { if (!sym) {
sym = &createSymbol(isAutoScoped(symName) ? labelScope->name + symName : symName); sym = &createSymbol(isAutoScoped(symName) ? globalScope->name + symName : symName);
sym->type = SYM_REF; sym->type = SYM_REF;
} }
@@ -566,10 +605,15 @@ void sym_Init(time_t now) {
NARGSymbol->data = NARGCallback; NARGSymbol->data = NARGCallback;
NARGSymbol->isBuiltin = true; NARGSymbol->isBuiltin = true;
labelScopeSymbol = &createSymbol("."s); globalScopeSymbol = &createSymbol("."s);
labelScopeSymbol->type = SYM_EQUS; globalScopeSymbol->type = SYM_EQUS;
labelScopeSymbol->data = labelScopeCallback; globalScopeSymbol->data = globalScopeCallback;
labelScopeSymbol->isBuiltin = true; globalScopeSymbol->isBuiltin = true;
localScopeSymbol = &createSymbol(".."s);
localScopeSymbol->type = SYM_EQUS;
localScopeSymbol->data = localScopeCallback;
localScopeSymbol->isBuiltin = true;
RSSymbol = sym_AddVar("_RS"s, 0); RSSymbol = sym_AddVar("_RS"s, 0);
RSSymbol->isBuiltin = true; RSSymbol->isBuiltin = true;

View File

@@ -1,12 +1,18 @@
ASSERT !DEF(@) && !DEF(.) ASSERT !DEF(@) && !DEF(.) && !DEF(..)
PURGE @, . PURGE @, ., ..
SECTION "test", ROM0[42] 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:{..}})"

View File

@@ -2,8 +2,12 @@ error: label-scope.asm(3):
'@' not defined '@' not defined
error: label-scope.asm(3): error: label-scope.asm(3):
'.' not defined '.' 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 Built-in symbol '@' cannot be purged
error: label-scope.asm(8): error: label-scope.asm(12):
Built-in symbol '.' cannot be purged 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)!

View File

@@ -1 +1,3 @@
PC: $002A; label scope: "Foobar"; Foobar: $002A PC: $002D
global scope: "Foo" ($002B)
local scope: "Foo.bar" ($002C)

View File

@@ -1,6 +1,33 @@
section "period", rom0 SECTION "period", ROM0
global1: db 1 assert !def(.) && !def(..)
.local db 2
. db 3 ; this... global1:
global1 db 4 ; ...expands to this 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")

View File

@@ -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)!

View File

@@ -3,11 +3,16 @@ assert !DEF(@)
println @ println @
println "{@}?" println "{@}?"
; not inside a label scope ; not inside a global scope
assert !DEF(.) assert !DEF(.)
println . println .
println "{.}?" println "{.}?"
; not inside a local scope
assert !DEF(..)
println ..
println "{..}?"
; not inside a macro ; not inside a macro
assert !DEF(_NARG) assert !DEF(_NARG)
println _NARG println _NARG
@@ -18,11 +23,16 @@ assert DEF(@)
println @ println @
println "{@}!" println "{@}!"
LabelScope: GlobalScope:
assert DEF(.) assert DEF(.)
println . println .
println "{.}!" println "{.}!"
.localScope:
assert DEF(..)
println ..
println "{..}!"
MACRO m MACRO m
assert DEF(_NARG) assert DEF(_NARG)
println _NARG println _NARG

View File

@@ -7,7 +7,11 @@ error: undefined-builtins.asm(8):
error: undefined-builtins.asm(9): error: undefined-builtins.asm(9):
Interpolated symbol "." does not exist Interpolated symbol "." does not exist
error: undefined-builtins.asm(13): 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): 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 Interpolated symbol "_NARG" does not exist
error: Assembly aborted (6 errors)! error: Assembly aborted (8 errors)!

View File

@@ -1,12 +1,16 @@
$0 $0
? ?
?
? ?
$0 $0
? ?
$42 $42
$42! $42!
$42 $42
LabelScope! GlobalScope!
$42
GlobalScope.localScope!
$3 $3
$3! $3!