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:
@@ -37,9 +37,10 @@ struct Symbol {
|
|||||||
|
|
||||||
std::variant<
|
std::variant<
|
||||||
int32_t, // If isNumeric()
|
int32_t, // If isNumeric()
|
||||||
int32_t (*)(), // If isNumeric() and has a callback
|
int32_t (*)(), // If isNumeric() via a callback
|
||||||
ContentSpan, // For SYM_MACRO
|
ContentSpan, // For SYM_MACRO
|
||||||
std::shared_ptr<std::string> // For SYM_EQUS
|
std::shared_ptr<std::string>, // For SYM_EQUS
|
||||||
|
std::shared_ptr<std::string> (*)() // For SYM_EQUS via a callback
|
||||||
>
|
>
|
||||||
data;
|
data;
|
||||||
|
|
||||||
|
|||||||
@@ -1526,6 +1526,7 @@ The following symbols are defined by the assembler:
|
|||||||
.Bl -column -offset indent "__ISO_8601_LOCAL__" "EQUS"
|
.Bl -column -offset indent "__ISO_8601_LOCAL__" "EQUS"
|
||||||
.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 _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
|
||||||
|
|||||||
@@ -327,8 +327,6 @@ static std::unordered_map<std::string, int, CaseInsensitive, CaseInsensitive> ke
|
|||||||
{"POPO", T_(POP_POPO) },
|
{"POPO", T_(POP_POPO) },
|
||||||
|
|
||||||
{"OPT", T_(POP_OPT) },
|
{"OPT", T_(POP_OPT) },
|
||||||
|
|
||||||
{".", T_(PERIOD) },
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool isWhitespace(int c) {
|
static bool isWhitespace(int c) {
|
||||||
@@ -1173,6 +1171,10 @@ 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
|
||||||
|
if (identifier.find_first_not_of('.') == identifier.npos)
|
||||||
|
tokenType = T_(ID);
|
||||||
|
|
||||||
return Token(tokenType, identifier);
|
return Token(tokenType, identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -107,7 +107,6 @@
|
|||||||
%token EOB "end of buffer"
|
%token EOB "end of buffer"
|
||||||
|
|
||||||
// General punctuation
|
// General punctuation
|
||||||
%token PERIOD "."
|
|
||||||
%token COMMA ","
|
%token COMMA ","
|
||||||
%token COLON ":" DOUBLE_COLON "::"
|
%token COLON ":" DOUBLE_COLON "::"
|
||||||
%token LBRACK "[" RBRACK "]"
|
%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 const *labelScope = nullptr; // Current section's label scope
|
||||||
static Symbol *PCSymbol;
|
static Symbol *PCSymbol;
|
||||||
static Symbol *NARGSymbol;
|
static Symbol *NARGSymbol;
|
||||||
|
static Symbol *labelScopeSymbol;
|
||||||
static Symbol *RSSymbol;
|
static Symbol *RSSymbol;
|
||||||
static char savedTIME[256];
|
static char savedTIME[256];
|
||||||
static char savedDATE[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() {
|
static int32_t PCCallback() {
|
||||||
return sect_GetSymbolSection()->org + sect_GetSymbolOffset();
|
return sect_GetSymbolSection()->org + sect_GetSymbolOffset();
|
||||||
}
|
}
|
||||||
@@ -78,7 +87,12 @@ ContentSpan const &Symbol::getMacro() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<std::string> Symbol::getEqus() 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);
|
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) {
|
static Symbol &createSymbol(std::string const &symName) {
|
||||||
// The symbol name should have been expanded already
|
assumeAlreadyExpanded(symName);
|
||||||
assume(!symName.starts_with('.'));
|
|
||||||
|
|
||||||
static uint32_t nextDefIndex = 0;
|
static uint32_t nextDefIndex = 0;
|
||||||
|
|
||||||
@@ -156,6 +175,10 @@ 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
|
||||||
|
if (dotPos == 0 && symName.find_first_not_of('.') == symName.npos)
|
||||||
|
return false;
|
||||||
|
|
||||||
// Check for nothing after the dot
|
// Check for nothing after the dot
|
||||||
if (dotPos == symName.length() - 1)
|
if (dotPos == symName.length() - 1)
|
||||||
fatalerror("'%s' is a nonsensical reference to an empty local label\n", symName.c_str());
|
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) {
|
Symbol *sym_FindExactSymbol(std::string const &symName) {
|
||||||
// The symbol name should have been expanded already
|
assumeAlreadyExpanded(symName);
|
||||||
assume(!symName.starts_with('.'));
|
|
||||||
|
|
||||||
auto search = symbols.find(symName);
|
auto search = symbols.find(symName);
|
||||||
return search != symbols.end() ? &search->second : nullptr;
|
return search != symbols.end() ? &search->second : nullptr;
|
||||||
@@ -198,6 +220,11 @@ 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
|
||||||
|
if (sym == labelScopeSymbol && !labelScope) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
return sym;
|
return sym;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,8 +258,7 @@ void sym_Purge(std::string const &symName) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool sym_IsPurgedExact(std::string const &symName) {
|
bool sym_IsPurgedExact(std::string const &symName) {
|
||||||
// The symbol name should have been expanded already
|
assumeAlreadyExpanded(symName);
|
||||||
assume(!symName.starts_with('.'));
|
|
||||||
|
|
||||||
return purgedSymbols.find(symName) != purgedSymbols.end();
|
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) {
|
static Symbol *addLabel(std::string const &symName) {
|
||||||
// The symbol name should have been expanded already
|
assumeAlreadyExpanded(symName);
|
||||||
assume(!symName.starts_with('.'));
|
|
||||||
|
|
||||||
Symbol *sym = sym_FindExactSymbol(symName);
|
Symbol *sym = sym_FindExactSymbol(symName);
|
||||||
|
|
||||||
@@ -541,6 +566,11 @@ void sym_Init(time_t now) {
|
|||||||
NARGSymbol->data = NARGCallback;
|
NARGSymbol->data = NARGCallback;
|
||||||
NARGSymbol->isBuiltin = true;
|
NARGSymbol->isBuiltin = true;
|
||||||
|
|
||||||
|
labelScopeSymbol = &createSymbol("."s);
|
||||||
|
labelScopeSymbol->type = SYM_EQUS;
|
||||||
|
labelScopeSymbol->data = labelScopeCallback;
|
||||||
|
labelScopeSymbol->isBuiltin = true;
|
||||||
|
|
||||||
RSSymbol = sym_AddVar("_RS"s, 0);
|
RSSymbol = sym_AddVar("_RS"s, 0);
|
||||||
RSSymbol->isBuiltin = true;
|
RSSymbol->isBuiltin = true;
|
||||||
|
|
||||||
|
|||||||
@@ -11,5 +11,23 @@ REDEF _NARG = 78
|
|||||||
DEF _NARG EQUS "hello"
|
DEF _NARG EQUS "hello"
|
||||||
REDEF _NARG EQUS "world"
|
REDEF _NARG EQUS "world"
|
||||||
|
|
||||||
SECTION "test", ROM0
|
SECTION "_NARG", ROM0
|
||||||
_NARG:
|
_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
|
'_NARG' is reserved for a built-in symbol
|
||||||
error: builtin-reserved.asm(15):
|
error: builtin-reserved.asm(15):
|
||||||
'_NARG' is reserved for a built-in symbol
|
'_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
|
global1: db 1
|
||||||
.local db 2
|
.local db 2
|
||||||
. db 3
|
. db 3 ; this...
|
||||||
|
global1 db 4 ; ...expands to this
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
error: period.asm(5):
|
error: period.asm(5):
|
||||||
syntax error, unexpected .
|
syntax error, unexpected DB, expecting : or ::
|
||||||
error: Assembly aborted (1 error)!
|
error: period.asm(6):
|
||||||
|
syntax error, unexpected DB, expecting : or ::
|
||||||
|
error: Assembly aborted (2 errors)!
|
||||||
|
|||||||
@@ -3,6 +3,11 @@ assert !DEF(@)
|
|||||||
println @
|
println @
|
||||||
println "{@}?"
|
println "{@}?"
|
||||||
|
|
||||||
|
; not inside a label scope
|
||||||
|
assert !DEF(.)
|
||||||
|
println .
|
||||||
|
println "{.}?"
|
||||||
|
|
||||||
; not inside a macro
|
; not inside a macro
|
||||||
assert !DEF(_NARG)
|
assert !DEF(_NARG)
|
||||||
println _NARG
|
println _NARG
|
||||||
@@ -13,6 +18,11 @@ assert DEF(@)
|
|||||||
println @
|
println @
|
||||||
println "{@}!"
|
println "{@}!"
|
||||||
|
|
||||||
|
LabelScope:
|
||||||
|
assert DEF(.)
|
||||||
|
println .
|
||||||
|
println "{.}!"
|
||||||
|
|
||||||
MACRO m
|
MACRO m
|
||||||
assert DEF(_NARG)
|
assert DEF(_NARG)
|
||||||
println _NARG
|
println _NARG
|
||||||
|
|||||||
@@ -3,7 +3,11 @@ error: undefined-builtins.asm(3):
|
|||||||
error: undefined-builtins.asm(4):
|
error: undefined-builtins.asm(4):
|
||||||
Interpolated symbol "@" does not exist
|
Interpolated symbol "@" does not exist
|
||||||
error: undefined-builtins.asm(8):
|
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):
|
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
|
Interpolated symbol "_NARG" does not exist
|
||||||
error: Assembly aborted (4 errors)!
|
error: Assembly aborted (6 errors)!
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
$0
|
$0
|
||||||
|
?
|
||||||
|
|
||||||
?
|
?
|
||||||
$0
|
$0
|
||||||
?
|
?
|
||||||
$42
|
$42
|
||||||
$42!
|
$42!
|
||||||
|
$42
|
||||||
|
LabelScope!
|
||||||
$3
|
$3
|
||||||
$3!
|
$3!
|
||||||
|
|||||||
Reference in New Issue
Block a user