2016-11-13 21 views
3

我想使用Megaparsec解析Haskell中类似于此的文本。使用Megaparsec解析块注释使用开始和结束符号

# 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 

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

+3

问题是你的解析器有一个共同的前缀。看看['try'](https://hackage.haskell.org/package/megaparsec-5.1.1/docs/Text-Megaparsec.html#v:try)。 – Alec

回答

6

正如亚历克已经评论的那样,问题是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"),它就会像没有任何字符匹配一样。

实际上,对于skip,传统的parsec也会给出相同的行为。只是,因为寻找一个字符串,只有匹配完全匹配是一个普遍的任务,megaparsec的string被实现为try . string,即如果失败发生在该固定字符串内,那么它总是会回溯。

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

+0

谢谢@leftaroundabout!非常好的解释。 –

相关问题