2013-10-13 17 views
8

Próbuję napisać fragment kodu, który zajmie parser ANTLR4 i użyje go do wygenerowania AST dla wejść podobnych do tych podanych w opcji -tree na grun (misc.TestRig). Jednak chciałbym dodatkowo, aby dane wyjściowe zawierały wszystkie informacje o liczbie linii/przesunięciu.Jak ładować wydruki i numery linii za pomocą ANTLR4?

Na przykład, zamiast drukować

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

Chciałbym dostać

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

lub coś podobnego.

Jeszcze nie ma ogromnej liczby przykładów odwiedzin dla ANTLR4, ale jestem prawie pewien, że mogę zrobić większość tego poprzez skopiowanie domyślnej implementacji dla toStringTree (używanej przez grun). Jednak nie widzę żadnych informacji na temat numerów linii lub przesunięć.

Spodziewałem się, aby móc napisać bardzo prosty kod tak:

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

ale nie wydaje się to proste. Zgaduję, że powinienem być w stanie uzyskać informacje o numerze linii z analizatora składni, ale nie mam pojęcia, jak to zrobić. W jaki sposób mogę pobrać informacje o numerze linii/przesunięciu w trakcie mojej traversal?


wypełnienie kilku wykrojów w poniższej rozwiązania użyłem:

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

uzyskać tree i ruleNames. program to nazwa najwyższej produkcji w mojej gramatyce.

+0

Istnieją 2 ' metody toStringTree'. Jeden pobiera instancję typu "Parser", ale drugi po prostu bierze "Listę " nazw reguł. –

+0

@ 280Z28: Podajesz prawdziwy fakt. Wywołanie 'toStringTree' za pomocą argumentu parser powoduje, że implementacja pobiera listę reguł (' recog.getRuleNames() ') i przekazuje ją do' toStringTree', który pobiera 'List'. W każdym razie to nadal nie wyjaśnia, jak uzyskać informacje o numerze linii/przesunięciu podczas pisania odwiedzającego. –

Odpowiedz

11

Metoda Trees.toStringTree może być zaimplementowana przy użyciu ParseTreeListener. Poniższy detektor generuje dokładnie to samo wyjście, co 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(); 
    } 
} 

Klasa może być stosowany w sposób następujący:

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

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

Klasa może być modyfikowany w celu uzyskania informacji w swoim wyjściu aktualizując metodę exitEveryRule:

@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

To działało świetnie. Aktualizuję pytanie, aby wypełnić kilka pustych miejsc. –

Powiązane problemy