Skip to content

Commit

Permalink
Implement . string constant for the current label scope (gbdev#1499)
Browse files Browse the repository at this point in the history
  • Loading branch information
Rangi42 authored Sep 13, 2024
1 parent bfb96b0 commit 122ef95
Show file tree
Hide file tree
Showing 15 changed files with 133 additions and 23 deletions.
9 changes: 5 additions & 4 deletions include/asm/symbol.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::string> // For SYM_EQUS
int32_t, // If isNumeric()
int32_t (*)(), // If isNumeric() via a callback
ContentSpan, // For SYM_MACRO
std::shared_ptr<std::string>, // For SYM_EQUS
std::shared_ptr<std::string> (*)() // For SYM_EQUS via a callback
>
data;

Expand Down
1 change: 1 addition & 0 deletions man/rgbasm.5
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 4 additions & 2 deletions src/asm/lexer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -327,8 +327,6 @@ static std::unordered_map<std::string, int, CaseInsensitive, CaseInsensitive> ke
{"POPO", T_(POP_POPO) },

{"OPT", T_(POP_OPT) },

{".", T_(PERIOD) },
};

static bool isWhitespace(int c) {
Expand Down Expand Up @@ -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);
}

Expand Down
1 change: 0 additions & 1 deletion src/asm/parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,6 @@
%token EOB "end of buffer"

// General punctuation
%token PERIOD "."
%token COMMA ","
%token COLON ":" DOUBLE_COLON "::"
%token LBRACK "[" RBRACK "]"
Expand Down
48 changes: 39 additions & 9 deletions src/asm/symbol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ std::unordered_set<std::string> 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];
Expand All @@ -50,6 +51,14 @@ static int32_t NARGCallback() {
}
}

static std::shared_ptr<std::string> labelScopeCallback() {
if (!labelScope) {
error("\".\" has no value outside of a label scope\n");
return std::make_shared<std::string>("");
}
return std::make_shared<std::string>(labelScope->name);
}

static int32_t PCCallback() {
return sect_GetSymbolSection()->org + sect_GetSymbolOffset();
}
Expand Down Expand Up @@ -78,7 +87,12 @@ ContentSpan const &Symbol::getMacro() const {
}

std::shared_ptr<std::string> Symbol::getEqus() const {
assume(std::holds_alternative<std::shared_ptr<std::string>>(data));
assume(
std::holds_alternative<std::shared_ptr<std::string>>(data)
|| std::holds_alternative<std::shared_ptr<std::string> (*)()>(data)
);
if (auto *callback = std::get_if<std::shared_ptr<std::string> (*)()>(&data); callback)
return (*callback)();
return std::get<std::shared_ptr<std::string>>(data);
}

Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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());
Expand All @@ -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;
Expand All @@ -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;
}

Expand Down Expand Up @@ -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();
}
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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;

Expand Down
20 changes: 19 additions & 1 deletion test/asm/builtin-reserved.asm
Original file line number Diff line number Diff line change
Expand Up @@ -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
18 changes: 17 additions & 1 deletion test/asm/builtin-reserved.err
Original file line number Diff line number Diff line change
Expand Up @@ -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)!
12 changes: 12 additions & 0 deletions test/asm/label-scope.asm
Original file line number Diff line number Diff line change
@@ -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:{.}}"
9 changes: 9 additions & 0 deletions test/asm/label-scope.err
Original file line number Diff line number Diff line change
@@ -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)!
1 change: 1 addition & 0 deletions test/asm/label-scope.out
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
PC: $002A; label scope: "Foobar"; Foobar: $002A
3 changes: 2 additions & 1 deletion test/asm/period.asm
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ section "period", rom0

global1: db 1
.local db 2
. db 3
. db 3 ; this...
global1 db 4 ; ...expands to this
6 changes: 4 additions & 2 deletions test/asm/period.err
Original file line number Diff line number Diff line change
@@ -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)!
10 changes: 10 additions & 0 deletions test/asm/undefined-builtins.asm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -13,6 +18,11 @@ assert DEF(@)
println @
println "{@}!"

LabelScope:
assert DEF(.)
println .
println "{.}!"

MACRO m
assert DEF(_NARG)
println _NARG
Expand Down
8 changes: 6 additions & 2 deletions test/asm/undefined-builtins.err
Original file line number Diff line number Diff line change
Expand Up @@ -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)!
4 changes: 4 additions & 0 deletions test/asm/undefined-builtins.out
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
$0
?

?
$0
?
$42
$42!
$42
LabelScope!
$3
$3!

0 comments on commit 122ef95

Please sign in to comment.