【问题标题】:How to know which alternative rule ANTLR parser is currently in during visit访问期间如何知道ANTLR解析器当前处于哪个替代规则
【发布时间】:2022-01-05 22:25:54
【问题描述】:

如果我们查看 bash 源代码,特别是 yacc 语法,我们可以看到所有重定向都是这样定义的:

redirection
        :   GREATER WORD
        |   LESS WORD
        |   NUMBER GREATER WORD
        |   NUMBER LESS WORD
        |   REDIR_WORD GREATER WORD
        |   REDIR_WORD LESS WORD
        |   GREATER_GREATER WORD
        |   NUMBER GREATER_GREATER WORD
        |   REDIR_WORD GREATER_GREATER WORD
        |   GREATER_BAR WORD
        |   NUMBER GREATER_BAR WORD
        |   REDIR_WORD GREATER_BAR WORD
        |   LESS_GREATER WORD
        |   NUMBER LESS_GREATER WORD
        |   REDIR_WORD LESS_GREATER WORD
        |   LESS_LESS WORD
        |   NUMBER LESS_LESS WORD
        |   REDIR_WORD LESS_LESS WORD
        |   LESS_LESS_MINUS WORD
        |   NUMBER LESS_LESS_MINUS WORD
        |   REDIR_WORD  LESS_LESS_MINUS WORD
        |   LESS_LESS_LESS WORD
        |   NUMBER LESS_LESS_LESS WORD
        |   REDIR_WORD LESS_LESS_LESS WORD
        |   LESS_AND NUMBER
        |   NUMBER LESS_AND NUMBER
        |   REDIR_WORD LESS_AND NUMBER
        |   GREATER_AND NUMBER
        |   NUMBER GREATER_AND NUMBER
        |   REDIR_WORD GREATER_AND NUMBER
        |   LESS_AND WORD
        |   NUMBER LESS_AND WORD
        |   REDIR_WORD LESS_AND WORD
        |   GREATER_AND WORD
        |   NUMBER GREATER_AND WORD
        |   REDIR_WORD GREATER_AND WORD
        |   GREATER_AND DASH
        |   NUMBER GREATER_AND DASH
        |   REDIR_WORD GREATER_AND DASH
        |   LESS_AND DASH
        |   NUMBER LESS_AND DASH
        |   REDIR_WORD LESS_AND DASH
        |   AND_GREATER WORD
        |   AND_GREATER_GREATER WORD
        ;

在我的访问者中,当调用 visitRedirection 时,几乎不可能轻松地知道访问者当前处于哪个选项中。我可以使用 # 和标签来标记每个选项,但只需为单个产品添加 43 种访问方法规则似乎有些过分。

通常我会通过 ctx.GREATER() != null 进行一些 null 检查以了解是否选择了第一个备选方案,但在此示例中几乎总是有 2 个相互冲突的备选方案,例如:

GREATER WORD
NUMBER GREATER WORD

那么我应该使用ctx.NUMBER() != null && ctx.GREATER() != null 匹配第二个备选方案并使用ctx.NUMBER() == null && ctx.GREATER() != null 匹配第一个备选方案吗?

是否有更简单或更简洁的方法可以知道访问者当前所处的具体替代方案?

【问题讨论】:

  • 我这样做的方法是get the first child of the node,然后测试令牌类型。或者,您可以标记每个 alt。
  • 43 个标签有点多,我正在尝试找到一种更清洁的方法。我看到RuleContext::getChild 可以让我得到第一个孩子,但它返回ParseTree,我看不到如何从javadoc 中获取令牌类型。引起我注意的是getAltNumber;这似乎正是我想要的,但它说它不是默认设置的,所以我不确定如何启用它
  • 将第一个孩子投射到 TerminalNode,然后 getSymbol()、getType() 和测试。
  • 很好,确实有效,我想我只需要分支很多(由于这个生产规则有多少替代方案)但这确实比仅仅标记所有内容有所帮助,谢谢!

标签: java antlr4 visitor-pattern


【解决方案1】:

重组你的语法以减少替代方案。其中许多都有共同的前导或尾随部分,例如:

redirection
        :   GREATER WORD
        |   LESS WORD
        |   NUMBER (GREATER | LESS) WORD
        |   REDIR_WORD (GREATER | LESS | LESS_LESS_MINUS) WORD
        |   ...

这样你在每个 alt 中都有一个唯一的第一个标记,然后你可以将它分配给一个局部变量:

redirection
        :   op = GREATER WORD
        |   op = LESS WORD
        |   op = NUMBER subOp= (GREATER | LESS) WORD
        |   op = REDIR_WORD subOp =(GREATER | LESS | LESS_LESS_MINUS) WORD
        |   ...

这样,您可以轻松地检查您在听众/访问者中的哪个 alt:

public exitRedirection(RedirectionContext ctx) {
    switch (ctx.op.getType()) {
        case YourParser.GREATER_WORD: {
            break;
        }

        case YourParser.REDIR_WORD: {
            switch (ctx.supOp.getType()) {
                case YourParser.LESS_LESS_MINUS: {
                    break;
                }
            }
            break;
        }
    }

【讨论】:

  • 我只是从 bash 的 parser.y yacc 文件中复制粘贴了生产规则,但是您的方法非常好,谢谢,我会这样做!
  • 我设法将其缩小为 3 个备选方案,这是一种非常好的做事方式,这是新的生产规则:pastebin.com/Q8Wm8aBQ 再次感谢!
  • 这样也更快!请参阅“合并子规则”部分中的 blog post from Gabriele Tomassetti
  • 哦,所以基本上只要我有 single 标记作为括号中的替代项,它使用查找表(例如一组)来匹配正确的标记,如果我理解的话正确。我肯定会保存这篇文章以供参考,谢谢!
  • 在不改变语法的情况下,有没有实际的方法可以确定解析了哪个替代规则?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多