【问题标题】:How can I access alternate labels in ANTLR4 while generically traversing a parse tree?如何在一般遍历解析树时访问 ANTLR4 中的备用标签?
【发布时间】:2013-10-15 03:45:19
【问题描述】:

如何在一般遍历解析树时访问 ANTLR4 中的备用标签?或者,是否有任何方法可以复制 ANTLR3 的 ^ 运算符的功能,因为这样可以解决问题。

我正在尝试为任何 ANTLR4 语法编写一个 AST 漂亮的打印机,遵循一种简单的方法(例如使用替代标签命名产品)。我希望能够将3 + 5 之类的术语漂亮地打印为(int_expression (plus (int_literal 3) (int_literal 5))),或者类似的东西,给定如下语法:

int_expression 
    : int_expression '+' int_expression # plus
    | int_expression '-' int_expression # minus
    | raw_int                           # int_literal
    ;
raw_int
    : Int
    ;
Int : [0-9]+ ;

我无法有效地为 plusminus 产品命名,因为将它们拉出到自己的产品中会导致工具抱怨规则是相互左递归的。拉不出来,怎么给这些作品起名字?

注 1:我能够通过将“好”终端(例如,上面的 Int)放入特殊产生式(产生式以特殊前缀开头,如 raw_)在方法上摆脱 + 参数.然后我可以只打印其父产品名为“raw_...”的终端,而忽略所有其他终端。这非常适合摆脱+,同时在输出中保留35。这可以通过 ANTLR3 中的! 来完成。

注 2:我知道我可以为给定语言的每种产品编写专门的漂亮打印机或使用操作,但我想使用 ANTLR4 来解析和生成各种语言的 AST,看起来像我应该能够通用地编写这样一个简单漂亮的打印机。换句话说,我只关心获得 AST,我宁愿不必为了获得 AST 而使用定制的漂亮打印机来阻碍每个语法。也许我应该回到 ANTLR3?

【问题讨论】:

    标签: pretty-print antlr4 abstract-syntax-tree


    【解决方案1】:

    我建议将漂亮的打印机实现为具有嵌套访问者类的侦听器实现,以获取各种上下文对象的名称。

    private MyParser parser; // you'll have to assign this field
    private StringBuilder builder = new StringBuilder();
    
    @Override
    public void enterEveryRule(@NotNull ParserRuleContext ctx) {
        if (!builder.isEmpty()) {
            builder.append(' ');
        }
    
        builder.append('(');
    }
    
    @Override
    public void visitTerminalNode(@NotNull TerminalNode node) {
        // TODO: print node text to builder
    }
    
    @Override
    public void visitErrorNode(@NotNull TerminalNode node) {
        // TODO: print node text to builder
    }
    
    @Override
    public void exitEveryRule(@NotNull ParserRuleContext ctx) {
        builder.append(')');
    }
    
    protected String getContextName(@NotNull ParserRuleContext ctx) {
        return new ContextNameVisitor().visit(ctx);
    }
    
    protected class ContextNameVisitor extends MyParserBaseVisitor<String> {
        @Override
        public String visitChildren() {
            return parser.getRuleNames()[ctx.getRuleIndex()];
        }
    
        @Override
        public String visitPlus(@NotNull PlusContext ctx) {
            return "plus";
        }
    
        @Override
        public String visitMinus(@NotNull MinusContext ctx) {
            return "minus";
        }
    
        @Override
        public String visitInt_literal(@NotNull MinusContext ctx) {
            return "int_literal";
        }
    }
    

    【讨论】:

    • 我正在寻找一种通用的方法来做到这一点,而无需为每种语言编写专门的漂亮打印机。有没有办法做到这一点?从用户的角度来看,我不明白为什么没有,因为备用标签就在那里。
    【解决方案2】:

    API 不包含访问备用标签的方法。

    但是有一个解决方法。 ANTLR4 使用备用标签生成 java 类名,这些 java 类可以在运行时访问。

    例如:要访问 ANTLR4 中的备用标签,同时一般遍历解析树(使用侦听器),您可以使用以下函数:

    // Return the embedded alternate label between
    // "$" and "Context" from the class name
    String getCtxName(ParserRuleContext ctx) {
        String str = ctx.getClass().getName();
        str = str.substring(str.indexOf("$")+1,str.lastIndexOf("Context"));
        str = str.toLowerCase();
        return str;
    }
    

    使用示例:

    @Override
    public void exitEveryRule(ParserRuleContext ctx) {
        System.out.println(getCtxName(ctx));
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-06-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-12-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多