如何创建允许语法错误的AST解析器?

时间:2014-09-26 12:26:00

标签: algorithm compiler-construction language-agnostic abstract-syntax-tree

首先,阅读有关解析和构建AST的内容?

如何为构建AST并允许语法错误的语言(如SQL)创建解析器?

例如,对于“3 + 4 * 5”:

  +
 / \
3   *
   / \
  4   5

对于语法错误的“3 + 4 * +”,解析器会猜测用户的意思是:

  +
 / \
3   *
   / \
  4   +
     / \
    ?   ?

从哪里开始?

SQL:

    SELECT_________________
   /           \           \
  .           FROM        JOIN
 / \           |         /    \
a city_name  people   address  ON
                                |
                                =______________
                               /               \
                              .____             .
                             /     \           / \
                            p  address_id     a  id

2 个答案:

答案 0 :(得分:3)

关于如何构建解析器(构建AST)的问题的标准答案是阅读有关编译的标准文本。 Aho和Ullman" Dragon"编译器书非常经典。如果你没有耐心去获得最好的参考资料,那么你会遇到更多麻烦,因为它们提供了理论并调查了细微之处。但here is my answer对于匆忙的人来说,建立递归下降解析器。

可以构建具有内置错误恢复的解析器。关于这类事情的论文很多,是20世纪80年代的热门话题。查看Google学术搜索,寻找"语法错误修复"。基本思想是解析器在遇到解析错误时跳过一些众所周知的信标(&#34 ;;"语句分隔符对于类C语言来说非常流行,这就是为什么你被问到的原因如果您的语言具有语句终止符,则发表评论),或者提出各种输入流删除或插入以克服语法错误。各种各样的计划令人惊讶。关键的想法通常是尽可能多地考虑围绕错误点的信息。我见过的最有趣的想法之一是两个解析器,一个在另一个之前运行N个令牌,寻找语法错误的地雷,第二个解析器是基于N的馈送错误修复令牌在遇到语法错误之前可用。这使得第二个解析器在达到语法错误之前选择采取不同的行为。如果你没有这个,大多数解析器会丢弃左上下文,从而失去修复能力。 (我从未实施过这样的计划。)

要插入的内容的选择通常可以从用于构建解析器的信息(通常是第一关注集合)中获得。这对于L(AL)R解析器来说相对容易,因为解析表包含必要的信息,并且在遇到错误时可供解析器使用。如果你想理解如何这样做,你需要理解如何构造解析器的理论(oops,那个编译器书籍)。 (我已多次成功实施此计划。)

无论你做什么,语法错误修复都没有多大帮助,因为几乎不可能猜出解析文档的编写者实际上想要什么。这表明花哨的计划不会真正有用。我坚持简单的;人们很乐意得到错误报告和一些半优雅的解析继续。

为真正的语言滚动自己的解析器的一个真正问题是,真正的语言是令人讨厌的混乱的东西;由于现有的代码库,或者坚持弯曲/改进语言(标准是针对懦夫,好东西用于营销)因为它很酷,所以建立真实实现的人会弄错并冻结在一块石头上。期望花费大量时间重新校准您认为语法的内容,而不是实际代码的基本事实。作为一般规则,如果你想要一个有效的解析器,最好得到一个具有跟踪记录而不是自己滚动的解析器。

大多数人都非常倾向于构建一个解析器,但如果他们想要对解析结果或树做任何任何,那么他们就会得到这样的教训。我需要比解析器更多的基本机器。检查我的生物"解析后的生活"。

答案 1 :(得分:1)

解析器可以做两件事:

  1. 报告错误并让用户重试。
  2. 修复错误并继续。
  3. 一般来说,第一个更容易(也更安全)。当语法错误时,解析器可能并不总是有足够的信息来推断意图。根据具体情况,进行修复可能会使输入在语法上正确但在语义上错误。

    我为小语言编写了一些手动递归下降解析器。在编写代码以明确解释语法规则时(与使用解析器生成器相反),很容易检测到错误,因为下一个标记不符合生产规则。生成的解析器倾向于吐出一个简单的“预期$(TOKEN_TYPE)here”消息,这对用户来说并不总是有用。使用手写解析器,通常很容易提供更具体的诊断消息,但覆盖每个案例都需要耗费时间。

    如果您的目标是报告问题但要继续解析(以便您可以查看是否存在其他问题),则可以在错误点将特殊的AST节点放在树中。这可以防止树木崩溃。

    然后,您必须重新同步到错误之外的某个点才能继续解析。正如Ira Baxter在他的回答中提到的那样,你可能会寻找一个标记,比如';',它将语句分开。要查找的正确令牌取决于您正在解析的语言。另一种可能性是猜测用户的意思(例如,在检测到错误的点处推断出额外的令牌或不同的令牌)然后继续。如果您在接下来的几个令牌中遇到另一个语法错误,您可以回溯,做出不同的猜测,然后再试一次。