【问题标题】:Parser errors - pattern for generating error handling automatically解析器错误 - 自动生成错误处理的模式
【发布时间】:2015-11-14 13:17:46
【问题描述】:

是否有任何已知方法可以为机器生成的解析器实现良好的错误处理? 这类问题是否存在“模式”或已知算法?

对于“好”,我的意思是类似于手工递归下降解析器和现代编译器可获得的结果: 解析器不会在第一个错误时停止,可以使解析器发出“有意义的”错误,而不仅仅是“xyz 行中无法识别的标记”一次错误。

理想情况下,这种方法也应该是自动化的,而不是 手工制作。

我不是在寻找一个库,我需要一种方法,它可以在不同的平台上使用,理想情况下应该尽可能独立于语言。

【问题讨论】:

  • 这可能不是您想听到的,但最好还是自己编写解析器和词法分析器。这不是一项特别艰巨的任务(尤其是与编写语义分析器和代码生成器相比),并且在错误处理方面会产生最佳结果。但是不要相信我,相信第一个原生 C++ 编译器的作者和 D 编程语言的发明者 Walter Bright。他在 Dr.Dobbs here 上有一篇关于这方面的文章。 (错误恢复在第 2 页)
  • 这正是我害怕听到的,没有实用的自动化方法是可能的。请将此添加为答案,即使我不喜欢答案,我也会投票,因为它很有用。据我了解,以我有限的经验,自动化工具对于分析机器生成的语法很有用,预计错误处理非常有限。例如:虚拟机反编译器、编码消息等。虽然我知道所有专业编译器都是手写的。

标签: parsing compiler-construction grammar


【解决方案1】:

我对这个问题有一个完全不同的看法,那就是您不应该将语法错误视为内部编译器错误。任何实用的编译器实际上都在实现三种语言:

  1. 作为指定目标语言的语言L。正确的程序是这种语言的成员。
  2. 语言 ML 加上编译器识别的所有错误组成。 M \ L 的成员收到信息错误。
  3. 编译器正常终止的语言Z。该集合应该是所有可能的输入字符串的集合,但如果编译器在某些输入上崩溃,则不是。 Z \ M 的成员接收有关编译器如何失败的一般消息,通常形式为“parser failed at line x, char y”。

如果您在解析器中指定语言 M 而不是语言 L,则可以使用自动解析器生成器工具。这种方法的问题在于语言设计者总是指定 L 而不是 M。我想不出有一个像 M 标准这样的情况。

这不仅仅是抽象的废话。最近对 C++ 的更改很好地说明了这种区别。以前是这样的

template< class T > class X;
template< class T > class Y;
X<Y<int>> foo; // syntax in M

在第三行出现错误,因为字符“>>”是右移运算符的标记。那行必须写

X<Y<int> > foo; // syntax in L

标准已更改为不需要额外的空间。原因是所有主要编译器都已经编写了代码来识别这种情况,以便生成有意义的错误消息。换句话说,他们发现 M 语言已经在所有地方实现了。一旦委员会确定了这一点,他们就将 M 语法转移到新版本的 L 中。

如果设计师在研究 L 语言的同时考虑 M 语言,我们的语言设计总体上会更好。只是为了他们自己的理智,他们会做出一些努力来最小化 M 的规范大小,这对每个人来说都是一件好事。唉,世界还没有出现。

结果是您需要设计自己的语言M。这就是难题。您是否为此使用自动化工具有点离题。它有帮助,但它并没有摆脱最耗时的部分。

【讨论】:

    【解决方案2】:

    使用传统的 YACC/bison 生成器,您将获得 yyerror/YYERROR 框架,由于 LALR 解析器的无序回溯特性,因此生成非常有用的错误消息并不容易。 您甚至可以在那里添加错误恢复规则,因为您可能需要它们来抑制失败规则中的错误错误消息,而您只想削减解析规则。

    使用基于 PEG 的解析器,您可以获得更好的 ~{} 后缀错误操作块语法。参见例如。 peg manual

      rule = e1 e2 e3 ~{ error("e[12] ok; e3 has failed"); }
             | ...
    
      rule = (e1 e2 e3) ~{ error("one of e[123] has failed"); }
             | ...
    

    您会在错误的实际位置收到出色的错误消息。但是您必须编写 PEG 规则,这并不容易编写,尤其是。处理运算符优先级时。使用 LALR 解析器会更容易。

    使用更简单的recursive descent parser 生成器,您可以获得与 PEG 相同的错误报告优势,但解析速度要慢得多。

    http://lambda-the-ultimate.org/node/4781查看相同的讨论

    【讨论】:

    • 我忘了写什么,你特别问:当然,所有这些框架都假设你将发现的错误推送到全局列表。您必须自己在解析结束时打印此最大 N 解析器错误列表。这一切都很简单,但你必须自己编写这段代码。
    【解决方案3】:

    从第一个错误开始,人们就一直在尝试报告和修复语法错误。有很多关于如何做到这一点的技术论文。 在 Academic.google.com 上搜索字符串“语法错误修复”会产生 57 次点击。

    确实有几个问题:

    1) 如何向读者报告有意义的错误。首先,有 是解析器检测到错误的地方,以及用户实际制作的地方 错误。例如,一个 C 程序可能在一个陌生的地方有一个 '++' 运算符:

    void p {
     x = y ++
         z = 0;
    <EOF>
    

    大多数解析器会在遇到“z”时阻塞,并将其报告为错误位置。然而,如果错误是在使用“+”时使用了“++”,那么该报告是错误的。不幸的是,要做到这一点,您需要能够读懂程序员的想法。

    您还有报告错误上下文的问题。 您是否将错误报告为表达式中的错误[乍一看,似乎如此]?在一份声明中?在一条线上?在函数体中?在函数声明中?可能您想报告可以围绕错误点的最窄句法类别。 (请注意,您不能将函数体或声明报告为“围绕”错误点,因为它们也不完整!) 如果错误真的是 ++ 之后缺少分号怎么办?那么错误位置并不是真正的“在表达式中”。如果修复需要插入缺失的字符串引号怎么办?宏继续符?

    因此,您必须以某种方式确定实际错误的构成,然后我们才能进行错误修复。

    2) 错误修复:为了使工具以有意义的方式进行,它必须修复错误。大概这意味着修补输入令牌流以生成合法程序(如果源有多个错误,您可能无法做到这一点)。如果有几个可能的补丁怎么办?很明显,最好的错误报告是“yyyy 错误,我怀疑你应该使用 xxxx”。修复时应该考虑多大的补丁:只是触发错误的令牌,紧随其后的令牌,之前的令牌怎么样?

    我注意到很难对手写解析器进行自动、通用的错误修复建议,因为指导此类修复所需的语法在任何地方都没有明确可用。因此,您会期望自动修复最适合语法是显式工件的工具。

    也可能是错误修复应该考虑到常见错误。如果人们倾向于离开';' off 语句,并插入一个修复文件,这可能是一个很好的修复。如果他们很少这样做,并且有多个修复(例如,将“++”替换为“+”),那么替代修复可能是更好的建议。

    3) 修复的语义影响。即使您修复了语法错误,修复后的程序也可能不明智。如果你的错误需要插入标识符,应该使用什么标识符?

    FWIW,我们的 DMS 软件再造工具包完全由语法驱动自动修复。它的运行假设应该删除错误点的令牌,或者应该将其他一些单个令牌插入到它的左侧。这会捕获丢失的“;”和额外的加号;它经常成功地进行合法修复。通常它不是“正确的”。至少它可以让解析器继续处理源代码的其余部分。

    我认为寻找良好的自动错误修复将持续很长时间。

    FWIW,论文 Syntax Error Repair for a Java-based Parser Generator 报告了 Burke 的博士。论文:

    M.G. Burke,1983,一种实用的 LR 和 LL 句法错误诊断和恢复方法,博士论文,纽约大学计算机科学系

    还不错。特别是,它通过考虑和修改错误的左上下文以及错误范围来修复错误。貌似可以get it from ACM

    【讨论】:

      【解决方案4】:

      这可能不是您想听到的,但您最好亲手编写解析器和词法分析器。

      这不是一项特别艰巨的任务(尤其是与编写语义分析器和代码生成器相比),并且在错误处理方面会产生最好的结果。

      但不要相信我,相信第一个原生 C++ 编译器的作者和 D 编程语言的发明者 Walter Bright。

      他在 Dr.Dobbs here 上有一篇关于此的文章。 (错误恢复在第 2 页)

      【讨论】:

        猜你喜欢
        • 2017-09-28
        • 2016-02-07
        • 1970-01-01
        • 2021-06-12
        • 2014-07-03
        • 1970-01-01
        • 2011-03-11
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多