Skip to content

Commit

Permalink
Avoid treating labels and macros differently in column 1 (#1515)
Browse files Browse the repository at this point in the history
Fixes #1512
  • Loading branch information
Rangi42 authored Sep 22, 2024
1 parent 15919e5 commit 6b89938
Show file tree
Hide file tree
Showing 9 changed files with 75 additions and 39 deletions.
1 change: 0 additions & 1 deletion man/rgbasm.5
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ or
Labels tie a name to a specific location within a section (see
.Sx Labels
below).
They must come first in the line.
.Pp
Instructions are assembled into Game Boy opcodes.
Multiple instructions on one line can be separated by double colons
Expand Down
14 changes: 13 additions & 1 deletion src/asm/lexer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1849,7 +1849,19 @@ static Token yylex_NORMAL() {
}
}

if (token.type == T_(ID) && (lexerState->atLineStart || peek() == ':'))
// This is a "lexer hack"! We need it to distinguish between label definitions
// (which start with `LABEL`) and macro invocations (which start with `ID`).
//
// If we had one `IDENTIFIER` token, the parser would need to perform "lookahead"
// to determine which rule applies. But since macros need to enter "raw" mode to
// parse their arguments, which may not even be valid tokens in "normal" mode, we
// cannot use lookahead to check for the presence of a `COLON`.
//
// Instead, we have separate `ID` and `LABEL` tokens, lexing as a `LABEL` if a ':'
// character *immediately* follows the identifier. Thus, at the beginning of a line,
// "Label:" and "mac:" are treated as label definitions, but "Label :" and "mac :"
// are treated as macro invocations.
if (token.type == T_(ID) && peek() == ':')
token.type = T_(LABEL);

return token;
Expand Down
16 changes: 0 additions & 16 deletions src/asm/parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -433,22 +433,6 @@ line:
fstk_StopRept();
yyerrok;
}
// Hint about unindented macros parsed as labels
| LABEL error {
lexer_SetMode(LEXER_NORMAL);
lexer_ToggleStringExpansion(true);
} endofline {
Symbol *macro = sym_FindExactSymbol($1);

if (macro && macro->type == SYM_MACRO)
fprintf(
stderr,
" To invoke `%s` as a macro it must be indented\n",
$1.c_str()
);
fstk_StopRept();
yyerrok;
}
;

endofline: NEWLINE | EOB;
Expand Down
28 changes: 28 additions & 0 deletions test/asm/lexer-hack.asm
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
MACRO mac
println "got {d:_NARG} args: \#"
ENDM

; indented, these were always macro invocations
mac
mac ro
mac : ld a, 1

; in column 1, we historically treated these as labels
mac
mac ro
mac : ld b, 2

SECTION "test", ROM0

; a colon makes these into labels
Label1: ld c, 3
Label2: ld d, 4

; a macro invocation when already defined as a label
Label1 args
; and a label definition when already defined as a macro
mac: ld e, 5

; the space before the colon matters!
undef :
undef :
9 changes: 9 additions & 0 deletions test/asm/lexer-hack.err
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
error: lexer-hack.asm(22):
"Label1" is not a macro
error: lexer-hack.asm(24):
'mac' already defined at lexer-hack.asm(1)
error: lexer-hack.asm(27):
Macro "undef" not defined
error: lexer-hack.asm(28):
Macro "undef" not defined
error: Assembly aborted (4 errors)!
6 changes: 6 additions & 0 deletions test/asm/lexer-hack.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
got 0 args:
got 1 args: ro
got 2 args: : ld a,1
got 0 args:
got 1 args: ro
got 2 args: : ld b,2
21 changes: 11 additions & 10 deletions test/asm/syntax-error-after-syntax-error.asm
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
MACRO mac
println "got {d:_NARG} args"
ENDM
mac
mac 42
notmac
mac
mac 42
mac::
mac ::
def x = 1 ; so far so good...
def n equ 2 + * / ^ 3 ; oops
def s equs "no closing quote, lol
section "test", rom0 ; good again
ld a, 42 ; keep going...
ld xor, ret ; oh no :(
label1: ; yes...
label2:: ; yes...
label3::: ; no!
halt stop abort ; please
println "finally!"
16 changes: 7 additions & 9 deletions test/asm/syntax-error-after-syntax-error.err
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
error: syntax-error-after-syntax-error.asm(2):
syntax error, unexpected *
error: syntax-error-after-syntax-error.asm(3):
Unterminated string
error: syntax-error-after-syntax-error.asm(6):
syntax error, unexpected newline, expecting : or ::
error: syntax-error-after-syntax-error.asm(7):
syntax error, unexpected newline, expecting : or ::
To invoke `mac` as a macro it must be indented
error: syntax-error-after-syntax-error.asm(8):
syntax error, unexpected number, expecting : or ::
To invoke `mac` as a macro it must be indented
syntax error, unexpected xor
error: syntax-error-after-syntax-error.asm(9):
'mac' already defined at syntax-error-after-syntax-error.asm(1)
syntax error, unexpected :
error: syntax-error-after-syntax-error.asm(10):
'mac' already defined at syntax-error-after-syntax-error.asm(1)
syntax error, unexpected stop, expecting newline or end of buffer or ::
error: Assembly aborted (5 errors)!
3 changes: 1 addition & 2 deletions test/asm/syntax-error-after-syntax-error.out
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
got 0 args
got 1 args
finally!

0 comments on commit 6b89938

Please # to comment.