ANTLR创建必填Token问题:Hello World语法报错排查
解决ANTLR Go解析器错误提示不准确的问题
我明白你现在的困扰啦——当输入只有Hello时,ANTLR给出的错误提示有点让人摸不着头脑,明明应该提示缺少world,结果却说是期待另一个Hello。别担心,咱们一步步来搞定这个问题~
问题根源
你的语法规则start : HELLO WORLD EOF本身是正确的,但ANTLR的默认错误提示逻辑有时候会因为解析器的预测回溯机制,给出不够精准的提示。当输入只到HELLO就结束时,解析器在尝试匹配规则的过程中,误判了预期的Token类型。
解决方案:自定义错误监听器
ANTLR允许我们通过自定义错误监听器来捕获并改写错误提示,这样就能输出更符合用户预期的友好信息。下面是修改后的完整代码:
修改后的main.go
package main import ( "fmt" "test/parser" "github.com/antlr/antlr4/runtime/Go/antlr" ) const rule = `Hello` type testListener struct { *parser.BasehelloworldListener } // 自定义错误监听器,用于改写错误提示 type customErrorListener struct { *antlr.DefaultErrorListener } // 重写SyntaxError方法,实现自定义错误提示逻辑 func (c *customErrorListener) SyntaxError(recognizer antlr.Recognizer, offendingSymbol interface{}, line, column int, msg string, e antlr.RecognitionException) { // 尝试将识别器转为我们的解析器类型,以便获取预期Token信息 if parserInst, ok := recognizer.(*parser.HelloworldParser); ok { expectedTokens := parserInst.GetExpectedTokens() // 检查是否期望WORLD这个Token if expectedTokens.Contains(parserInst.WORLD) { fmt.Printf("line %d:%d 错误:hello后需输入world\n", line, column) return } } // 其他错误情况,输出默认提示 fmt.Printf("line %d:%d %s\n", line, column, msg) } func main() { // 初始化输入流 is := antlr.NewInputStream(rule) // 创建词法分析器 lexer := parser.NewhelloworldLexer(is) // 打印所有Token(可选,用于调试) for { t := lexer.NextToken() if t.GetTokenType() == antlr.TokenEOF { break } fmt.Printf("%s (%q)\n", lexer.SymbolicNames[t.GetTokenType()], t.GetText()) } // 创建Token流 stream := antlr.NewCommonTokenStream(lexer, antlr.TokenDefaultChannel) // 创建语法分析器 p := parser.NewhelloworldParser(stream) // 移除默认错误监听器,添加我们自定义的 p.RemoveErrorListeners() p.AddErrorListener(&customErrorListener{}) // 开始解析并遍历语法树 antlr.ParseTreeWalkerDefault.Walk(&testListener{}, p.Start()) }
代码说明
- 自定义错误监听器:我们创建了
customErrorListener结构体,继承自ANTLR的默认错误监听器,并重写了SyntaxError方法。 - 错误判断逻辑:在方法中,我们检查解析器当前预期的Token是否包含
WORLD,如果是的话,就输出自定义的友好提示;其他错误则保留默认提示。 - 替换错误监听器:在main函数中,我们移除了默认的错误监听器,替换成自己实现的,这样错误提示就会按照我们的逻辑输出。
测试效果
现在重新执行构建和运行命令:
antlr -Dlanguage=Go -o parser helloworld.g4 && go run main.go
输出结果会变成:
HELLO ("Hello") line 1:5 错误:hello后需输入world
完美!现在的错误提示就完全符合你的预期了~这种自定义错误处理的方式还能扩展到更复杂的语法场景中,根据不同的错误场景输出更精准的提示,提升用户体验。
内容的提问来源于stack exchange,提问作者Dennis van der Veeke
相关产品推荐
相关产品推荐

