将ANTLR解析规则映射到自定义JavaAST类以生成代码

将ANTLR解析规则映射到自定义JavaAST类以生成代码,java,parsing,antlr,llvm,abstract-syntax-tree,Java,Parsing,Antlr,Llvm,Abstract Syntax Tree,我似乎在努力解决AST->StringTemplate方面的问题,可能是因为我是从手工编写解析器->LLVM开始的 我要寻找的是一种将解析规则自动匹配到AST类的方法,该AST类可以表示解析规则,并包含生成目标语言输出的方法。(在本例中,可能使用StringTemplate。) 在伪代码中,给出以下示例语法: numberExpression : DIGIT+ ; 我想将其映射到此AST类: 类号pressionast扩展了BaseAST{ 私人双重价值; 公号pression

我似乎在努力解决AST->StringTemplate方面的问题,可能是因为我是从手工编写解析器->LLVM开始的

我要寻找的是一种将解析规则自动匹配到AST类的方法,该AST类可以表示解析规则,并包含生成目标语言输出的方法。(在本例中,可能使用StringTemplate。)

在伪代码中,给出以下示例语法:

numberExpression
    : DIGIT+
    ;
我想将其映射到此AST类:

类号pressionast扩展了BaseAST{
私人双重价值;
公号pressionast(节点){
this.value=node.value;
}
公共字符串generateCode(){
//但是,我们希望生成输出。
//也许使用模板,也许字符串文字,也许治愈癌症…随便什么。
}
}
为了配合它们,可能会有如下胶水:(或者你可能会对
Class.forName
之类的东西发疯)

我一直在浏览网页,在语法中找到了解析重写规则,使用
->
,但我似乎不知道如何将所有这些逻辑排除在语法之外。特别是从模板设置和生成目标输出的代码。我可以在树上走几次

我想也许我可以使用选项
output=AST
,然后提供我自己的从CommonTree扩展的AST类?我承认,我对ANTLR的理解非常原始,所以请原谅我的无知。我跟随的每一个教程都展示了如何按照语法来做这些事情,这对我来说是完全疯狂的,而且很难维护

有人能给我指出一种完成类似事情的方法吗


目标:将AST/codegen/template逻辑排除在语法之外

编辑---------------------------------------------

我已经求助于跟踪ANTLR的实际源代码(因为它们自己使用),我看到类似的东西,如
BlockAST
RuleAST
,等等,都是从
CommonTree
继承的。我还没有弄清楚重要的部分…他们是如何使用它们的

环顾四周,我注意到您基本上可以键入提示标记:

identifier
    : IDENTIFIER<AnyJavaClassIWantAST>
    ;
标识符
:标识符
;
您不能对解析规则执行完全相同的操作…但是如果您创建一些令牌来表示整个解析规则,您可以使用如下的重写规则:

declaration
    : type identifier -> SOME_PARSE_RULE<AnyJavaClassIWantAST>
    ;
声明
:type identifier->SOME\u PARSE\u规则
;
所有这些都接近我想要的,但理想情况下,我不应该乱扔语法…有没有办法把这些放在其他地方

你能加上这个作为回答吗

下面是一个精心设计的示例,它使用了ANTLR4的一些特性,这些特性在将语法与输出语言(主要是输出语言和生成的侦听器)分离方面有很大的帮助。这个示例语法可以表示一些琐碎的代码,但它不需要语言引用,甚至不需要调用
skip()
来查找lexer中的空白。测试类使用生成的侦听器将输入转换为一些Java输出

我避免在第一次尝试中使用任何我不能得到的东西,所以无论如何不要认为这是一个详尽的例子。

辛普朗 与lexer和解析器一起,ANTLR4生成一个监听器接口和默认的空实现类。下面是为上述语法生成的接口

SimpleListener.java 下面是一个测试类,它重写空侦听器中的一些方法并调用解析器

SimplangTest.java 以下是测试类中硬编码的测试输入:

var x = 4;
foo(x, 10);
bar(y + 10 - 1, 'x' + 'y' + 'z');
以下是生成的输出:

define("x", 4);
call("foo", new Object[]{read("x"), 10, });
call("bar", new Object[]{read("y") + 10 - 1, "x" + "y" + "z", });

这是一个愚蠢的例子,但它展示了一些在构建自定义AST时可能对您有用的功能。

“目标:将AST/codegen/template逻辑排除在语法之外…[I]我真的不应该浪费语法…”听起来您想要ANTLR的所有好处,而不是ANTLR的任何好处我认为你唯一真正的选择是编写你自己的语法分析器,按照你的方式来做,或者咬紧牙关,按照设计使用ANTLR:使用生成的代码,在语法中指定AST类型,等等。我明白你的意思,尽管我的“目标”可能有点过于直白。ANTLR当然不仅仅是解析的语法语法,所以我当然想利用它的其他特性,但是从实际语法规则本身进行某种程度的抽象会更好。我认为我的
标识符:标识符功能非常适合我。如果您愿意切换到ANTLR 4,您可以使用its更接近您的目标,它会变成由生成的代码触发的侦听器事件。在这一点上,我对它们的了解还不够,无法给出一个完整的答案,但它确实看起来像是一个不错的语言中立抽象层。@tenterhook非常酷。你能加上这个作为回答吗?我很乐意接受?谢谢大家!@巴特基,你太好了D
public interface SimplangListener extends ParseTreeListener {
    void enterArglist(SimplangParser.ArglistContext ctx);
    void exitArglist(SimplangParser.ArglistContext ctx);
    void enterCall(SimplangParser.CallContext ctx);
    void exitCall(SimplangParser.CallContext ctx);
    void enterCompilationUnit(SimplangParser.CompilationUnitContext ctx);
    void exitCompilationUnit(SimplangParser.CompilationUnitContext ctx);
    void enterVariableName(SimplangParser.VariableNameContext ctx);
    void exitVariableName(SimplangParser.VariableNameContext ctx);
    void enterBlock(SimplangParser.BlockContext ctx);
    void exitBlock(SimplangParser.BlockContext ctx);
    void enterExpr(SimplangParser.ExprContext ctx);
    void exitExpr(SimplangParser.ExprContext ctx);
    void enterPrimary_expr(SimplangParser.Primary_exprContext ctx);
    void exitPrimary_expr(SimplangParser.Primary_exprContext ctx);
    void enterAdd_expr(SimplangParser.Add_exprContext ctx);
    void exitAdd_expr(SimplangParser.Add_exprContext ctx);
    void enterArg(SimplangParser.ArgContext ctx);
    void exitArg(SimplangParser.ArgContext ctx);
    void enterAdd_op(SimplangParser.Add_opContext ctx);
    void exitAdd_op(SimplangParser.Add_opContext ctx);
    void enterStatements(SimplangParser.StatementsContext ctx);
    void exitStatements(SimplangParser.StatementsContext ctx);
    void enterBlockStatement(SimplangParser.BlockStatementContext ctx);
    void exitBlockStatement(SimplangParser.BlockStatementContext ctx);
    void enterCallStatement(SimplangParser.CallStatementContext ctx);
    void exitCallStatement(SimplangParser.CallStatementContext ctx);
    void enterMethodName(SimplangParser.MethodNameContext ctx);
    void exitMethodName(SimplangParser.MethodNameContext ctx);
    void enterDeclStatement(SimplangParser.DeclStatementContext ctx);
    void exitDeclStatement(SimplangParser.DeclStatementContext ctx);
    void enterDecl(SimplangParser.DeclContext ctx);
    void exitDecl(SimplangParser.DeclContext ctx);
}
public class SimplangTest {

    public static void main(String[] args) {

        ANTLRInputStream input = new ANTLRInputStream(
                "var x = 4;\nfoo(x, 10);\nbar(y + 10 - 1, 'x' + 'y' + 'z');");

        SimplangLexer lexer = new SimplangLexer(input);

        SimplangParser parser = new SimplangParser(new CommonTokenStream(lexer));

        parser.addParseListener(new SimplangBaseListener() {
            public void exitArg(SimplangParser.ArgContext ctx) {
                System.out.print(", ");
            }

            public void exitCall(SimplangParser.CallContext call) {
                System.out.print("})");
            }

            public void exitMethodName(SimplangParser.MethodNameContext ctx) {
                System.out.printf("call(\"%s\", new Object[]{", ctx.ID()
                        .getText());
            }

            public void exitCallStatement(SimplangParser.CallStatementContext ctx) {
                System.out.println(";");
            }

            public void enterDecl(SimplangParser.DeclContext ctx) {
                System.out.print("define(");
            }

            public void exitVariableName(SimplangParser.VariableNameContext ctx) {
                System.out.printf("\"%s\", ", ctx.ID().getText());
            }

            public void exitDeclStatement(SimplangParser.DeclStatementContext ctx) {
                System.out.println(");");
            }

            public void exitAdd_op(SimplangParser.Add_opContext ctx) {
                if (ctx.MINUS() != null) {
                    System.out.print(" - ");
                } else {
                    System.out.print(" + ");
                }
            }

            public void exitPrimary_expr(SimplangParser.Primary_exprContext ctx) {
                if (ctx.string != null) {
                    String value = ctx.string.getText();
                    System.out.printf("\"%s\"",
                            value.subSequence(1, value.length() - 1));
                } else if (ctx.altNum == 2){    //cheating and using the alt# for "INT"
                    System.out.printf("read(\"%s\")", ctx.id.getText());
                } else {
                    System.out.print(ctx.INT().getText());
                }
            }
        });

        parser.compilationUnit();
    }
}
var x = 4;
foo(x, 10);
bar(y + 10 - 1, 'x' + 'y' + 'z');
define("x", 4);
call("foo", new Object[]{read("x"), 10, });
call("bar", new Object[]{read("y") + 10 - 1, "x" + "y" + "z", });