在ANTLR Go Runtime的Listener中抛出错误的正确方式
在Go ANTLR Listener中实现错误检查与终止遍历
ANTLR生成的Listener接口方法确实不支持返回error,要实现语义检查时的错误抛出和遍历终止,可通过以下两种实用方式:
方式一:通过共享错误状态+Panic立即终止
给自定义Listener添加错误字段,在检查到错误时设置字段并触发panic,随后在遍历入口处捕获panic并转为标准error返回,这样能立即终止遍历流程。
步骤1:修改Listener结构体
import "fmt" type Listener struct { stack []ast.Node err error // 存储错误信息 } // 补充带错误检查的pop方法 func (l *Listener) pop() ast.Node { if len(l.stack) == 0 { err := fmt.Errorf("stack underflow when popping StatementBlock") l.err = err panic(err) } node := l.stack[len(l.stack)-1] l.stack = l.stack[:len(l.stack)-1] return node }
步骤2:在Listener方法中添加检查逻辑
func (l *Listener) ExitProcedureBody(ctx *gen.ProcedureBodyContext) { stmtBlock, ok := l.pop().(*ast.StatementBlock) if !ok { err := fmt.Errorf("invalid node type on stack: expected *ast.StatementBlock, got %T", stmtBlock) l.err = err panic(err) // 触发panic终止遍历 } l.push(&ast.ProcedureBody{ StatementBlock: stmtBlock, }) }
步骤3:在遍历入口处捕获Panic
func ProcessInput(input string) error { // 初始化ANTLR词法分析器、语法分析器 lexer := gen.NewYourLexer(antlr.NewInputStream(input)) stream := antlr.NewCommonTokenStream(lexer, antlr.TokenDefaultChannel) parser := gen.NewYourParser(stream) listener := &Listener{} walker := antlr.NewParseTreeWalker() // 捕获panic并转为error defer func() { if r := recover(); r != nil { if err, ok := r.(error); ok { listener.err = err } else { listener.err = fmt.Errorf("traversal failed: %v", r) } } }() // 启动遍历 walker.Walk(listener, parser.ProcedureBody()) return listener.err }
方式二:仅记录错误,遍历完成后再处理
如果不需要立即终止遍历,可只在Listener中记录错误,遍历结束后统一检查并返回。这种方式适合需要收集所有错误的场景:
func (l *Listener) ExitProcedureBody(ctx *gen.ProcedureBodyContext) { if len(l.stack) == 0 { l.err = fmt.Errorf("stack underflow at ProcedureBody exit") return } stmtBlock, ok := l.stack[len(l.stack)-1].(*ast.StatementBlock) if !ok { l.err = fmt.Errorf("invalid node type: expected *ast.StatementBlock") return } l.stack = l.stack[:len(l.stack)-1] l.push(&ast.ProcedureBody{StatementBlock: stmtBlock}) } // 遍历入口无需panic捕获,直接返回错误 func ProcessInput(input string) error { // 初始化词法/语法分析器(省略) listener := &Listener{} antlr.NewParseTreeWalker().Walk(listener, parser.ProcedureBody()) return listener.err }
注意事项
- 使用panic方式时,要确保只捕获遍历过程中自己抛出的错误,避免掩盖其他未知panic。
- 如果需要在错误中携带位置信息(如行号、列号),可通过
ctx.GetStart().GetLine()和ctx.GetStart().GetColumn()获取并添加到错误信息中。
内容的提问来源于stack exchange,提问作者Michael Burbidge
相关产品推荐
相关产品推荐

