首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在antlr的go目标中编写自定义错误报告

如何在antlr的go目标中编写自定义错误报告
EN

Stack Overflow用户
提问于 2021-02-05 17:07:20
回答 1查看 803关注 0票数 2

我正在尝试将一个antlr项目从c++迁移到go。语法和代码生成主要是完成的(基于65038949中提供的解决方案),但是一个挂起的项目是在go中编写自定义错误报告。

为此目的,我正在寻找一个自定义错误报告:

  1. 我想打印我的自定义消息,可能有额外的信息(如文件名,这不是由默认的错误打印机打印)。
  2. 对于每个错误,错误报告程序更新一个全局计数器,如果这个error_count>0被跳过,那么在主程序中将跳过进一步的处理。

下面是在c++项目中所做的工作:

  1. 在此函数中定义了自定义消息: 字符串MyErrorMessage(无符号int l,无符号int p,字符串m) { << l <<::“<< p <<”“<< m”;全局->错误+;返回s.str()};
  2. 并更新了antlr运行时(ConsoleErrorListener.cpp)以调用上述函数: ::syntaxError(IRecognizer *,Token *,size_t line,size_t charPositionInLine,const::string &msg,std::exception_ptr) { std::cerr << MyErrorMessage(line,charPositionInLine,msg) << std::endl;
  3. 最后,主程序将跳过这样的进一步处理: parser.top_rule();if(全局->错误> 0) { exit(0);}

如何为antlr的go目标重写这些c++代码?

在浏览了antlr运行时代码(来自github.com/antlr/antlr 4/运行时/Go/antlr)之后,还需要做一些额外的说明:

  • "_SyntaxErrors",有一个变量parser.go,它在每次出错时都会增加,但似乎没有人使用它。这个变量的用途是什么,在解析之后如何使用它来检查是否发生了错误?我做了下面的工作,但很明显那是行不通的!(解决办法是在解析器中添加一个新变量MyErrorCount,并在_SyntaxErrors也增加时增加它,但这看起来并不是一个优雅的解决方案,因为这里我正在编辑运行时代码!) 树:= parser.Top_rule() //这是ok fmt.Printf("errors=%d\n",parser._SyntaxErrors) //这会给出一个编译器错误//fmt.Printf("errors=%d\n",parser.MyErrorCount) //这是可以的。
  • 在上面的注释中,我在antlr代码中引入了一个新变量,并以用户代码的方式读取它--糟糕的编码风格,但是有效。但是我也需要做相反的操作--反错误报告器(error_listener.go:SyntaxError())需要读取具有文件名的用户代码的变量并打印它。我可以通过在antlr中添加一个新函数来传递这个参数并在一个新变量中注册这个文件名,但是有更好的方法吗?
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-02-07 17:48:57

Antlr很棒,但是有一点要注意的是,当涉及到错误处理时,它并不是惯用的Go。这使得整个错误过程对GoLang工程师来说是不直观的。

为了在每一步中注入您自己的错误处理(词法、解析、行走),您必须向错误侦听器/处理程序注入恐慌。恐慌和恢复非常类似于Java异常,我认为这就是为什么它是这样设计的(Antlr是用Java编写的)。

Lex/Parse错误收集(容易完成)

您可以实现任意数量的ErrorListeners。默认使用的是ConsoleErrorListenerInstance。它所做的就是打印到SyntaxErrors上的stderr,所以我们删除它。自定义错误报告的第一步是替换它。我做了一个基本的错误,它只收集自定义类型中的错误,以后可以使用/报告。

代码语言:javascript
复制
type CustomSyntaxError struct {
    line, column int
    msg          string
}

type CustomErrorListener struct {
    *antlr.DefaultErrorListener // Embed default which ensures we fit the interface
    Errors []error
}

func (c *CustomErrorListener) SyntaxError(recognizer antlr.Recognizer, offendingSymbol interface{}, line, column int, msg string, e antlr.RecognitionException) {
    c.Errors = append(c.Errors, &CustomSyntaxError{
        line:   line,
        column: column,
        msg:    msg,
    })
}

您可以将错误侦听器注入解析器/lexer(同时清除默认监听器)。

代码语言:javascript
复制
lexerErrors := &CustomErrorListener{}
lexer := NewMyLexer(is)
lexer.RemoveErrorListeners()
lexer.AddErrorListener(lexerErrors)

parserErrors := &CustomErrorListener{}
parser := NewMyParser(stream)
p.removeErrorListeners()
p.AddErrorListener(parserErrors)

当词法/解析结束时,两个数据结构都会有在词法/解析阶段发现的语法错误。您可以在SyntaxError中给出的字段中游玩。您必须在其他地方寻找其他接口函数,如ReportAmbuiguity

代码语言:javascript
复制
    if len(lexerErrors.Errors) > 0 {
        fmt.Printf("Lexer %d errors found\n", len(lexerErrors.Errors))
        for _, e := range lexerErrors.Errors {
            fmt.Println("\t", e.Error())
        }
    }

    if len(parserErrors.Errors) > 0 {
        fmt.Printf("Parser %d errors found\n", len(parserErrors.Errors))
        for _, e := range parserErrors.Errors {
            fmt.Println("\t", e.Error())
        }
    }

Lex/Parse错误中止(不确定这是否可靠)

警告:这真的让人觉得好笑。如果只需要收集错误,只需执行上面所示的操作!

若要中途中止lex/a,您必须在错误侦听器中引发恐慌。老实说,我不认为这个设计是正确的,但是词法/解析代码被包装在恐慌恢复中,用于检查恐慌是否为RecognitionException类型。此异常作为参数传递给您的ErrorListener,因此修改SyntaxError表达式

代码语言:javascript
复制
func (c *CustomErrorListener) SyntaxError(recognizer antlr.Recognizer, offendingSymbol interface{}, line, column int, msg string, e antlr.RecognitionException) {
  // ...
  panic(e) // Feel free to only panic on certain conditions. This stops parsing/lexing
}

这个恐慌错误会被捕获并传递给实现ErrorHandlerErrorStrategy。我们关心的重要功能是Recover()。恢复从错误中恢复的尝试,使用令牌流,直到找到预期的模式/令牌。既然我们想要这个失败,我们可以从BailErrorStrategy获得灵感。这个策略仍然很糟糕,因为它使用恐慌来停止所有的工作。您可以简单地省略这个实现。

代码语言:javascript
复制
type BetterBailErrorStrategy struct {
    *antlr.DefaultErrorStrategy
}

var _ antlr.ErrorStrategy = &BetterBailErrorStrategy{}

func NewBetterBailErrorStrategy() *BetterBailErrorStrategy {

    b := new(BetterBailErrorStrategy)

    b.DefaultErrorStrategy = antlr.NewDefaultErrorStrategy()

    return b
}

func (b *BetterBailErrorStrategy) ReportError(recognizer antlr.Parser, e antlr.RecognitionException) {
    // pass, do nothing
}


func (b *BetterBailErrorStrategy) Recover(recognizer antlr.Parser, e antlr.RecognitionException) {
    // pass, do nothing
}

// Make sure we don't attempt to recover from problems in subrules.//
func (b *BetterBailErrorStrategy) Sync(recognizer antlr.Parser) {
    // pass, do nothing
}

然后添加到解析器中。

代码语言:javascript
复制
parser.SetErrorHandler(NewBetterBailErrorStrategy())

话虽如此,我还是建议您与听众一起收集错误,不要费心提前中止。BailErrorStrategy似乎并没有那么好地发挥作用,使用恐慌来恢复感觉在GoLang中非常笨重,很容易陷入困境。

票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/66067549

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档