fix(parser): peel stacked labels the way the formatter renders them

Assisted-by: GLM 5.3
This commit is contained in:
2026-10-02 00:40:20 +02:00
parent 9a5217d9c1
commit 6570709226
2 changed files with 19 additions and 11 deletions
@@ -0,0 +1,2 @@
go test fuzz v1
string("A:A:")
+17 -11
View File
@@ -160,12 +160,20 @@ func (p *state) parseLine(line []token.Token) {
return
}
// Label (ident immediately followed by a colon).
// Label (ident immediately followed by a colon). Stacked labels
// share one line ("a: b: MOVQ …"): each peels off as its own Label,
// the same statement sequence the canonical layout puts on separate
// lines, so a file and its formatted image parse alike. A directive
// never peels: it stays with the instruction parse, as in the
// formatter's label handling.
if len(line) >= 2 && line[1].Kind == token.Colon {
lbl := &ast.Label{Name: line[0], Colon: line[1]}
p.addStmt(lbl)
// A label may share its line with an instruction: "loop: MOVQ …".
if rest := dropColon(line); len(rest) > 0 {
rest := line
for len(rest) >= 2 && rest[0].Kind == token.Ident && rest[1].Kind == token.Colon &&
!isDirective(rest[0].Text) {
p.addStmt(&ast.Label{Name: rest[0], Colon: rest[1]})
rest = rest[2:]
}
if len(rest) > 0 {
p.parseInstr(rest)
}
p.pending = nil
@@ -182,12 +190,10 @@ func (p *state) parseLine(line []token.Token) {
p.pending = nil
}
// dropColon removes the leading "ident :" of a label, returning the remainder.
func dropColon(line []token.Token) []token.Token {
if len(line) >= 2 && line[1].Kind == token.Colon {
return line[2:]
}
return nil
// isDirective reports whether the identifier names a top-level declaration
// directive rather than an instruction or a label.
func isDirective(s string) bool {
return s == "TEXT" || s == "DATA" || s == "GLOBL"
}
func (p *state) addStmt(s ast.Stmt) {