4

我想使用 Megaparsec 在 Haskell 中解析与此类似的文本。

# START SKIP
def foo(a,b):
    c = 2*a # Foo 
    return a + b
# END SKIP

, where# START SKIP# END SKIP标记要解析的文本块的开始和结束。

skipBlockComment相比,我希望解析器返回开始和结束标记之间的行。

这是我的解析器。

skip :: Parser String
skip = s >> manyTill anyChar e
  where s = string "# START SKIP"
        e = string "# END SKIP"

skip解析器按预期工作。

为了在开始和结束标记内允许可变数量的空白,例如,# START SKIP我尝试了以下操作:

skip' :: Parser String
skip' = s >> manyTill anyChar e
  where s = symbol "#" >> symbol "START" >> symbol "SKIP"
        e = symbol "#" >> symbol "END" >> symbol "SKIP"

用于skip'解析上述文本会出现以下错误。

3:15:
unexpected 'F'
expecting "END", space, or tab

我想了解此错误的原因以及如何修复它。

4

1 回答 1

7

正如 Alec 已经评论的那样,问题是一旦e遇到'#',它就会被视为消耗的角色。而 parsec 及其派生词的工作方式是,一旦您使用了任何字符,您就会致力于该解析分支 - 即manyTill anyChar不再考虑替代方案,即使e最终在这里失败。

不过,您可以通过将结束分隔符包含在以下内容中轻松地请求回溯try

skip' :: Parser String
skip' = s >> manyTill anyChar e
  where s = symbol "#" >> symbol "START" >> symbol "SKIP"
        e = try $ symbol "#" >> symbol "END" >> symbol "SKIP"

然后,这将在使用之前'#'设置一个“检查点”,并且当e稍后失败时(在您的示例中, at "Foo"),它将表现得好像根本没有匹配的字符。

事实上,传统的 parsec 也会对skip. 只是,因为寻找一个字符串并且只有在它完全匹配的情况下才成功是一项常见的任务,所以 megaparsec 的string实现类似于try . string,即如果失败发生在该固定字符串中,那么它将总是回溯。

然而,复合解析器在默认情况下仍然不会回溯,就像在 attoparsec 中所做的那样。主要原因是,如果任何事情都可以回溯到任何一点,您就无法真正获得明确的故障点以显示在错误消息中。

于 2016-11-14T00:17:11.763 回答