2013-10-13 48 views
8

我正在嘗試編寫一段代碼,它將採用ANTLR4解析器並使用它來生成類似於grun上的-tree選項給出的輸入的AST(misc.TestRig)。但是,我還希望輸出包含所有行號/偏移量信息。如何使用ANTLR4打印製作和行號?

例如,而不是打印

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

我想獲得

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

或類似的東西。

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; 

得到treeruleNamesprogram是我的語法中頂尖生產的名稱。

+0

有2' toStringTree'方法。一個採用「Parser」實例,另一個採用規則名稱的「列表」。 –

+0

@ 280Z28:你陳述了一個真實的事實。使用解析器參數調用'toStringTree'會導致實現獲取規則列表('recog.getRuleNames()')並將其傳遞給'toStringTree',該列表需要一個'List'。無論如何,這仍然不能解釋如何在寫訪問者時獲得行號/抵消信息。 –

回答

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

類可以用作如下:

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

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

類可以通過更新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

這很好。我正在更新這個問題以填補少數空白。 –