2013-10-13 6 views
8

私は、ANTLR4パーサをとり、grun(misc.TestRig)の-treeオプションで指定されたものと同様の入力用にASTを生成するコードを記述しようとしています。しかし、私はさらに、すべての行番号/オフセット情報を出力に含めることをお勧めします。例えばANTLR4を使用して、プロダクションと行番号をどのように印字するのですか?

、代わりに私が似て

(add (int 5 [line 3, offset 6:7]) '+' (int 6 [line 3, offset 8:9]) [line 3, offset 5:10]) 

か何かを取得したいのですが

(add (int 5) '+' (int 6)) 

を印刷します。

ANTLR4の訪問者の例はまだまだたくさんありませんが、ほとんどの場合、デフォルトの実装をtoStringTree(grunが使用)にコピーすることでこれを行うことができます。ただし、行番号やオフセットに関する情報は表示されません。

私はこのような超簡単なコード書くことができると期待:

String visit(ParseTree t) { 
    return "(" + t.productionName + t.visitChildren() + t.lineNumber + ")"; 
} 

を、それはこの単純ではないようです。私はパーサから行番号情報を得ることができるはずだと思っていますが、どうやってそれを行うのか分かりません。私の横断でこの行番号/オフセット情報を取得するにはどうすればよいですか?以下の溶液中で、いくつかの空白を埋めるために


は、私が使用:

List<String> ruleNames = Arrays.asList(parser.getRuleNames()); 
parser.setBuildParseTree(true); 
ParserRuleContext prc = parser.program(); 
ParseTree tree = prc; 

treeruleNamesを取得します。 programは私の文法の中でトッププロダクションの名前です。

+0

List<String> ruleNames = ...; ParseTree tree = ...; TreePrinterListener listener = new TreePrinterListener(ruleNames); ParseTreeWalker.DEFAULT.walk(listener, tree); String formatted = listener.toString(); 

クラスがexitEveryRuleメソッドを更新することによって、あなたの出力に情報を生成するように変更することができますtoStringTree'メソッドを呼び出します。 1つは 'Parser'インスタンスを取りますが、もう1つはルール名の' List 'を取るだけです。 –

+0

@ 280Z28:あなたは本当の事実を述べています。パーサ引数で 'toStringTree'を呼び出すと、実装はルールのリスト(' recog.getRuleNames() ')を取得し、それを' List'をとる 'toStringTree'に渡します。とにかく、これはまだ訪問者を書く間に行番号/オフセット情報を取得する方法を説明していません。 –

答えて

11

Trees.toStringTreeの方法は、ParseTreeListenerを使用して実装できます。次のリスナーは、Trees.toStringTreeとまったく同じ出力を生成します。次のように

public class TreePrinterListener implements ParseTreeListener { 
    private final List<String> ruleNames; 
    private final StringBuilder builder = new StringBuilder(); 

    public TreePrinterListener(Parser parser) { 
     this.ruleNames = Arrays.asList(parser.getRuleNames()); 
    } 

    public TreePrinterListener(List<String> ruleNames) { 
     this.ruleNames = ruleNames; 
    } 

    @Override 
    public void visitTerminal(TerminalNode node) { 
     if (builder.length() > 0) { 
      builder.append(' '); 
     } 

     builder.append(Utils.escapeWhitespace(Trees.getNodeText(node, ruleNames), false)); 
    } 

    @Override 
    public void visitErrorNode(ErrorNode node) { 
     if (builder.length() > 0) { 
      builder.append(' '); 
     } 

     builder.append(Utils.escapeWhitespace(Trees.getNodeText(node, ruleNames), false)); 
    } 

    @Override 
    public void enterEveryRule(ParserRuleContext ctx) { 
     if (builder.length() > 0) { 
      builder.append(' '); 
     } 

     if (ctx.getChildCount() > 0) { 
      builder.append('('); 
     } 

     int ruleIndex = ctx.getRuleIndex(); 
     String ruleName; 
     if (ruleIndex >= 0 && ruleIndex < ruleNames.size()) { 
      ruleName = ruleNames.get(ruleIndex); 
     } 
     else { 
      ruleName = Integer.toString(ruleIndex); 
     } 

     builder.append(ruleName); 
    } 

    @Override 
    public void exitEveryRule(ParserRuleContext ctx) { 
     if (ctx.getChildCount() > 0) { 
      builder.append(')'); 
     } 
    } 

    @Override 
    public String toString() { 
     return builder.toString(); 
    } 
} 

クラスを使用することができます:2 `があります

@Override 
public void exitEveryRule(ParserRuleContext ctx) { 
    if (ctx.getChildCount() > 0) { 
     Token positionToken = ctx.getStart(); 
     if (positionToken != null) { 
      builder.append(" [line "); 
      builder.append(positionToken.getLine()); 
      builder.append(", offset "); 
      builder.append(positionToken.getStartIndex()); 
      builder.append(':'); 
      builder.append(positionToken.getStopIndex()); 
      builder.append("])"); 
     } 
     else { 
      builder.append(')'); 
     } 
    } 
} 
+0

これはうまくいった。いくつかの空白を埋めるために質問を更新しています。 –

関連する問題