Haskell编译器如何在实践中实现解析错误(t)规则?

时间:2015-09-02 04:25:27

标签: parsing haskell ghc lexical-analysis

Haskell报告在布局规则中包含一个名为“parse-error(t)”的臭名昭着的条款。此规则的目的是避免强制程序员在单行let表达式和类似情况下编写大括号。相关的句子是:

  

边条件解析错误(t)将被解释如下:如果到目前为止L生成的标记与下一个标记t一起表示Haskell语法的无效前缀,并且到目前为止生成的标记是L后跟标记“}”表示Haskell语法的有效前缀,然后parse-error(t)为真。

这会创建一个不寻常的依赖关系,其中词法分析器必然都会为解析器生成令牌,并通过插入额外的令牌来解析解析器中产生的错误。这与任何其他语言定义中的任何内容都不同,如果100%字面解释,则会严重影响实现。

不出所料,我所知道的Haskell编译器没有像编写的那样实现整个规则。例如,GHC fails to parse the following expression,根据报告是合法的:

let x = 42 in x == 42 == True

还有各种其他类似的奇怪案例。 This post列出了一些特别困难的例子。其中一些GHC正常工作,但它(从7.10.1开始)也失败了:

e = case 1 of 1 -> 1 :: Int + 1

此外,似乎GHC有一个名为AlternativeLayoutRule的未记录的语言扩展,它用词法分析器中的一堆令牌上下文替换parse-error(t)子句,在大多数情况下给出类似的结果;但是,这不是默认行为。

现实世界的Haskell编译器(特别是GHC)在lexing期间做什么来近似解析错误(t)规则?我很好奇,因为我试图实现一个简单的Haskell编译器和这个规则真的让我感到沮丧。 (另见this related question。)

1 个答案:

答案 0 :(得分:4)

我认为parse-error(t)规则意味着难以实施。是的,它确实需要解析器与词法分析器进行通信,但除此之外,它可能设计为相对 easy 以当时的主流解析技术实现:基于LALR(1)生成的解析器有一些小的纠错支持,比如GNU Bison,或者确实喜欢GHC使用的Happy。

可能具有讽刺意味的是,至少部分原因是Haskell在启用解析器组合库方面的成功,旧技术并不像以前那样占主导地位,至少在Haskell社区中是这样。

LALR(1)(或LR(1))生成的解析器具有以下功能,非常适合parse-error(t)规则意图的解释方式:

  • 它永远不会回溯。
  • 它的表驱动决定意味着它总是“知道”#34;一个给定的令牌在当前位置是否合法,如果是,那该怎么办呢。

Happy有一个特殊的error令牌,可用于在当前词汇令牌合法时实现操作。鉴于此,GHC的Happy语法中的most relevant definition

close :: { () } 
        : vccurly               { () } -- context popped in lexer. 
        | error                 {% popContext } 

vccurly("虚拟关闭")是词法分析器自行选择关闭布局级别时发送的标记。 popContextan action defined in the lexer source,用于从布局堆栈中删除布局级别。 (注意BTW,在这个实现中,error情况需要词法分析器发回vccurly令牌。

使用这个,所有GHC解析器规则必须使用close作为非终结令牌来结束用vocurly打开的缩进块。假设语法的其余部分是正确的,这也正确地实现了规则。

或者至少,这是理论。事实证明,由于Haskell / GHC的其他功能,它有时会因为LALR(1)语法不适合

而失败。

在上面的两个例子中,第一个在Haskell 2010中被更改了(因为人们意识到解析起来太尴尬了),所以GHC在那里是正确的。但第二个(e = case 1 of 1 -> 1 :: Int + 1)发生的原因是different design decision GHC使:

  

使解析器精确地解析正确的语言很难。所以GHC的解析器遵循以下原则:

     
      
  • 我们经常过度慷慨地解析"并在以后过滤掉不良案例。
  •   

GHC有足够的扩展程序,Int + 1 可以解析为具有足够启用它们的类型。此外,必须编写LALR(1)-parser来直接处理启用/禁用扩展的每个组合将真的尴尬(不确定它甚至可能)。因此它首先解析最通用的语言,并在检查是否启用了所需的结果扩展时失败。但到那时解析已经完成,触发parse-error规则已经太晚了。 (或者我假设。)

最后,我应该说,即使您不使用(我)也不会认为有任何关于处理parse-error(t)规则 的事情 LA)LR(1)解析器。我怀疑像GHC的close令牌这样的东西在组合器中也可以很好地工作。但是你仍然需要与词法分析者进行某种沟通。