当解析器的一部分成功而其余部分失败时,为什么Parsec不回溯?

时间:2016-08-20 19:27:34

标签: haskell parsec

我有这个parsec解析器:

a = optionMaybe $ do {try $ spaceornull *> string "hello";string "No"}                        

spaceornull((:[]) <$> try space) <|> string ""

的位置

当我用输入“”测试a时,我得到:

  

左(第1行,第2列):
    意外的输入结束
    期待“你好”

我不明白这一点,spaceornull *> string "hello"应该失败,因为没有“你好”,然后使用try parsec回溯,现在没有消耗的输入但是try无论如何都会失败所以传递给optionMaybe的解析器(do内的一个)完全失败,它不应该尝试消耗更多的输入,所以我们最终得到一个失败的解析器而不消耗任何输入所以我应该得到{{ 1}}。

但错误信息显示它,空间被消耗,因此Right Nothing没有真正回溯,解析器的一部分成功时try不回溯吗?以及如何使其回溯以上?

1 个答案:

答案 0 :(得分:4)

try与是否允许失败无关。它只是使可能在发生故障时回溯,但是为了开始回溯,你需要提供一个替代解析器来启动那一点。通常的方法是使用<|> operator

a = optionMaybe $ (try $ spaceornull *> string "hello") <|> string "No"

OTOH,您的代码相当于

a = optionMaybe $ (try $ spaceornull *> string "hello") >> string "No"

其中monadic链接operator >>(与*>相同)将在parsec的情况下检查LHS是否成功,然后继续并运行RHS解析器。所以一定是,因为你也可以写:

a = optionMaybe $ do
       s <- try $ spaceornull *> string "hello"
       string $ "No"++s

在这里我使用了第一个解析器的结果(你只是扔掉了,而不是<- - 将它与任何变量相匹配)来决定第二个应该看的对于。这显然只有第一次实际成功!

基本上,<|>仅在LHS 立即在第一个字符处失败时,或者如果您设置回溯点时才有效try。需要这样做的原因是,如果parsec需要在每个需要ckecked的替代方案之前留下一个回溯点,那将是非常低效的。