我想使用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
我想了解此错误的原因以及我如何解决它。
答案 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中一样。主要原因是,如果有任何事情可以回溯到任何一点,你就无法在错误信息中显示明确的故障点。