如何获得树结构ANLTR4

时间:2016-11-11 21:52:40

标签: java parsing tree antlr antlr4

我需要以这样的形式解析配置文件

"agent 1"(
"ip"("192.67.4.1"),
"port"("12345"),
"neighbours"(
    "agent 2"(
        "ip"("192.67.4.2"),
        "port"("12345")),
    "agent 3"(
        "ip"("192.67.4.1"),
        "port"("12346"))),
"measurements"(
    "voltage"("4.2V"),
    "power"("7KW")))

到目前为止我得到的是语法(主要通过少数教程中的试错法获得)

grammar Tree;

compileUnit
    :   group EOF
    ;

group
    :   (node '(' group ')' (',')? )*                # root
    |   node                                         # value
    ;
node
    : STRING                                         # label
    ;

STRING   :   '"'[ a-zA-Z0-9\.]+'"';
WS      :   [ \t\r\n] -> channel(HIDDEN);

在预览中它的结果是这样的树 Tree

我已经有了以表格形式向我的树添加节点的功能(其他要求):

  

ADD"标签1" "标签2" ......"标签n"

因此,解析器的完美结果将是String数组,如:

  • "代理1" " IP" " 192.67.4.1"
  • "代理1" "端口" " 12345"
  • "代理1" "邻居" "代理2" " IP" " 192.67.4.2"
  • "代理1" "邻居" "代理2" "端口" " 12345"
  • 依旧......

但是我无法进一步实现我的解析器以获得这样的String数组。所以我的问题是如何做到这一点?我知道我需要扩展像BasicListener的Parser这样的东西但是无法管理它。

1 个答案:

答案 0 :(得分:1)

解决方案

首先,让我们将命名的替代root更改为:

group
    :   node '(' (group (',')?)+ ')'     # root
    |   node                             # value
    ;

这样我们就可以跟踪每个node的标题(group)。当我进一步引用时,我的意思是配置树结构。然后让我们做两个定义:

  • 路径 - 是从树的根到树的叶子收集的值的字符串(例如"Agent 1" "neighbours" "agent 2" "ip" "192.67.4.2"),
  • 部分路径 - 是一个不以树叶结束的路径(未完成的路径,例如"Agent 1" "neighbours" "agent 2" "ip")。

现在我们将编写一个简单的树监听器来收集路径

public class TreeToArray extends TreeBaseListener {
    Stack<String> partialPath = new Stack<>();
    public List<String> paths = new ArrayList<>();

    @Override
    public void enterRoot(TreeParser.RootContext ctx) {
        if (partialPath.isEmpty()) {
            // We are in top-root, partial path consists of top-root's name
            partialPath.push(ctx.node().getText());
        } else {
            // We are in one of the sub-roots, partialPath.peek() returns a current partial path
            partialPath.push(partialPath.peek() + " " + ctx.node().getText());
        }
    }

    @Override
    public void enterValue(TreeParser.ValueContext ctx) {
        paths.add(partialPath.peek() + " " + ctx.getText());
    }

    @Override
    public void exitRoot(TreeParser.RootContext ctx) {
        partialPath.pop();
    }
}

堆栈partialPath保留有关当前构建的部分路径的信息。当我们访问主要内容时,首先root(例如"Agent 1")我们没有任何部分路径,因此我们将root放入堆栈头。否则,我们会检索部分路径并将其添加到root的标头中。当我们遇到value我们在树的叶子时,我们完成了路径。我们pop() 部分路径,然后我们添加node值。结果字符串(完整的路径)会进入paths列表。