antlr4/java:将解析树漂亮地打印到标准输出

antlr4/java:将解析树漂亮地打印到标准输出,java,antlr,antlr4,pretty-print,Java,Antlr,Antlr4,Pretty Print,初学者问题:如何将可读版本的解析树打印到标准输出 CharStream input = CharStreams.fromFileName("testdata/test.txt"); MyLexer lexer = new MyLexer(input); CommonTokenStream tokens = new CommonTokenStream(lexer); MyParser parser = new MyParser(tokens); parser.setBuildParseT

初学者问题:如何将可读版本的解析树打印到标准输出

CharStream input = CharStreams.fromFileName("testdata/test.txt");
MyLexer lexer = new MyLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
MyParser parser = new MyParser(tokens);     
parser.setBuildParseTree(true);
RuleContext tree = parser.record();
System.out.println(tree.toStringTree(parser));
这将在由括号“()”分隔的单行上打印整个树

(记录(丈夫(名字KOHAI Nikolaus)\n(出生*um.1872(普洛希茨广场))\n\n(妻子(婚姻oo)\n(名字SCHLOTTHAUER Maria)\n(出生*um.1877
...
我想要这样的

record 
  husband
    <id>
    name
       <name>
...
  wife
记录
丈夫
名称
...
妻子
作为独立的实用程序类从中提取:

import java.util.List;

import org.antlr.v4.runtime.misc.Utils;
import org.antlr.v4.runtime.tree.Tree;
import org.antlr.v4.runtime.tree.Trees;

public class TreeUtils {

    /** Platform dependent end-of-line marker */
    public static final String Eol = System.lineSeparator();
    /** The literal indent char(s) used for pretty-printing */
    public static final String Indents = "  ";
    private static int level;

    private TreeUtils() {}

    /**
     * Pretty print out a whole tree. {@link #getNodeText} is used on the node payloads to get the text
     * for the nodes. (Derived from Trees.toStringTree(....))
     */
    public static String toPrettyTree(final Tree t, final List<String> ruleNames) {
        level = 0;
        return process(t, ruleNames).replaceAll("(?m)^\\s+$", "").replaceAll("\\r?\\n\\r?\\n", Eol);
    }

    private static String process(final Tree t, final List<String> ruleNames) {
        if (t.getChildCount() == 0) return Utils.escapeWhitespace(Trees.getNodeText(t, ruleNames), false);
        StringBuilder sb = new StringBuilder();
        sb.append(lead(level));
        level++;
        String s = Utils.escapeWhitespace(Trees.getNodeText(t, ruleNames), false);
        sb.append(s + ' ');
        for (int i = 0; i < t.getChildCount(); i++) {
            sb.append(process(t.getChild(i), ruleNames));
        }
        level--;
        sb.append(lead(level));
        return sb.toString();
    }

    private static String lead(int level) {
        StringBuilder sb = new StringBuilder();
        if (level > 0) {
            sb.append(Eol);
            for (int cnt = 0; cnt < level; cnt++) {
                sb.append(Indents);
            }
        }
        return sb.toString();
    }
}
import java.util.List;
导入org.antlr.v4.runtime.misc.Utils;
导入org.antlr.v4.runtime.tree.tree;
导入org.antlr.v4.runtime.tree.Trees;
公营树木{
/**平台相关的线端标记*/
公共静态最终字符串Eol=System.lineSeparator();
/**用于精确打印的文字缩进字符*/
公共静态最终字符串缩进=”;
私有静态int级;
私有树(){}
/**
*漂亮地打印出整个树。{@link#getNodeText}用于节点有效负载以获取文本
*用于节点。(从Trees.toStringTree(..)派生)
*/
公共静态字符串toPrettyTree(最终树t,最终列表规则名){
级别=0;
返回进程(t,规则名).replaceAll(“(?m)^\\s+$”,”).replaceAll(\\r?\\n\\r?\\n”,下线);
}
私有静态字符串进程(最终树t、最终列表规则名){
if(t.getChildCount()==0)返回Utils.escapeWhitespace(Trees.getNodeText(t,ruleNames),false;
StringBuilder sb=新的StringBuilder();
某人追加(领导(级别));
级别++;
字符串s=Utils.escapeWhitespace(Trees.getNodeText(t,ruleNames),false);
某人附加(s+“”);
对于(int i=0;i0){
某人追加(Eol);
对于(int-cnt=0;cnt
按如下方式调用该方法:

List<String> ruleNamesList = Arrays.asList(parser.getRuleNames());
String prettyTree = TreeUtils.toPrettyTree(tree, ruleNamesList);
List ruleNamesList=Arrays.asList(parser.getRuleNames());
字符串prettyTree=TreeUtils.toPrettyTree(树,规则名称列表);

除了图形解析树之外,还生成格式化文本解析树:


如果您只想将regex用于真正的用途,您可以自己打印一棵树:

import org.antlr.v4.runtime.Parser;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.Trees;

public static String printSyntaxTree(Parser parser, ParseTree root) {
    StringBuilder buf = new StringBuilder();
    recursive(root, buf, 0, Arrays.asList(parser.getRuleNames()));
    return buf.toString();
}

private static void recursive(ParseTree aRoot, StringBuilder buf, int offset, List<String> ruleNames) {
    for (int i = 0; i < offset; i++) {
        buf.append("  ");
    }
    buf.append(Trees.getNodeText(aRoot, ruleNames)).append("\n");
    if (aRoot instanceof ParserRuleContext) {
        ParserRuleContext prc = (ParserRuleContext) aRoot;
        if (prc.children != null) {
            for (ParseTree child : prc.children) {
                recursive(child, buf, offset + 1, ruleNames);
            }
        }
    }
}

我想利用我已经在我的项目中使用的事实,对这一点发表自己的见解。这意味着我不必像其他答案那样手动处理级别。这也使输出格式更易于自定义

最重要的是,我发布这篇文章的主要原因是因为我决定跳过我只是“通过”的打印规则,即使用链规则时

a : b | something_else ;
b : c | another ;
c : d | yet_more ;
d : rule that matters ;
因为当从小输入检查树时,它们会使我的输出变得混乱,而没有添加任何有用的信息。在
//传递规则
注释位置,这也很容易更改

我还复制了
Trees.getNodeText
的定义,并将其修改为使用普通数组来消除不必要的包装,甚至如果我愿意,还可以对其进行自定义

最后,我让解析器和树直接转储到System.out,因为这是我唯一需要它的情况

import org.antlr.v4.runtime.Parser;
导入org.antlr.v4.runtime.RuleContext;
导入org.antlr.v4.runtime.Token;
导入org.antlr.v4.runtime.tree.ErrorNode;
导入org.antlr.v4.runtime.tree.TerminalNode;
导入org.antlr.v4.runtime.tree.tree;
导入org.stringtemplate.v4.ST;
//以简洁的形式展示美丽的树木
公营树木{
私有静态最终ST模板(){
返回新的ST(“\n\t”);
}
私有静态最终ST文本(字符串文本){
返回新的ST(“”)。添加(“文本”,text);
}
公共静态空转储(解析器、树){
process(parser.getRuleNames(),tree.render());
}
私有静态字符串getNodeText(树t,字符串[]规则名){
if(规则上下文的t实例){
int ruleIndex=((RuleContext)t).getRuleContext().getRuleIndex();
字符串ruleName=ruleNames[ruleIndex];
返回规则名;
}
else if(错误节点的t实例){
返回t.toString();
}
else if(终端节点的t实例){
令牌符号=((终端节点)t).getSymbol();
如果(符号!=null){
字符串s=symbol.getText();
返回s;
}
}
对象有效载荷=t.getPayload();
if(有效负载实例of令牌){
返回((令牌)有效负载).getText();
}
返回t.getPayload().toString();
}
私有静态ST进程(字符串[]规则名,树t){
if(t.getChildCount()==0){
返回文本(getNodeText(t,ruleNames));
}else if(t.getChildCount()==1){
//通过规则
返回进程(ruleNames,t.getChild(0));
}否则{
ST out=模板();
添加(“rule_text”,getNodeText(t,ruleNames));

对于(int i=0;iThere还有一个PyCharm:如何显示图形?在调试器中运行语法后,这两种类型的解析树都会显示出来(它们在启动任务中被启用)。有关更多详细信息,请参阅。
ParseTree root = parser.yourOwnRule();
System.out.println(printSyntaxTree(parser, root));
a : b | something_else ;
b : c | another ;
c : d | yet_more ;
d : rule that matters ;