Search code examples
haskellparsec

Parsec: parse string that satisfies some predicate


Parsec already have a satisfy parser which succeeds when a given predicate returns True when applied to given char:

punctuationOrSymbol = satisfy isPunctuation <|> satisfy isSymbol

How can I do something similar for a whole string? I need it because I want to parse an URI but only if it is valid. For that, I want to leverage isURI function in network-uri package. Something like:

uri :: ParsecT s u m URI
uri = parseURI <$> satisfyForString isURI

I suspect that it has something to do with token function, but I'm new to Haskell and I haven't been able to understand it well.


Solution

  • ParsecT is an instance of Alternative, so you can use guard to do something like this:

    predicateP :: (a -> Bool) -> Parsec s u a -> Parsec s u a
    predicateP pr p = do
       x <- p
       guard $ pr x
       return x
    

    I wouldn't be surprised if that already existed as a combinator somewhere, but I'm not super familiar with parsec.

    Now that only gives you a way to apply a predicate; To apply that to a string you'll have to get a string which you think is or might be a URL first. How you get that depends on your domain (when parsing XML you'll most likely find those in attributes or text contents for instance).