代码之家  ›  专栏  ›  技术社区  ›  Some Guy

在antl4 enter listener中,访问函数意外返回空值

  •  0
  • Some Guy  · 技术社区  · 6 年前

    我有以下的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 listener回调函数将my listener的成员变量(属性)初始化为与“美元”解析规则匹配的值,然后让exitmoneymult和exitmoneydiv的侦听器通过乘以或除以与每个规则关联的数字值来修改该变量的值。

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

    请注意,如果我调用exitMoneyTerm中的ctx.dollars().text,我会得到正确的返回值(“1.5”),但到那时,当然为时已晚,因为我需要从左到右进行乘法和除法运算,到那时,我已经从moneyTermSuffixes中恢复过来了。

    我不明白为什么会发生这种“从ctx.dollars()返回空值”的行为,或者如何处理它。(在调试过程中,我注意到MoneyTermContext对象的“children”对象设置为空,这解释了为什么dollars()返回空值,但本身无法解释。)

    我想我可以列出货币后缀隐含的乘法和除法,然后在exitmoneyterm中的事实之后应用它们,但这看起来相当不雅,如果可能的话,我宁愿避免它。

    有人能告诉我为什么ctx.dollars()在entermoneyterm中返回空值,或者我能做些什么,以便在分析moneytermsaffixes之前收集“dollars”文本的值吗?

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

    1 回复  |  直到 6 年前
        1
  •  1
  •   sepp2k    6 年前

    使用时 Parser.addParseListener ,将在分析程序分析时调用侦听器。这个 enter 方法将在分析子表达式和 exit 方法。

    所以这就解释了为什么孩子们 null 在Context对象中:它们还没有被解析,因此没有构建解析树。

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