简单的递归下降解析器?

问题描述 投票:6回答:4

我正在编写一个用于编译成JS的模板语言的解析器(如果相关的话)。我从一些简单的正则表达式开始,这似乎有效,但正则表达式非常脆弱,所以我决定编写一个解析器。我开始编写一个简单的解析器,通过推送/弹出堆栈来记住状态,但事情不断升级,直到我手上有一个递归下降解析器。

不久之后,我比较了以前所有解析方法的性能。递归下降解析器是迄今为止最慢的。我被困了:是否值得使用递归下降解析器来处理简单的事情,或者我是否有理由采取快捷方式?我很想去纯粹的正则表达式路线,这种路线非常快(几乎比RD解析器快3倍),但在某种程度上非常黑客且无法维护。我认为性能并不是非常重要,因为编译模板是缓存的,但是递归下降解析器是每个任务的正确工具吗?我想我的问题可以被视为更具哲学性的问题:在多大程度上牺牲性能的可维护性/灵活性是值得的?

javascript parsing templates lexer tokenize
4个回答
6
投票

递归下降解析器可以非常快。

这些通常使用词法分析器进行组织,使用正则表达式来识别提供给解析器的语言标记。处理源文本的大部分工作都是由词法分析器逐个字符完成的,使用RE经常编译成的疯狂快速的FSA。

解析器偶尔会看到令牌与词法分析器看到字符的速率相比,因此它的速度通常无关紧要。然而,当比较解析器到解析器的速度,忽略lex令牌所需的时间时,递归下降解析器可以非常快,因为它们使用函数调用实现解析器堆栈,与一般解析器push-current-state-相比,函数调用已经非常有效在模拟的堆栈。

所以,你也可以吃蛋糕,也可以吃。使用regexps作为词位。使用解析器(任何类型,递归下降都很好)来处理词位。你应该对表现感到满意。

这种方法也满足了其他答案所做的观察:以一种使其可维护的方式编写它。我保证,Lexer / Parser分离非常好。


0
投票

可读性第一,性能稍晚......

因此,如果您的解析器使代码更具可读性,那么它就是正确的工具


0
投票

在多大程度上牺牲性能的可维护性/灵活性?

我认为将清晰的可维护代码作为第一优先级非常重要。在您的代码不仅表明它是瓶颈,而且您的应用程序性能也受到影响之前,您应始终将清晰的代码视为最佳代码。

不重新发明轮子也很重要。看看另一个解析器的评论非常好。通常会找到用于编写此类例程的通用解决方案。

当应用于适用的东西时,递归非常优雅。在我自己的实验中,由于递归导致的慢代码是一个例外,而不是常态。


0
投票

递归下降解析器应该更快

......或者你做错了什么。

首先,您的代码应分为两个不同的步骤。 Lexer + Parser。

在线的一些参考示例首先将整个语法首先标记为大型中间数据结构,然后将其传递给解析器。虽然有益于示范;不要这样做,它会使时间和内存复杂性增加一倍。相反,只要词法分析器确定匹配,就通知解析器状态转换或状态转换+数据。

至于词法分析者。这可能是您找到当前瓶颈的地方。如果词法分析器与解析器完全分离,您可以尝试在Regex和非Regex实现之间进行抖动以比较性能。

无论如何,正则表达式不比读取原始字符串更快。它只是避免了一些常见的错误。具体来说,不必要地创建字符串对象。理想情况下,您的词法分析器应扫描您的代码并生成一个零中间数据的输出,除了在解析器中跟踪状态所需的最低限度。记忆方面你应该有:

  • 原始输入(即来源)
  • 解析状态(ex isExpression,isSatement,row,col)
  • 数据(Ex AST,Tree,2D Array等)。

例如,如果您当前的词法分析器与非终端匹配并逐个复制每个字符,直到它到达下一个终端;你实际上是为匹配的每个字母重新创建字符串。请记住,字符串数据类型是不可变的,concat将始终创建一个新字符串。您应该使用指针算法或某些等效项扫描文本。

要解决此问题,您需要从非终端的startPos扫描到非终端的末尾,并仅在匹配完成时进行复制。

正则表达式默认支持所有这一切,这就是为什么它是编写词法分析器的首选工具。不要试图编写一个解析整个语法的正则表达式,而是编写一个只关注匹配终端和非终端作为捕获组的程序。跳过标记化,并将结果直接传递到解析器/状态机。

关键在于,不要尝试使用Regex作为状态机。它最多只适用于Regular(即Chomsky Type III,无堆栈)声明性语法 - 因此称为Regular Expression。例如,HTML是一种无上下文(即基于Chomsky Type II,基于堆栈)的声明性语法,这就是为什么仅Rexeg永远不足以解析它。您的语法以及通常所有模板语法都属于这一类。你已经明显达到了Regex的极限,所以你走在了正确的轨道上。

仅使用正则表达式进行标记化。如果您真的关心性能,请重写词法分析器以消除任何和所有不必要的字符串复制和/或中间数据。看看你是否能胜过Regex版本。

关键是。正则表达式版本更易于理解和维护,而如果正确编写,您的手动词法分析器可能会更快。传统智慧说,帮自己一个忙,更喜欢前者。就Big-O复杂性而言,两者之间应该没有任何区别。它们是同一件事的两种形式。

© www.soinside.com 2019 - 2024. All rights reserved.