Skip to content

Commit a86f145

Browse files
committed
Symbol names with all dots could be defined as constants
Such names could also trip an assertion in `make develop` when used as labels
1 parent 533d32d commit a86f145

File tree

4 files changed

+182
-9
lines changed

4 files changed

+182
-9
lines changed

src/asm/lexer.cpp

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1268,6 +1268,16 @@ static uint32_t readGfxConstant() {
12681268
// Functions to read identifiers and keywords
12691269

12701270
static Token readIdentifier(char firstChar, bool raw) {
1271+
// Greedily lex label scopes `..` and `.` now, to prevent lexing `...` or longer
1272+
if (firstChar == '.') {
1273+
if (int c = peek(); c == '.') {
1274+
shiftChar();
1275+
return Token(T_(SYMBOL), "..");
1276+
} else if (!continuesIdentifier(c)) {
1277+
return Token(T_(SYMBOL), ".");
1278+
}
1279+
}
1280+
12711281
std::string identifier(1, firstChar);
12721282
bool keywordBeforeLocal = false;
12731283
int tokenType = firstChar == '.' ? T_(LOCAL) : T_(SYMBOL);
@@ -1294,10 +1304,8 @@ static Token readIdentifier(char firstChar, bool raw) {
12941304
}
12951305
}
12961306

1297-
// Label scopes `.` and `..` are the only nonlocal identifiers that start with a dot
1298-
if (identifier.find_first_not_of('.') == identifier.npos) {
1299-
tokenType = T_(SYMBOL);
1300-
}
1307+
// Label scopes `.` and `..` were already lexed
1308+
assume(identifier.find_first_not_of('.') != identifier.npos);
13011309

13021310
// A keyword before a non-raw local label is an error
13031311
if (keywordBeforeLocal) {
@@ -1935,11 +1943,12 @@ static Token yylex_NORMAL() {
19351943

19361944
// `token` is either a `SYMBOL` or a `LOCAL`, and both have a `std::string` value.
19371945
assume(std::holds_alternative<std::string>(token.value));
1946+
std::string const &identifier = std::get<std::string>(token.value);
19381947

19391948
// Raw symbols and local symbols cannot be string expansions
19401949
if (!raw && token.type == T_(SYMBOL) && lexerState->expandStrings) {
19411950
// Attempt string expansion
1942-
if (Symbol const *sym = sym_FindExactSymbol(std::get<std::string>(token.value));
1951+
if (Symbol const *sym = sym_FindExactSymbol(identifier);
19431952
sym && sym->type == SYM_EQUS) {
19441953
beginExpansion(sym->getEqus(), sym->name);
19451954
continue; // Restart, reading from the new buffer
@@ -1950,6 +1959,7 @@ static Token yylex_NORMAL() {
19501959
// - label definitions (which are followed by a ':' and use the token `LABEL`)
19511960
// - quiet macro invocations (which are followed by a '?' and use the token `QMACRO`)
19521961
// - regular macro invocations (which use the token `SYMBOL`)
1962+
// - label scopes `.` and `..` (which use the token `SYMBOL` no matter what follows)
19531963
//
19541964
// If we had one `IDENTIFIER` token, the parser would need to perform "lookahead" to
19551965
// determine which rule applies. But since macros need to enter "raw" mode to parse
@@ -1960,7 +1970,7 @@ static Token yylex_NORMAL() {
19601970
// one to lex depending on the character *immediately* following the identifier.
19611971
// Thus "name:" is a label definition, and "name?" is a quiet macro invocation, but
19621972
// "name :" and "name ?" and just "name" are all regular macro invocations.
1963-
if (token.type == T_(SYMBOL)) {
1973+
if (token.type == T_(SYMBOL) && identifier.find_first_not_of('.') != identifier.npos) {
19641974
c = peek();
19651975
token.type = c == ':' ? T_(LABEL) : c == '?' ? T_(QMACRO) : T_(SYMBOL);
19661976
}

src/asm/symbol.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -215,8 +215,11 @@ static void redefinedError(Symbol const &sym) {
215215

216216
static void assumeAlreadyExpanded(std::string const &symName) {
217217
// Either the symbol name is `Global.local` or entirely '.'s (for scopes `.` and `..`),
218-
// but cannot be unqualified `.local`
219-
assume(!symName.starts_with('.') || symName.find_first_not_of('.') == symName.npos);
218+
// but cannot be unqualified `.local` or a nested scope (`...`, `....`, etc)
219+
assume(
220+
!symName.starts_with('.')
221+
|| (symName.find_first_not_of('.') == symName.npos && (symName == "." || symName == ".."))
222+
);
220223
}
221224

222225
static Symbol &createSymbol(std::string const &symName) {
@@ -253,7 +256,9 @@ static bool isAutoScoped(std::string const &symName) {
253256
}
254257

255258
// Label scopes `.` and `..` are the only nonlocal identifiers that start with a dot
256-
if (dotPos == 0 && symName.find_first_not_of('.') == symName.npos) {
259+
// It is possible here for symName to be three or more dots if it was interpolated,
260+
// e.g. "{...}", so we handle that case with the fatal errors below
261+
if (symName == "." || symName == "..") {
257262
return false;
258263
}
259264

test/asm/nested-label-scopes.asm

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
section "test", rom0
2+
3+
#.
4+
#.:
5+
#.?
6+
def #. equs "#one"
7+
jp #.
8+
9+
#..
10+
#..:
11+
#..?
12+
def #.. equs "#two"
13+
jp #..
14+
15+
...
16+
...:
17+
...?
18+
def ... equs "three"
19+
jp ....
20+
#...
21+
#...:
22+
#...?
23+
def #... equs "#three"
24+
jp #...
25+
26+
....
27+
....:
28+
....?
29+
def .... equs "four"
30+
jp ....
31+
#....
32+
#....:
33+
#....?
34+
def #.... equs "#four"
35+
jp #....
36+
37+
assert !strlen("{.}")
38+
assert !strlen("{..}")
39+
assert !strlen("{...}")
40+
assert !strlen("{....}")

test/asm/nested-label-scopes.err

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
error: `.` is not a macro
2+
at nested-label-scopes.asm(3)
3+
error: `.` is not a macro
4+
at nested-label-scopes.asm(4)
5+
error: `.` is not a macro
6+
at nested-label-scopes.asm(5)
7+
error: `.` is reserved for a built-in symbol
8+
at nested-label-scopes.asm(6)
9+
error: `.` has no value outside of a label scope
10+
at nested-label-scopes.asm(7)
11+
warning: Treating strings as numbers is deprecated; use character literals or `CHARVAL` instead [-Wobsolete]
12+
at nested-label-scopes.asm(7)
13+
error: Strings as numbers must be a single charmap unit
14+
at nested-label-scopes.asm(7)
15+
error: `..` is not a macro
16+
at nested-label-scopes.asm(9)
17+
error: `..` is not a macro
18+
at nested-label-scopes.asm(10)
19+
error: `..` is not a macro
20+
at nested-label-scopes.asm(11)
21+
error: `..` is reserved for a built-in symbol
22+
at nested-label-scopes.asm(12)
23+
error: `..` has no value outside of a local label scope
24+
at nested-label-scopes.asm(13)
25+
warning: Treating strings as numbers is deprecated; use character literals or `CHARVAL` instead [-Wobsolete]
26+
at nested-label-scopes.asm(13)
27+
error: Strings as numbers must be a single charmap unit
28+
at nested-label-scopes.asm(13)
29+
error: `..` has no value outside of a local label scope
30+
at nested-label-scopes.asm(15)
31+
error: `.` has no value outside of a label scope
32+
at nested-label-scopes.asm(15)
33+
error: `..` has no value outside of a local label scope
34+
at nested-label-scopes.asm(16)
35+
error: `.` has no value outside of a label scope
36+
at nested-label-scopes.asm(16)
37+
error: `..` has no value outside of a local label scope
38+
at nested-label-scopes.asm(17)
39+
error: `.` has no value outside of a label scope
40+
at nested-label-scopes.asm(17)
41+
error: syntax error, unexpected ?
42+
at nested-label-scopes.asm(17)
43+
error: `.` has no value outside of a label scope
44+
at nested-label-scopes.asm(18)
45+
error: `..` is reserved for a built-in symbol
46+
at nested-label-scopes.asm(18)
47+
error: `..` has no value outside of a local label scope
48+
at nested-label-scopes.asm(19)
49+
error: `..` has no value outside of a local label scope
50+
at nested-label-scopes.asm(19)
51+
error: syntax error, unexpected end of line
52+
at nested-label-scopes.asm(19)
53+
error: `..` is not a macro
54+
at nested-label-scopes.asm(20)
55+
error: `..` is not a macro
56+
at nested-label-scopes.asm(21)
57+
error: `..` is not a macro
58+
at nested-label-scopes.asm(22)
59+
error: `.` has no value outside of a label scope
60+
at nested-label-scopes.asm(23)
61+
error: `..` is reserved for a built-in symbol
62+
at nested-label-scopes.asm(23)
63+
error: `.` has no value outside of a label scope
64+
at nested-label-scopes.asm(24)
65+
error: `..` has no value outside of a local label scope
66+
at nested-label-scopes.asm(24)
67+
warning: Treating strings as numbers is deprecated; use character literals or `CHARVAL` instead [-Wobsolete]
68+
at nested-label-scopes.asm(24)
69+
error: Strings as numbers must be a single charmap unit
70+
at nested-label-scopes.asm(24)
71+
error: `..` has no value outside of a local label scope
72+
at nested-label-scopes.asm(26)
73+
error: `..` has no value outside of a local label scope
74+
at nested-label-scopes.asm(26)
75+
error: `..` has no value outside of a local label scope
76+
at nested-label-scopes.asm(27)
77+
error: `..` has no value outside of a local label scope
78+
at nested-label-scopes.asm(27)
79+
error: `..` has no value outside of a local label scope
80+
at nested-label-scopes.asm(28)
81+
error: `..` has no value outside of a local label scope
82+
at nested-label-scopes.asm(28)
83+
error: syntax error, unexpected ?
84+
at nested-label-scopes.asm(28)
85+
error: `..` has no value outside of a local label scope
86+
at nested-label-scopes.asm(29)
87+
error: `..` is reserved for a built-in symbol
88+
at nested-label-scopes.asm(29)
89+
error: `..` has no value outside of a local label scope
90+
at nested-label-scopes.asm(30)
91+
error: `..` has no value outside of a local label scope
92+
at nested-label-scopes.asm(30)
93+
error: syntax error, unexpected end of line
94+
at nested-label-scopes.asm(30)
95+
error: `..` is not a macro
96+
at nested-label-scopes.asm(31)
97+
error: `..` is not a macro
98+
at nested-label-scopes.asm(32)
99+
error: `..` is not a macro
100+
at nested-label-scopes.asm(33)
101+
error: `..` has no value outside of a local label scope
102+
at nested-label-scopes.asm(34)
103+
error: `..` is reserved for a built-in symbol
104+
at nested-label-scopes.asm(34)
105+
error: `..` has no value outside of a local label scope
106+
at nested-label-scopes.asm(35)
107+
error: `..` has no value outside of a local label scope
108+
at nested-label-scopes.asm(35)
109+
warning: Treating strings as numbers is deprecated; use character literals or `CHARVAL` instead [-Wobsolete]
110+
at nested-label-scopes.asm(35)
111+
error: Strings as numbers must be a single charmap unit
112+
at nested-label-scopes.asm(35)
113+
error: Interpolated symbol `.` does not exist
114+
at nested-label-scopes.asm(37)
115+
error: Interpolated symbol `..` does not exist
116+
at nested-label-scopes.asm(38)
117+
FATAL: `...` is a nonsensical reference to a nested local label
118+
at nested-label-scopes.asm(39)

0 commit comments

Comments
 (0)