使用Megaparsec解析块注释,使用符号开始和结束

时间:2016-11-13 23:58:28

标签: haskell megaparsec

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

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

,其中# 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

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

1 个答案:

答案 0 :(得分:6)

正如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稍后失败(在您的示例中,"Foo"),它将表现为没有字符完全匹配。

事实上,传统的parsec也会为skip提供相同的行为。只是,因为寻找一个字符串并且只有匹配完全才能成功,这是一项常见任务,所以megaparsec的string就像try . string一样实现,即如果失败发生在那个固定的然后它会一直回溯。

但是,复合解析器默认情况下仍然没有回溯,就像在attoparsec中一样。主要原因是,如果有任何事情可以回溯到任何一点,你就无法在错误信息中显示明确的故障点