2013-10-13 10 views
8

Sto provando a scrivere un codice che utilizzi un parser ANTLR4 e lo utilizzi per generare AST per input simili a quelli forniti dall'opzione -tree su grun (misc.TestRig). Tuttavia, mi piacerebbe anche che l'output includesse tutte le informazioni sul numero di riga/offset.In che modo posso stampare prodotti e numeri di riga, usando ANTLR4?

Per esempio, invece di stampare

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

Mi piacerebbe ottenere

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

o qualcosa di simile.

Non c'è ancora un numero enorme di esempi di visitatori per ANTLR4, ma sono abbastanza sicuro di poter fare la maggior parte di ciò copiando l'implementazione predefinita per toStringTree (usata da grun). Tuttavia, non vedo alcuna informazione sui numeri di linea o sugli offset.

mi aspettavo di essere in grado di scrivere il codice super semplice come questo:

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

ma non sembra essere questo semplice. Immagino che dovrei essere in grado di ottenere informazioni sul numero di riga dal parser, ma non ho capito come farlo. Come posso afferrare questo numero di linea/le informazioni di offset nella mia traversa?


per riempire gli spazi vuoti pochi nella soluzione qui di seguito, ho usato:

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

per ottenere il tree e ruleNames. program è il nome per la migliore produzione nella mia grammatica.

+0

Ci sono 2 ' metodi toStringTree'. Si prende un'istanza di 'Parser', ma l'altra prende semplicemente un' Elenco 'di nomi di regole. –

+0

@ 280Z28: dichiari un fatto reale. Chiamando 'toStringTree' con un argomento parser, l'implementazione prende l'elenco delle regole (' recog.getRuleNames() ') e lo passa al' toStringTree' che accetta un 'List'. In ogni caso, questo non spiega ancora come ottenere il numero di linea/le informazioni di offset durante la scrittura di un visitatore. –

risposta

11

Il metodo Trees.toStringTree può essere implementato utilizzando ParseTreeListener. Il seguente listener produce esattamente la stessa uscita di 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(); 
    } 
} 

La classe può essere utilizzato come segue:

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

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

La classe può essere modificato per produrre le informazioni nell'output aggiornando il metodo 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

Questo ha funzionato alla grande. Sto aggiornando la domanda per riempire i pochi spazi vuoti. –

Problemi correlati