2013-10-13 8 views
8

Ich versuche, ein Stück Code zu schreiben, der einen ANTLR4-Parser verwendet und ihn verwendet, um ASTs für Eingaben zu erzeugen, die denen der Option -tree auf grun (misc.TestRig) ähneln. Ich möchte aber zusätzlich, dass die Ausgabe alle Zeilennummern/Offset-Informationen enthält.Wie drucke ich Produktionen und Zeilennummern mit ANTLR4?

Zum Beispiel, anstatt zu drucken

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

Ich möchte

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

Oder etwas Ähnliches bekommen.

Es gibt noch keine große Anzahl von Besucher-Beispielen für ANTLR4, aber ich bin ziemlich sicher, dass ich das meiste durch Kopieren der Standardimplementierung für toStringTree (von grun verwendet) machen kann. Ich sehe jedoch keine Informationen über die Zeilennummern oder Offsets.

ich voraussichtlich in der Lage sein super einfach Code wie folgt zu schreiben:

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

aber es scheint nicht so einfach zu sein. Ich schätze, ich sollte Zeilennummerninformationen vom Parser bekommen können, aber ich habe nicht herausgefunden, wie das geht. Wie kann ich diese Zeilennummer/Offset-Information in meinem Traversal erfassen?


in den wenigen Rohlinge in der Lösung unter zu füllen, habe ich:

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

die tree und die ruleNames zu bekommen. program ist der Name für die Top-Produktion in meiner Grammatik.

+0

Es gibt 2 ' toStringTree' Methoden. Man nimmt eine "Parser" -Instanz, aber die andere nimmt nur eine "Liste " von Regelnamen. –

+0

@ 280Z28: Sie geben eine wahre Tatsache an. Der Aufruf von 'toStringTree' mit einem Parser-Argument bewirkt, dass die Implementierung die Liste der Regeln (' recog.getRuleNames() ') ergreift und an den' toStringTree' übergibt, der eine 'List' übernimmt. Auf jeden Fall erklärt dies noch nicht, wie man beim Schreiben eines Besuchers auf die Zeilennummer/Offset-Information kommt. –

Antwort

11

Die Trees.toStringTree Methode kann mit einem ParseTreeListener implementiert werden. Der folgende Listener erzeugt genau die gleiche Ausgabe wie Trees.toStringTree.

List<String> ruleNames = ...; 
ParseTree tree = ...; 

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

Die Klasse durch die Aktualisierung der exitEveryRule Methode, um die Informationen in Ihrer Ausgabe zu erzeugen, kann geändert werden:

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

Die Klasse kann wie folgt verwendet werden

@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

Das hat super funktioniert. Ich aktualisiere die Frage, um die wenigen Lücken zu füllen. –