fix: Phase 3 — #25,28,29,41 token/AST/parser cleanup

Files modified (4):
  compiler/token/token.go — #25: Replace hand-rolled itoa with strconv.Itoa
    Fixes math.MinInt overflow bug in original implementation
  compiler/ast/ast.go — #29: Fix VarDecl.End() returning last var position
    Was returning Pos() (useless span info)
  compiler/parser/stmtreg.go — #28: Eliminate all 7 token array mutations
    rewriteAsIdent() modifies p.current only, not the token array
    Prevents backtracking corruption and improves safety
  compiler/lexer/lexer.go — Already clean from Phase 2

Issues resolved: #25 (MEDIUM), #28 (MEDIUM), #29 (MEDIUM), #41 partial (LOW)
Total fixed: 29/53

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-01 11:58:20 +09:00
parent f950cb0784
commit 6ffcf77dd8
4 changed files with 103 additions and 51 deletions

View File

@@ -88,6 +88,13 @@ func (p *Parser) lookupStmtParser() StmtParser {
// --- Thin wrappers: each calls the existing parse method ---
// rewriteAsIdent treats the current keyword token as an identifier for expression parsing.
// Instead of mutating the token array (which breaks backtracking), it temporarily
// overwrites the current token and restores nothing — the token is consumed immediately.
func (p *Parser) rewriteAsIdent(name string) {
p.current = token.Token{Kind: token.IDENT, Literal: name, Pos: p.current.Pos}
}
func (p *Parser) stmtIf() ast.Stmt {
if p.peekAt(1) == token.LPAREN {
if p.looksLikeIIF() {
@@ -99,9 +106,7 @@ func (p *Parser) stmtIf() ast.Stmt {
func (p *Parser) stmtDo() ast.Stmt {
if p.peekAt(1) == token.LPAREN {
p.tokens[p.pos].Kind = token.IDENT
p.tokens[p.pos].Literal = "Do"
p.current = p.tokens[p.pos]
p.rewriteAsIdent("Do")
return p.parseExprStmt()
}
if p.peekAt(1) == token.CASE || token.LookupKeyword(p.peekLitAt(1)) == token.CASE {
@@ -118,9 +123,7 @@ func (p *Parser) stmtDo() ast.Stmt {
func (p *Parser) stmtWhile() ast.Stmt {
if p.peekAt(1) == token.LPAREN {
p.tokens[p.pos].Kind = token.IDENT
p.tokens[p.pos].Literal = "While"
p.current = p.tokens[p.pos]
p.rewriteAsIdent("While")
return p.parseExprStmt()
}
return p.parseDoWhile()
@@ -130,9 +133,7 @@ func (p *Parser) stmtFor() ast.Stmt {
next := p.peekAt(1)
if next == token.ASSIGN || next == token.LPAREN ||
next == token.PLUSEQ || next == token.MINUSEQ {
p.tokens[p.pos].Kind = token.IDENT
p.tokens[p.pos].Literal = "for"
p.current = p.tokens[p.pos]
p.rewriteAsIdent("for")
return p.parseExprStmt()
}
return p.parseFor()
@@ -140,9 +141,7 @@ func (p *Parser) stmtFor() ast.Stmt {
func (p *Parser) stmtBegin() ast.Stmt {
if p.peekAt(1) != token.SEQUENCE && p.peekAt(1) != token.NEWLINE && p.peekAt(1) != token.EOF {
p.tokens[p.pos].Kind = token.IDENT
p.tokens[p.pos].Literal = "begin"
p.current = p.tokens[p.pos]
p.rewriteAsIdent("begin")
return p.parseExprStmt()
}
return p.parseBeginSequence()
@@ -153,9 +152,7 @@ func (p *Parser) stmtSwitch() ast.Stmt { return p.parseSwitch() }
func (p *Parser) stmtReturn() ast.Stmt {
next := p.peekAt(1)
if next == token.ASSIGN || next == token.PLUSEQ || next == token.MINUSEQ {
p.tokens[p.pos].Kind = token.IDENT
p.tokens[p.pos].Literal = "return"
p.current = p.tokens[p.pos]
p.rewriteAsIdent("return")
return p.parseExprStmt()
}
return p.parseReturn()
@@ -179,8 +176,7 @@ func (p *Parser) stmtPublic() ast.Stmt { return p.parsePrivatePublic(ast.ScopeP
func (p *Parser) stmtVarDecl() ast.Stmt { return p.parseVarDecl() }
func (p *Parser) stmtParameters() ast.Stmt {
p.tokens[p.pos].Kind = token.LOCAL
p.current = p.tokens[p.pos]
p.current = token.Token{Kind: token.LOCAL, Literal: "LOCAL", Pos: p.current.Pos}
return p.parseVarDecl()
}
@@ -201,9 +197,7 @@ func (p *Parser) stmtAt() ast.Stmt { return p.parseAtCmd() }
func (p *Parser) stmtGo() ast.Stmt {
if p.current.Kind == token.GO && p.peekAt(1) == token.LPAREN {
p.tokens[p.pos].Kind = token.IDENT
p.tokens[p.pos].Literal = "Go"
p.current = p.tokens[p.pos]
p.rewriteAsIdent("Go")
return p.parseExprStmt()
}
return p.parseGo()