在 Antlr 4 中重复评估同一访客

edd*_*xie 5 java parsing antlr antlr4

我使用语法构建了一个 Antlr 4 过滤器(在此处的上下文中并不重要),过滤器看起来像“年龄 > 30 AND 高度 < 6.1”。

然而,问题是,我会构建一次这个过滤器,并用它来评估可能有一千个文档。每个文档中都有一个“年龄”和“身高”属性。

但是我不确定的是,如何重用解析器或词法分析器,以便我可以加快评估速度。每次都构建一个词法分析器 && 解析器似乎真的是在浪费时间。

java代码是这样的

  public Boolean createFilterVisitor(String input, DocFieldAccessor docFieldAccessor) {
    FilterLexer lexer = new FilterLexer(CharStreams.fromString(input));
    lexer.removeErrorListener(ConsoleErrorListener.INSTANCE);
    CommonTokenStream tokens = new CommonTokenStream(lexer);
    FilterParser parser = new FilterParser(tokens);
    parser.addErrorListener(new FilterErrorListener());
    parser.removeErrorListener(ConsoleErrorListener.INSTANCE);

    FilterVisitorImpl filterVisitor = new FilterVisitorImpl(docFieldAccessor);

    return filterVisitor.visit(parser.filter());
  }
Run Code Online (Sandbox Code Playgroud)

进而

for doc in docs:
  createFilterVisitor(doc, someAccessor);
Run Code Online (Sandbox Code Playgroud)

我尝试构建一次词法分析器和解析器,然后在循环开始时执行 lexer.reset() 和 parser.reset()。它似乎有效(它过滤了合理的文档),但我不确定我是否做得正确。我不知道重置是什么意思,什么时候应该使用它

所以我的问题是:

  1. 如何解析过滤器字符串并构建一次解析器/词法分析器而不是为每个文档?
  2. 我是否正确使用了重置?

我有这个代码。这行得通吗?

public class KalaFilter {
  private final String filterClause;
  private FilterLexer lexer;
  private FilterParser parser;

  @Getter
  private final FilterAnalyzer filterAnalyzer;

  public KalaFilter(String filterClause) {
    this.filterClause = filterClause;

    lexer = new FilterLexer(CharStreams.fromString(filterClause));

    lexer.removeErrorListener(ConsoleErrorListener.INSTANCE);
    CommonTokenStream tokens = new CommonTokenStream(lexer);

    parser = new FilterParser(tokens);
    parser.addErrorListener(new FilterErrorListener());
    parser.removeErrorListener(ConsoleErrorListener.INSTANCE);

    ParseTree parseTree = parser.filter();

    filterAnalyzer = new FilterAnalyzer();
    ParseTreeWalker walker = new ParseTreeWalker(); // create standard walker

    walker.walk(filterAnalyzer, parseTree);
  }

  // return filter result by visit parser
  public Boolean visitFitlerResult(DocFieldAccessor docFieldAccessor) {
    //lexer.reset();
    //FilterLexer lexer = new FilterLexer(CharStreams.fromString(filterClause));
    /*
    CommonTokenStream tokens = new CommonTokenStream(lexer);
    FilterParser parser = new FilterParser(tokens);
    parser.addErrorListener(new FilterErrorListener());
    parser.removeErrorListener(ConsoleErrorListener.INSTANCE);
     */

    parser.reset();
    FilterVisitorImpl filterVisitor = new FilterVisitorImpl(docFieldAccessor);
    return filterVisitor.visit(parser.filter());
  }
}
Run Code Online (Sandbox Code Playgroud)

sep*_*p2k 5

代码的布局方式是将字符串传递给构造函数,在构造函数中解析该字符串,然后在每次visitFilterResult调用时再次解析完全相同的字符串。除非您在语法操作中做了一些非常不寻常的事情,否则每次解析相同的字符串都应该产生完全相同的结果,因此没有理由重复解析相同的字符串而不是重复使用结果。

因此,与其将字符串、词法分析器和解析器存储为实例变量,不如存储从调用parser.filter();. 然后visitFilterResult,您可以只使用现有的解析树并在其上调用访问者,而不是在 中调用解析器。这样就不需要重置任何东西,因为解析器只被调用一次(在构造函数中)。