2017-10-09 3 views
2

私はコンパイルされた文法を持っており、それを使って入力シーケンスをXMLに変換したいと思っています。私のケースでは、非常に大きな文法を持っていますが、私のコードで各文法ルールを上書きしないようにしたいと思います。XMLを構文解析ツリーに変換する

混乱を避けるために例を使用します。私たちは、次の文法出力

解析ツリー

に対応
<prog> 
    <stat> 
     A = 
     <expr> 10 
     </expr> 
     \r\n 
    </stat> 
    <stat> 
     B = 
     <expr> 
      <expr>A</expr> 
      * 
      <expr> A</expr> 
     </expr> 
     \r\n 
    </stat> 
</prog> 

期待

grammar expr; 

prog: stat+ ; 

stat: expr NEWLINE 
| ID '=' expr NEWLINE 
| NEWLINE 
; 

expr: expr ('*'|'/') expr 
| INT 
| ID 
| '(' expr ')' 
; 

ID : [a-zA-Z]+ ; // match identifiers 
INT : [0-9]+ ; // match integers 
NEWLINE:'\r'? '\n' ; // return newlines to parser (is end-statement signal) 
WS : [ \t]+ -> skip ; // toss out whitespace 

入力シーケンス

A = 10 
B = A * A 

てみましょう

enter image description here

現在、私は私がParseTreeを作成するアプローチを使用して、私は次の文字列私はその後、上記のようなXMLに変換する

(prog (stat A = (expr 10) \r\n) (stat B = (expr (expr A) * (expr A)) \r\n)) 

(私が働いてシンプルな汎用コードを使用を生成toStringTree方法を使用してどんな文法でも)。私はこのアプローチがダミーだと思う。 toStringTreeなしでそれを解決することは可能ですか?私は訪問者の各文法​​規則を無効にする必要性を避けたいと思います。 (私は何百もある)。

EDIT

私は基本的にXML形式に汎用的な解析ツリーのシリアル化のいくつかの種類が必要です。主な目標は、ルールごとにJavaで特別なシリアライズメソッドを書く必要がないことです。

+0

静的メソッド 'printXml(ParseTree tree)' 'を使用して、ParseTreeを再帰的に下ろして印刷することはどうですか? –

+0

@StefanHaustein私はそれを確認します、ありがとう。私はAntlrに多くの経験がありません。 –

+0

XMLを作成するのは良い考えではありません。それをどうやって操作しますか? XSLTは大きな木ではあまり効果がありません。そして、あなたはJavaのプログラムを適度に小さくするために巨大な木を得るでしょう。さらに悪いことに、XSLTは文脈依存型のチェックには向いていません。すべてのJava構文はコンテキスト感応型です(変数は宣言によって変数が異なります)。とにかくAST以上のものを望む理由は、解析後の人生を参照してください。http://www.semdesigns.com/Products/DMS/LifeAfterParsing.html –

答えて

1

おそらく、このアプローチはあなたのニーズに合っているかもしれません。私は読みやすくするために余分なタグtで終端記号をラップし、空白文字をスキップしました。それでも、必要に応じて出力を調整するのは大きな問題ではありません。

final exprLexer lexer = new exprLexer(CharStreams.fromString("A=10\nB = A * A\n")); 
final CommonTokenStream tokens = new CommonTokenStream(lexer); 
final exprParser parser = new exprParser(tokens); 
final ParseTree tree = parser.prog(); 
ParseTreeWalker.DEFAULT.walk(new exprBaseListener() 
{ 
    final String INDENT = " "; 
    int level = 0; 
    @Override 
    public void enterEveryRule(final ParserRuleContext ctx) 
    { 
     System.out.printf("%s<%s>%n", indent(), parser.getRuleNames()[ctx.getRuleIndex()]); 
     ++level; 
     super.enterEveryRule(ctx); 
    } 

    @Override 
    public void exitEveryRule(final ParserRuleContext ctx) 
    { 
     --level; 
     System.out.printf("%s</%s>%n", indent(), parser.getRuleNames()[ctx.getRuleIndex()]); 
     super.exitEveryRule(ctx); 
    } 

    @Override 
    public void visitTerminal(final TerminalNode node) 
    { 
     final String value = node.getText(); 
     if (!value.matches("\\s+")) 
     { 
      System.out.printf("%s<t>%s</t>%n", indent(), node.getText()); 
     } 
     super.visitTerminal(node); 
    } 

    private String indent() 
    { 
     return String.join("", Collections.nCopies(level, INDENT)); 
    } 
}, tree); 
+0

優れている、私はより良い解決策があることを新しくしました。どうもありがとうございました。 –

3

あなたはANTLR4のビジター機能を利用できます。使用しているツールによっては、クラスの生成中に-visitor command line parameterを追加する必要があります。これがうまく機能するために

、私はいくつかのlabels to your parser rules追加:

String source = "A = 10\nB = A * A\n"; 
exprLexer lexer = new exprLexer(CharStreams.fromString(source)); 
exprParser parser = new exprParser(new CommonTokenStream(lexer)); 
ParseTree tree = parser.prog(); 
String xml = new XmlVisitor().visit(tree); 
System.out.println(xml); 
:次のコードを実行し、この訪問者をテストするには

public class XmlVisitor extends exprBaseVisitor<String> { 

    @Override 
    public String visitProg(exprParser.ProgContext ctx) { 
    StringBuilder builder = new StringBuilder("<prog>"); 
    for (exprParser.StatContext stat : ctx.stat()) { 
     builder.append(super.visit(stat)); 
    } 
    return builder.append("</prog>").toString(); 
    } 

    @Override 
    public String visitExprStat(exprParser.ExprStatContext ctx) { 
    return "expr"; 
    } 

    @Override 
    public String visitAssignStat(exprParser.AssignStatContext ctx) { 
    return "<stat>" + ctx.ID() + " = " + super.visit(ctx.expr()) + "\\r\\n</stat>"; 
    } 

    @Override 
    public String visitEmptyStat(exprParser.EmptyStatContext ctx) { 
    return "\\r\\n"; 
    } 

    @Override 
    public String visitMultExpr(exprParser.MultExprContext ctx) { 
    return "<expr>" + super.visit(ctx.lhs) + ctx.op.getText() + super.visit(ctx.rhs) + "</expr>"; 
    } 

    @Override 
    public String visitIntExpr(exprParser.IntExprContext ctx) { 
    return "<expr>" + ctx.INT().getText() + "</expr>"; 
    } 

    @Override 
    public String visitIdExpr(exprParser.IdExprContext ctx) { 
    return "<expr>" + ctx.ID().getText() + "</expr>"; 
    } 

    @Override 
    public String visitNestedExpr(exprParser.NestedExprContext ctx) { 
    return "<expr>" + super.visit(ctx.expr()) + "</expr>"; 
    } 
} 

:あなたの訪問者が次のようになります

prog 
: stat+ EOF 
; 

stat 
: expr NEWLINE  #exprStat 
| ID '=' expr NEWLINE #assignStat 
| NEWLINE    #emptyStat 
; 

expr 
: lhs=expr op=('*'|'/') rhs=expr #multExpr 
| INT       #intExpr 
| ID        #idExpr 
| '(' expr ')'     #nestedExpr 
; 

印刷する:

<prog><stat>A = <expr>10</expr>\r\n</stat><stat>B = <expr><expr>A</expr>*<expr>A</expr></expr>\r\n</stat></prog> 
+0

ありがとうございました。しかし、私の最後の文に注意してください。 "私はビジターの文法規則をオーバーライドする必要がないようにしたいと思います。また、文法に何か変わった場合は、私の現在の解決策では不要なものを適切に変更する必要があります。 –

+0

文法のルールが変わった場合は、常に別のものを変更する必要があります。ビジターや静的な方法で書いてください。私はこれがあなたの問題を解決する "最善の"(最もクリーンな)方法だと思って、それを投稿したのです。私はそれが誰かに恩恵を受けると確信しています。もちろん、あなたも大歓迎です! :) –

+0

あなたのツリーを検査しノードを放出する静的な方法は(おそらく)スパゲッティコードの大きな山になるでしょう。ビジターでは、各ノードは独自の方法で放出され、きれいできれいに保たれます。また、単体テストにも非常に簡単です! –

0

ParseTree.toStringTreeメソッドで生成されたLISPスタイルのツリーを読み取るANTLR文法を作成しました。プロジェクトはhereにアクセス可能です。これは、次の部品

文法

grammar str; 

expr: 
STRING expr        # exprString 
| LR_BRACKET expr RR_BRACKET expr   # exprParenthesis 
| LR_STRING_BRACKET expr RR_BRACKET expr # exprRule 
| <EOF>         # exprEnd 
| EOF_MARK         # exprEOF 
|           # exprEpsilon 
; 

EOF_MARK:   '<EOF>' ; 
LR_STRING_BRACKET: '(' ~[()]+; 
LR_BRACKET:   '('; 
RR_BRACKET:   ')'; 
STRING:    ~[()]+; 
SPACE:    [ \t\r\n]+ -> skip; // toss out whitespace 

strXMLVisitorを持っています。Javaの

public class strXMLVisitor extends strBaseVisitor<String> { 

    @Override 
    public String visitExprString(strParser.ExprStringContext ctx) 
    { 
    return ctx.STRING().getText() + super.visit(ctx.expr()); 
    } 

    @Override 
    public String visitExprParenthesis(strParser.ExprParenthesisContext ctx) { 
    return "(" + super.visit(ctx.expr(0)) + ")" + super.visit(ctx.expr(1)); 
    } 

    @Override 
    public String visitExprRule(strParser.ExprRuleContext ctx) { 
    String value = ctx.LR_STRING_BRACKET().getText().substring(1); 
    return "<" + value + ">" + super.visit(ctx.expr(0)) + "</" + value + ">" + super.visit(ctx.expr(1)); 
    } 

    @Override 
    public String visitExprEnd(strParser.ExprEndContext ctx) { 
    return ""; 
    } 

    @Override 
    public String visitExprEOF(strParser.ExprEOFContext ctx) { 
    return ""; 
    } 

    @Override 
    public String visitExprEpsilon(strParser.ExprEpsilonContext ctx) { 
    return ""; 
    } 
} 

main.java

import org.antlr.v4.runtime.*; 
import org.antlr.v4.runtime.tree.*; 

public class main { 
    public static void main(String[] args) throws Exception { 
     // create a CharStream that reads from standard input 
     ANTLRInputStream input = new ANTLRInputStream(System.in); 
     // create a lexer that feeds off of input CharStream 
     strLexer lexer = new strLexer(input); 
     // create a buffer of tokens pulled from the lexer 
     CommonTokenStream tokens = new CommonTokenStream(lexer); 
     // create a parser that feeds off the tokens buffer 
     strParser parser = new strParser(tokens); 
     ParseTree tree = parser.expr(); // begin parsing at init rule 

     String xml = "<?xml version=\"1.0\"?>" + new strXMLVisitor().visit(tree); 
     System.out.println(xml);  
    } 
} 

あなたはantlr4レディ(と同様CLASSPATH参照)あなたがそれを実行するには、次のコマンドを使用することができたら:

antlr4 -visitor str.g4 
javac *.java 
java main < file 

ファイルにはLISPツリー形式の入力が含まれていなければなりません。結果は標準出力のXMLです。