问题 可以使用Haskell的Parsec库来实现带备份的递归下降解析器吗?


我一直在考虑使用Haskell的Parsec解析库来解析Java的一个子集作为递归下降解析器,作为更传统的解析器生成器解决方案(如Happy)的替代。 Parsec似乎很容易使用,解析速度绝对不是我的一个因素。不过,我想知道是否可以用Parsec实现“备份”,这是一种通过依次尝试每个产品来找到正确生产的技术。举一个简单的例子,考虑JLS Java语法的开头:

Literal:
    IntegerLiteral  
    FloatingPointLiteral

我想要一种方法来不必弄清楚我应该如何命令这两个规则来使解析成功。就目前而言,这样一个天真的实现:

literal = do {
    x <- try (do { v <- integer; return (IntLiteral v)}) <|>
         (do { v <- float; return (FPLiteral v)});
    return(Literal x)
}

无法工作......像“15.2”之类的输入将导致整数解析器首先成功,然后整个事情将会扼杀“。”符号。当然,在这种情况下,您可以通过重新订购两个产品来解决问题。但在一般情况下,发现这样的事情将成为一场噩梦,我很可能会错过一些案例。理想情况下,我想让Parsec为我找出这样的东西。这是可能的,还是我只是想对图书馆做太多事情? Parsec文档声称它可以“解析上下文敏感的,无限的超前语法”,所以看起来我应该能够在这里做点什么。


3648
2018-03-20 14:37


起源



答案:


要么使用Parsec's notFollowedBy 为了保证 integer 将所有内容消耗到某个标记分隔符(这种方法在大多数情况下将扩展到任意方案),或者查看探索所有可能的解析替代方案的解析器组合器。首先要想到的是 UU_Parsing 图书馆。


2
2018-03-22 21:40





你可以这样做的一种方法是使用 try 组合器,它允许解析器使用输入并失败而不会使整个解析失败。

另一种是使用 Text.ParserCombinators.ReadP,它实现了一个对称选择运算符,在其中证明了这一点 a +++ b = b +++ a,所以订单真的无关紧要。我很偏爱 ReadP,因为它很小,但提供了构建一个非常强大的解析器所需的东西。


8
2018-03-23 17:42



请注意,经过一段时间的经验,我不会那么着迷 ReadP。它有一些指数行为,有时很难找到,并且不会很好地失败。 Parsec 更大更笨,但是,我现在发现,更好的软件。 - luqui


答案:


要么使用Parsec's notFollowedBy 为了保证 integer 将所有内容消耗到某个标记分隔符(这种方法在大多数情况下将扩展到任意方案),或者查看探索所有可能的解析替代方案的解析器组合器。首先要想到的是 UU_Parsing 图书馆。


2
2018-03-22 21:40





你可以这样做的一种方法是使用 try 组合器,它允许解析器使用输入并失败而不会使整个解析失败。

另一种是使用 Text.ParserCombinators.ReadP,它实现了一个对称选择运算符,在其中证明了这一点 a +++ b = b +++ a,所以订单真的无关紧要。我很偏爱 ReadP,因为它很小,但提供了构建一个非常强大的解析器所需的东西。


8
2018-03-23 17:42



请注意,经过一段时间的经验,我不会那么着迷 ReadP。它有一些指数行为,有时很难找到,并且不会很好地失败。 Parsec 更大更笨,但是,我现在发现,更好的软件。 - luqui