8

AST-> StringTemplate側で苦労しているようです。おそらく、パーサーを手動で作成しているためです->LLVM。

私が探しているのは、解析ルールを、それを表すことができ、ターゲット言語の出力を生成するメソッドを含むASTクラスに自動的に一致させる方法です。(この場合、おそらくStringTemplateを使用します。)

擬似コードでは、次の例の文法が与えられます。

numberExpression
    : DIGIT+
    ;

このASTクラスにマップしてもらいたい:

class NumberExpressionAST extends BaseAST {
    private double value;

    public NumberExpressionAST(node) {
        this.value = node.value;
    }

    public String generateCode() {
        // However we want to generate the output.
        // Maybe use a template, maybe string literals, maybe cure cancer...whatever.
    }
}

それらを組み合わせるために、おそらく以下のような接着剤があるでしょう:(またはあなたはClass.forNameものに夢中になる可能性があります)

switch (child.ruleName) {
    case 'numberExpression':
        return new NumberExpressionAST(child);
        break;
}

私はWebを精査していて、文法に解析書き換えルールを見つけました->が、このすべてのロジックを文法から除外する方法を理解できないようです。特に、テンプレートからターゲット出力を設定および生成するためのコード。何度も木を歩いても大丈夫です。

このオプションoutput=ASTを使用して、CommonTreeから拡張する独自のASTクラスを提供できるのではないかと思いました。確かに、ANTLRに対する私の理解は非常に原始的であるため、私の無知を許してください。私が従うすべてのチュートリアルは、私にとって完全に正気で維持するのが難しい文法に沿ってこれらすべてのことを行うことを示しています。

誰かが私に似たようなことを達成する方法を教えてもらえますか?

目標:AST /codegen/テンプレートロジックを文法から除外します。

編集 - - - - - - - - - - - - - - - - - - - - - - -

私はANTLRの実際のソースコードをトレースすることに頼りました(彼らはそれ自体を使用しているので)、そして、、などのような同様のものがすべてから継承されているのを見てBlockASTRuleASTますCommonTree。私は重要な部分を完全に理解していません...彼らがそれらをどのように使用しているか..

周りを見回してみると、基本的にヒントトークンを入力できることに気づきました。

identifier
    : IDENTIFIER<AnyJavaClassIWantAST>
    ;

解析ルールに対してまったく同じことを行うことはできません...ただし、解析ルール全体を表すトークンを作成する場合は、次のように書き換えルールを使用できます。

declaration
    : type identifier -> SOME_PARSE_RULE<AnyJavaClassIWantAST>
    ;

これはすべて私が望むものに近いですが、理想的には文法を散らかす必要はありません...これらを別の場所に置く方法はありますか?

4

1 に答える 1

7

これを答えとして追加していただけませんか...

これは、主に代替ラベルと生成されたリスナーである出力言語から文法を分離するのに大いに役立つ、いくつかのANTLR4の機能を使用する考案された例です。この例の文法は、コードのいくつかの些細なビットを表すことができますが、言語参照なしでそれを行います-skip()レクサーの空白の呼び出しさえありません。テストクラスは、生成されたリスナーを使用して、入力をJava出力に変換します。

最初の数回の試行で作業できなかったものは使用しないようにしたので、これを完全な例とは決して考えないでください。

Simplang.g

grammar Simplang;


compilationUnit : statements EOF;
statements      : statement+;
statement       : block #BlockStatement 
                | call  #CallStatement
                | decl  #DeclStatement
                ;
block           : LCUR statements RCUR;    
call            : methodName LPAR args=arglist? RPAR SEMI;
methodName      : ID;
arglist         : arg (COMMA arg)*;
arg             : expr;    
decl            : VAR variableName EQ expr SEMI;
variableName    : ID;
expr            : add_expr;     
    
add_expr        : lhs=primary_expr (add_op rhs=primary_expr)*;
add_op          : PLUS | MINUS;    
primary_expr    : string=STRING
                | id=ID
                | integer=INT
                ;    
    
VAR: 'var';   
ID: ('a'..'z'|'A'..'Z')+;
INT: ('0'..'9')+;
STRING: '\'' ~('\r'|'\n'|'\'')* '\'';
SEMI: ';';
LPAR: '(';
RPAR: ')';
LCUR: '{';
RCUR: '}';
PLUS: '+';
MINUS: '-';    
COMMA: ',';
EQ: '=';
WS: (' '|'\t'|'\f'|'\r'|'\n') -> skip;

ANTLR4は、レクサーとパーサーに加えて、リスナーインターフェイスとデフォルトの空の実装クラスを生成します。上記の文法用に生成されたインターフェースは次のとおりです。

SimplangListener.java

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);
}

これは、空のリスナーのいくつかのメソッドをオーバーライドし、パーサーを呼び出すテストクラスです。

SimplangTest.java

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", });

これはばかげた例ですが、カスタムASTを構築するときに役立つ可能性のある機能のいくつかを示しています。

于 2012-12-15T04:02:50.437 に答える