You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 20:33:31