As an experiment with trifecta I've written the following simple function:
filterParser :: (a -> Bool) -> Parser a -> Parser a
filterParser cond p = do
a <- p
if cond a
then return a
else unexpected "condition failed!"
The idea was to be able to add a condition to a parser. For example (assuming predicate prime
already exists), you would write:
filterParser prime integer
to create a parser that only accepts prime numbers.
With single parses it seems OK:
> parseString (filterParser (> 'm') letter) mempty "z"
> Success 'z
> parseString (filterParser (> 'm') letter) mempty "a"
> Failure (interactive):1:2: error: unexpected
> condition failed!
But with 'many' it doesn't work - compare:
> parseString (many $ filterParser (> 'm') letter) mempty "zzz2"
> Success "zzz"
> parseString (many $ filterParser (> 'm') letter) mempty "zzza"
> Failure (interactive):1:5: error: unexpected
> condition failed!
I was hoping the last example would also return Success "zzz"
. The call to unexpected
seems to de-rail the entire parse, which isn't what I wanted.
You need to make filterParser
recoverable by using try
:
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
However, this would get rid of the custom parse error. Restoring that by using unexpected "Condition failed"
in the else
branch doesn't help either, because of the try
.
Instead, we can reinstate the custom error message after the 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
This works as expected:
*Main> parseString (many $ filterParser (> 'm') letter) mempty "zzza"
Success "zzz"
and
*Main> parseString (filterParser (> 'm') letter) mempty "a"
Failure (interactive):1:1: error: unexpected
Condition failed
a<EOF>