我正在尝试将一个antlr项目从c++迁移到go。语法和代码生成主要是完成的(基于65038949中提供的解决方案),但是一个挂起的项目是在go中编写自定义错误报告。
为此目的,我正在寻找一个自定义错误报告:
下面是在c++项目中所做的工作:
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;如何为antlr的go目标重写这些c++代码?
在浏览了antlr运行时代码(来自github.com/antlr/antlr 4/运行时/Go/antlr)之后,还需要做一些额外的说明:
发布于 2021-02-07 17:48:57
Antlr很棒,但是有一点要注意的是,当涉及到错误处理时,它并不是惯用的Go。这使得整个错误过程对GoLang工程师来说是不直观的。
为了在每一步中注入您自己的错误处理(词法、解析、行走),您必须向错误侦听器/处理程序注入恐慌。恐慌和恢复非常类似于Java异常,我认为这就是为什么它是这样设计的(Antlr是用Java编写的)。
Lex/Parse错误收集(容易完成)
您可以实现任意数量的ErrorListeners。默认使用的是ConsoleErrorListenerInstance。它所做的就是打印到SyntaxErrors上的stderr,所以我们删除它。自定义错误报告的第一步是替换它。我做了一个基本的错误,它只收集自定义类型中的错误,以后可以使用/报告。
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(同时清除默认监听器)。
lexerErrors := &CustomErrorListener{}
lexer := NewMyLexer(is)
lexer.RemoveErrorListeners()
lexer.AddErrorListener(lexerErrors)
parserErrors := &CustomErrorListener{}
parser := NewMyParser(stream)
p.removeErrorListeners()
p.AddErrorListener(parserErrors)当词法/解析结束时,两个数据结构都会有在词法/解析阶段发现的语法错误。您可以在SyntaxError中给出的字段中游玩。您必须在其他地方寻找其他接口函数,如ReportAmbuiguity。
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表达式
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
}这个恐慌错误会被捕获并传递给实现ErrorHandler的ErrorStrategy。我们关心的重要功能是Recover()。恢复从错误中恢复的尝试,使用令牌流,直到找到预期的模式/令牌。既然我们想要这个失败,我们可以从BailErrorStrategy获得灵感。这个策略仍然很糟糕,因为它使用恐慌来停止所有的工作。您可以简单地省略这个实现。
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
}然后添加到解析器中。
parser.SetErrorHandler(NewBetterBailErrorStrategy())话虽如此,我还是建议您与听众一起收集错误,不要费心提前中止。BailErrorStrategy似乎并没有那么好地发挥作用,使用恐慌来恢复感觉在GoLang中非常笨重,很容易陷入困境。
https://stackoverflow.com/questions/66067549
复制相似问题