ANTLR ParseTree的序列化

时间:2014-03-21 14:50:54

标签: antlr antlr4

我有一个生成的语法,可以做两件事:

  • 检查域特定语言的语法
  • 根据该域特定语言评估输入

这两个函数是分开的,我们称之为validate()和evaluate()。

validate()函数从String输入构建树,同时确保它满足语言的BNF要求。 evaluate()函数将值插入到该树中以获得结果(通常为true或false)。

代码当前正在做的是每次在输入上运行validate(),只是为了生成evaluate()使用的树。有些输入需要60秒才能检查。我想要做的是序列化validate()的结果(假设它符合语法要求),将序列化的表单存储在后端数据库中,并将其作为evaluate()的一部分从数据库加载。

我注意到我可以在解析树上执行方法toStringTree(),并检索LISP样式树。但是,我可以将LISP样式树还原到ANTLR分析树吗?如果没有,任何人都可以推荐另一种方法来序列化和存储生成的解析树吗?

感谢您的帮助。

杰森

2 个答案:

答案 0 :(得分:1)

ANTLR 4的ParseRuleContext数据结构(生成的解析器用来表示解析树中语法规则的ParseTree的具体实现)默认情况下不可序列化。在项目问题跟踪器上打开issue #233涵盖功能请求。但是,根据我使用ANTLR进行解析的许多应用程序的经验,我不相信序列化解析树从长远来看会很有用。对于序列化解析树的每个问题都要解决,已经存在一个更好的解决方案。

另一种选择是在数据库中存储最后一个已知有效文件的哈希值。使用解析器创建解析树后,如果输入文件具有与上次验证时相同的散列,则可以跳过验证步骤。这利用了ANTLR 4的两个方面:

  1. 对于相同的输入文件,运行解析器两次将生成相同的解析树。
  2. 几乎在所有情况下,ANTLR 4解析器都非常快(例如,Java语法每秒可处理大约20MB的源)。剩下的情况往往是由结构不良的语法规则引起的,ANTLRWorks 2.2中新的解析器解释器功能可以分析并提出改进建议。
  3. 如果您需要的性能超出您的性能,那么解析树就不是您应该使用的数据结构。 StringTemplate 4与StringTemplate 3相比具有巨大的性能优势主要来自于解释器从使用AST(相当于用于此推理的解析树)转换为线性字节码表示/解释器的事实。出于性能原因,ST4的AST永远不需要序列化,因为字节码将被序列化。实际上,StringTemplate 4的C#端口正好提供了这个功能。

答案 1 :(得分:0)

如果语法的输入数据由几个独立的块组成,您可以尝试分别存储每个块的字符串,并使用ThreadPool为每个块单独再次运行解析过程。

例如,您的输入数据是一组方法声明:

int add(int a, int b) {
   return a+b;
}

int mul(int a, int b) {
    return a*b;
}

...

并且语法类似于:

methodList : methodDeclaration methodList
           |
           ;
methodDeclaration : // your method declaration rules...

解析器的第一次运行只收集每个方法文本并存储它。解析器在 methodList 规则启动该过程。

void visitMethodList(MethodListContext ctx) {
    if(ctx.methodDeclaration() != null) {
        String methodStr = formatParseTree(ctx.methodDeclaration(), " ");
        // store methodStr for later parsing
    }

    // visit next method list item, if any
    if(ctx.methodList() != null) {
        visit(ctx.methodList());
    }
}

第二次运行启动每个方法声明的解析(例如在一个单独的线程中)。为此,解析器从 methodDeclaration 规则开始。

void visitMethodDeclaration(MethodDeclarationContext ctx) {
    // parse the method block
}

如果因为直接调用ctx.methodDeclaration()。getText()而将formatDeclaration规则的文本格式化的原因将组合所有子节点AntLR doc的文本,可能使其无法再次解析。如果空格是语法中的标记分隔符,则在标记之间添加一个空格不应更改解析树。

String formatParseTree(ParseTree tree, String separator) {
    StringBuilder builder = new StringBuilder();
    for(int i = 0; i < tree.getChildCount(); i ++) {
        ParseTree child = tree.getChild(i);

        if(child instanceof TerminalNode) {
            builder.append(child.getText());
            builder.append(separator);
        } else if(child instanceof RuleContext) {
            builder.append(formatParseTree(child, separator));
        }
    }

    return builder.toString();
}
相关问题