mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 10:12:06 +00:00
Implement . string constant for the current label scope (#1499)
This commit is contained in:
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -107,7 +107,6 @@
|
||||
%token EOB "end of buffer"
|
||||
|
||||
// General punctuation
|
||||
%token PERIOD "."
|
||||
%token COMMA ","
|
||||
%token COLON ":" DOUBLE_COLON "::"
|
||||
%token LBRACK "[" RBRACK "]"
|
||||
|
||||
@@ -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];
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
test/asm/label-scope.asm
Normal file
12
test/asm/label-scope.asm
Normal file
@@ -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
test/asm/label-scope.err
Normal file
9
test/asm/label-scope.err
Normal file
@@ -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
test/asm/label-scope.out
Normal file
1
test/asm/label-scope.out
Normal file
@@ -0,0 +1 @@
|
||||
PC: $002A; label scope: "Foobar"; Foobar: $002A
|
||||
@@ -2,4 +2,5 @@ section "period", rom0
|
||||
|
||||
global1: db 1
|
||||
.local db 2
|
||||
. db 3
|
||||
. db 3 ; this...
|
||||
global1 db 4 ; ...expands to this
|
||||
|
||||
@@ -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)!
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)!
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
$0
|
||||
?
|
||||
|
||||
?
|
||||
$0
|
||||
?
|
||||
$42
|
||||
$42!
|
||||
$42
|
||||
LabelScope!
|
||||
$3
|
||||
$3!
|
||||
|
||||
Reference in New Issue
Block a user