我有以下Antlr4语法:
grammar CategoryExpr;
@header {
package org.example.antlr;
}
moneyTerm
: dollars moneyTermSuffixes*
;
moneyTermSuffixes
: '*' DIGITS # MoneyMult
| '/' DIGITS # MoneyDiv
;
dollars : DIGITS ('.' DIGITS)? ;
DIGITS : [0-9]+ ;
ERRORCHAR : . ;
以及以下Kotlin代码:
private class MyListener : CategoryExprBaseListener() {
override fun enterMoneyTerm(ctx: MoneyTermContext) {
System.out.println(ctx.dollars().text.toDouble()) // ctx.dollars() unexpectedly returns null here!
}
override fun exitMoneyMult(ctx: CategoryExprParser.MoneyMultContext) {}
override fun exitMoneyDiv(ctx: CategoryExprParser.MoneyDivContext) {}
override fun exitMoneyTerm(ctx: MoneyTermContext) {
System.out.println(ctx.dollars().text.toDouble()) // ctx.dollars() returns non-null here.
}
}
fun testMoneyTerm() {
val input = CharStreams.fromString("1.5")
val lexer = CategoryExprLexer(input)
val tokens = CommonTokenStream(lexer)
val listener = MyListener()
CategoryExprParser(tokens).apply {
errorHandler = BailErrorStrategy()
buildParseTree = false
addParseListener(listener)
moneyTerm()
}
}
我的计划是使用enterMoneyTerm侦听器回调函数将MyListener的成员变量(属性)初始化为与"美元"解析规则匹配的值,然后让exitMoneyMult和exitMoneyDiv的侦听器通过乘以或除以与这些规则相关的DIGITS值来修改该变量的值。
然而,这种方法似乎不起作用,因为在enterMoneyTerm函数中,如果我尝试计算"ctx.dollars()",它会意外地返回null。(我似乎无法在调用enterMoneyTerm中检索到与"美元"相关的值。)
请注意,如果我在exitMoneyTerm中调用ctx.dollars().text,我会返回正确的值("1.5"),但到那时,当然已经太晚了,因为我需要从左到右进行乘法和除法,到那时,我已经递归了moneyTermSuffix。
我不明白为什么会发生这种"从ctx.dollars()返回null"的行为,也不明白该怎么办
我想我可以列出moneyTermSuffix隐含的乘法和除法,然后在exitMoneyTerm中的事实之后应用它们,但这似乎相当不雅,如果可能的话,我宁愿避免它。
有人能向我解释为什么ctx.dollars()在enterMoneyTerm中返回null吗?或者我可以对此做些什么,以便在解析moneyTermSuffix之前收集"美元"文本的值?
编辑:为了澄清,请注意,在我的大型应用程序中,转换为使用基于树遍历器的方法而不是基于侦听器的方法对我来说并不是一个真正的选择。我希望有一种方法可以最小限度地修改我的语法,以确保解析器在开始为moneyTermSuffixes规则发出回调之前,已经调用了侦听器回调,为我提供了"美元"规则匹配的完整文本值。
使用Parser.addParseListener
时,侦听器将在解析器进行解析时被调用。enter
方法将在解析子表达式之前调用,exit
方法将在子表达式解析之后调用。
这就解释了为什么子对象在上下文对象中是null
:它们还没有被解析,所以还没有构建解析树。
为了避免这个问题,可以在构建完整个解析树之后使用ParseTreeWalker.DEFAULT.walk
而不是addParseListener
来应用监听器。