Skip to content

Commit

Permalink
Track local label scope, string equated as .. (gbdev#1504)
Browse files Browse the repository at this point in the history
  • Loading branch information
Rangi42 authored Sep 18, 2024
1 parent 197f6cb commit 9ef2e43
Show file tree
Hide file tree
Showing 13 changed files with 167 additions and 67 deletions.
8 changes: 5 additions & 3 deletions include/asm/symbol.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <string>
#include <string_view>
#include <time.h>
#include <utility>
#include <variant>

#include "asm/lexer.hpp"
Expand Down Expand Up @@ -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<Symbol const *, Symbol const *> sym_GetCurrentLabelScopes();
void sym_SetCurrentLabelScopes(std::pair<Symbol const *, Symbol const *> newScopes);
void sym_ResetCurrentLabelScopes();

#endif // RGBDS_ASM_SYMBOL_HPP
1 change: 1 addition & 0 deletions man/rgbasm.5
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/asm/lexer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
19 changes: 10 additions & 9 deletions src/asm/section.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <utility>

#include "helpers.hpp"

Expand All @@ -31,7 +32,7 @@ struct UnionStackEntry {
struct SectionStackEntry {
Section *section;
Section *loadSection;
Symbol const *scope;
std::pair<Symbol const *, Symbol const *> labelScopes;
uint32_t offset;
int32_t loadOffset;
std::stack<UnionStackEntry> unionStack;
Expand All @@ -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)
Section *currentSection = 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)

// A quick check to see if we have an initialized section
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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;
Expand All @@ -490,7 +491,7 @@ void sect_EndLoadSection() {
curOffset += loadOffset;
loadOffset = 0;
currentLoadSection = nullptr;
sym_SetCurrentLabelScope(currentLoadScope);
sym_SetCurrentLabelScopes(currentLoadLabelScopes);
}

Section *sect_GetSymbolSection() {
Expand Down Expand Up @@ -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 = {},
Expand All @@ -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);
}

Expand All @@ -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);
Expand All @@ -979,5 +980,5 @@ void sect_EndSection() {

// Reset the section scope
currentSection = nullptr;
sym_SetCurrentLabelScope(nullptr);
sym_ResetCurrentLabelScopes();
}
102 changes: 73 additions & 29 deletions src/asm/symbol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@ using namespace std::literals;
std::unordered_map<std::string, Symbol> symbols;
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 *NARGSymbol;
static Symbol *labelScopeSymbol;
static Symbol *globalScopeSymbol;
static Symbol *localScopeSymbol;
static Symbol *RSSymbol;
static char savedTIME[256];
static char savedDATE[256];
Expand All @@ -51,12 +53,20 @@ static int32_t NARGCallback() {
}
}

static std::shared_ptr<std::string> labelScopeCallback() {
if (!labelScope) {
static std::shared_ptr<std::string> globalScopeCallback() {
if (!globalScope) {
error("\".\" has no value outside of a label scope\n");
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() {
Expand Down Expand Up @@ -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);
}
Expand All @@ -166,16 +176,18 @@ 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('.');

// If there are no dots, it's not a local label
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;

Expand All @@ -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;
Expand All @@ -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) {
Expand All @@ -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;
}

Expand Down Expand Up @@ -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);
}
Expand All @@ -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() {
Expand Down Expand Up @@ -302,12 +320,23 @@ uint32_t sym_GetConstantValue(std::string const &symName) {
return 0;
}

Symbol const *sym_GetCurrentLabelScope() {
return labelScope;
std::pair<Symbol const *, Symbol const *> sym_GetCurrentLabelScopes() {
return {globalScope, localScope};
}

void sym_SetCurrentLabelScopes(std::pair<Symbol const *, Symbol const *> 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_SetCurrentLabelScope(Symbol const *newScope) {
labelScope = newScope;
void sym_ResetCurrentLabelScopes() {
globalScope = nullptr;
localScope = nullptr;
}

static Symbol *createNonrelocSymbol(std::string const &symName, bool numeric) {
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
}

Expand All @@ -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;
Expand Down
18 changes: 12 additions & 6 deletions test/asm/label-scope.asm
Original file line number Diff line number Diff line change
@@ -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:{..}})"
10 changes: 7 additions & 3 deletions test/asm/label-scope.err
Original file line number Diff line number Diff line change
Expand Up @@ -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)!
4 changes: 3 additions & 1 deletion test/asm/label-scope.out
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
PC: $002A; label scope: "Foobar"; Foobar: $002A
PC: $002D
global scope: "Foo" ($002B)
local scope: "Foo.bar" ($002C)
Loading

0 comments on commit 9ef2e43

Please sign in to comment.