如何在三连词解析器中发出失败信号

时间:2016-03-21 21:35:31

标签: haskell trifecta

作为三连胜的实验,我写了以下简单的函数:

filterParser :: (a -> Bool) -> Parser a -> Parser a
filterParser cond p = do
  a <- p
  if cond a 
    then return a 
    else unexpected "condition failed!"

这个想法是能够为解析器添加一个条件。例如(假设谓词prime已经存在),你会写: filterParser prime integer创建一个只接受素数的解析器。

单次解析似乎没问题:

> parseString (filterParser (> 'm') letter) mempty "z"
> Success 'z

> parseString (filterParser (> 'm') letter) mempty "a"
> Failure (interactive):1:2: error: unexpected
> condition failed!

但是“很多”它不起作用 - 比较:

> parseString (many $ filterParser (> 'm') letter) mempty "zzz2"
> Success "zzz"

> parseString (many $ filterParser (> 'm') letter) mempty "zzza"
> Failure (interactive):1:5: error: unexpected
> condition failed!

我希望最后一个例子也会返回Success "zzz"。对unexpected的调用似乎会破坏整个解析,这不是我想要的。

2 个答案:

答案 0 :(得分:1)

您需要使用try

恢复filterParser
import Text.Trifecta
import Control.Applicative

filterParser :: (a -> Bool) -> Parser a -> Parser a
filterParser cond p = try $ do
  x <- p
  if cond x then return x else empty

然而,这将摆脱自定义解析错误。由于unexpected "Condition failed",使用else分支中的try进行恢复并不会有所帮助。

相反,我们可以在try

之后恢复自定义错误消息
filterParser :: (a -> Bool) -> Parser a -> Parser a
filterParser cond p = (<|> unexpected "Condition failed") $ try $ do
  x <- p
  if cond x then return x else empty

这可以按预期工作:

*Main> parseString (many $ filterParser (> 'm') letter) mempty "zzza"
Success "zzz"

*Main> parseString (filterParser (> 'm') letter) mempty "a"
Failure (interactive):1:1: error: unexpected
    Condition failed
a<EOF> 

答案 1 :(得分:1)

除了Cactus建议的解决方案之外,还有以下内容:

filterParser :: (a -> Bool) -> Parser a -> Parser a
filterParser cond p = do
  a <- lookAhead p
  if cond a then p else unexpected "condition failed!"

这似乎给了我想要的东西:

> parseString (filterParser (> 'm') letter) mempty "z"
> Success 'z'

> parseString (filterParser (> 'm') letter) mempty "4"
> Failure (interactive):1:1: error: expected: letter

> parseString (filterParser (> 'm') letter) mempty "a"
> Failure (interactive):1:1: error: unexpected
>     condition failed!

> parseString (many $ filterParser (> 'm') letter) mempty "zzz4"
> Success "zzz"

> parseString (many $ filterParser (> 'm') letter) mempty "zzza"
> Success "zzz"