我想对此进行自己的尝试,利用我已经在我的项目中使用StringTemplate 的事实。这意味着我不必像其他答案那样手动处理级别。它还使输出格式更易于自定义。
最重要的是,我发布这个的主要原因是因为我决定跳过我只是“通过”的打印规则,即使用链式规则时
a : b | something_else ;
b : c | another ;
c : d | yet_more ;
d : rule that matters ;
因为他们在从小输入中检查树时会弄乱我的输出,而没有添加任何有用的信息。这也很容易更改,在 //pass-through rules 评论位置。
我还复制了Trees.getNodeText的定义并修改为使用普通数组来摆脱不必要的包装,如果我喜欢的话,甚至让我自定义它。
最后,我让它使用解析器和树,然后直接转储到 System.out,因为这是我唯一需要它的情况。
import org.antlr.v4.runtime.Parser;
import org.antlr.v4.runtime.RuleContext;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.tree.ErrorNode;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.antlr.v4.runtime.tree.Tree;
import org.stringtemplate.v4.ST;
//for pretty-dumping trees in short form
public class TreeUtils {
private static final ST template() {
return new ST("<rule_text>\n\t<child; separator=\"\n\">");
}
private static final ST literal(String text) {
return new ST("<text>").add("text", text);
}
public static void dump(Parser parser, Tree tree) {
System.out.println(process(parser.getRuleNames(),tree).render());
}
private static String getNodeText(Tree t, String[] ruleNames) {
if ( t instanceof RuleContext ) {
int ruleIndex = ((RuleContext)t).getRuleContext().getRuleIndex();
String ruleName = ruleNames[ruleIndex];
return ruleName;
}
else if ( t instanceof ErrorNode) {
return t.toString();
}
else if ( t instanceof TerminalNode) {
Token symbol = ((TerminalNode)t).getSymbol();
if (symbol != null) {
String s = symbol.getText();
return s;
}
}
Object payload = t.getPayload();
if ( payload instanceof Token ) {
return ((Token)payload).getText();
}
return t.getPayload().toString();
}
private static ST process(String[] ruleNames, Tree t) {
if(t.getChildCount()==0) {
return literal(getNodeText(t, ruleNames));
} else if(t.getChildCount()==1) {
//pass-through rules
return process(ruleNames,t.getChild(0));
} else {
ST out=template();
out.add("rule_text", getNodeText(t, ruleNames));
for(int i=0;i<t.getChildCount();i++) {
out.add("child", process(ruleNames,t.getChild(i)));
}
return out;
}
}
}