Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/352.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在Antlr4 enter listener中,访问器函数意外返回null_Antlr4 - Fatal编程技术网

在Antlr4 enter listener中,访问器函数意外返回null

在Antlr4 enter listener中,访问器函数意外返回null,antlr4,Antlr4,我有以下Antlr4语法: grammar CategoryExpr; @header { package org.example.antlr; } moneyTerm : dollars moneyTermSuffixes* ; moneyTermSuffixes : '*' DIGITS # MoneyMult | '/' DIGITS # MoneyDiv ; dollars : DIGITS ('.' DIGITS)? ; DIGITS : [0-

我有以下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的侦听器通过乘以或除以与这些规则中的每个规则关联的数字值来修改该变量的值

但是,这种方法似乎不起作用,因为在enterMoneyTerm函数中,如果我尝试计算“ctx.dollars()”,它会意外地返回null。(在调用enterMoneyTerm时,我似乎无法检索与“美元”关联的值。)

请注意,如果我在exitMoneyTerm中调用ctx.dollars().text,我会返回正确的值(“1.5”),但到那时,当然已经太晚了,因为我需要从左到右进行乘法和除法,到那时,我已经递归出了MoneyTerm后缀

我不明白为什么会发生这种“从ctx.dollars()返回null”的行为,或者该怎么办。(在调试过程中,我观察到MoneyTermContext对象的“children”对象设置为null,这解释了为什么$s()返回null,但其本身无法解释。)

我想我可以列出MoneyTerm后缀所隐含的乘法和除法,然后在exitMoneyTerm中应用它们,但这似乎很不雅观,如果可能的话,我宁愿避免

有人能解释一下为什么ctx.dollars()在enterMoneyTerm中返回null,或者我能做些什么,以便在解析MoneyTerm后缀之前收集“dollars”文本的值吗


编辑:为了澄清这一点,请注意,在我的大型应用程序的上下文中,转换为使用基于树行者的方法而不是基于侦听器的方法并不是我真正的选择。我希望有一种方法可以最小限度地修改我的语法,以确保解析器在开始为MoneyTermSuffix规则发出回调之前调用侦听器回调,为我提供“美元”规则匹配的完整文本值。

当使用
parser.addParseListener
时,解析器解析时将调用侦听器。在分析子表达式之前将调用
enter
方法,在分析子表达式之后将调用
exit
方法

这就解释了为什么上下文对象中的子对象
null
:它们还没有被解析,所以没有构造解析树


为了避免此问题,您可以使用
ParseTreeWalker.DEFAULT.walk
而不是
addParseListener
在构建整个解析树后应用侦听器。

您如何应用侦听器?您使用的是
ParseTreeWalker
还是
Parser.addParseListener
?我使用的是addParseListener。您可以在Kotlin代码示例中看到对它的调用。对不起,我想我没有仔细阅读代码。嗯。这让我想知道为什么上下文会被传递到“enter”函数中。我知道我可以使用树行走而不是侦听器来避免这个问题。然而,在我的大型应用程序的语法中,这个语法只是一个很小的子集,使用侦听器是相当可取的,所以不幸的是,这个解决方案对我来说并不真正有效。有没有一种方法可以让我重新构造语法,迫使“美元”规则在“MoneyTermSuffix”规则之前完全执行?@SomeGuy
ParsetRewalker
不是监听器的替代方案(
walk
仍然将监听器作为参数)。这是应用侦听器的另一种方法(或者更确切地说是首选方法)。您能解释一下为什么在您的情况下使用
addParseListener
更可取吗?如果您确实必须使用
addParseListener,您可能不得不忍受无法使用
enter`methods中的子级-我认为在使用addParseListener时没有办法解决这个问题。还要注意,我主要是为了提高速度,避免在尝试执行此操作时创建另一个临时的额外AST数据结构已经在我的输入和退出回调中创建了我自己的数据结构。在任何情况下,通过您对在子表达式之前调用enter但在子表达式之后调用exit的行为的解释,我能够想出一个解决方案:在exitDollars(已知美元值的地方)中设置变量,而不是在enterMoneyTerm(还没有)中设置变量。这似乎奏效了。将此标记为已接受的答案。谢谢你的帮助!