marcosh
marcosh

Reputation: 9008

Haskell - intersperse a parser with another one

I have two parsers parser1 :: Parser a and parser2 :: Parser a.

I would like now to parse a list of as interspersing them with parser2

The desired signature is something like

interspersedParser :: Parser b -> Parser a -> Parser [a]

For example, if Parser a parses the 'a' character and Parser b parser the 'b' character, then the interspersedParser should parse

""
"a"
"aba"
"ababa"
...

I'm using megaparsec. Is there already some combinator which behaves like this, which I'm currently not able to find?

Upvotes: 1

Views: 250

Answers (2)

Daniel Martin
Daniel Martin

Reputation: 23548

Sure, you can use sepBy, but isn't this just:

interspersedParser sepP thingP = (:) <$> thingP <*> many (sepP *> thingP)

EDIT: Oh, this requires at least one thing to be there. You also wanted empty, so just stick a <|> pure [] on the end.

In fact, this is basically how sepBy1 (a variant of sepBy that requires at least one) is implemented:

-- | @sepBy p sep@ parses /zero/ or more occurrences of @p@, separated
-- by @sep@. Returns a list of values returned by @p@.
--
-- > commaSep p = p `sepBy` comma

sepBy :: Alternative m => m a -> m sep -> m [a]
sepBy p sep = sepBy1 p sep <|> pure []
{-# INLINE sepBy #-}

-- | @sepBy1 p sep@ parses /one/ or more occurrences of @p@, separated
-- by @sep@. Returns a list of values returned by @p@.

sepBy1 :: Alternative m => m a -> m sep -> m [a]
sepBy1 p sep = (:) <$> p <*> many (sep *> p)
{-# INLINE sepBy1 #-}

Upvotes: 3

Niko
Niko

Reputation: 401

In parsec there is a sepBy parser which does that. The same parser seems to be available in megaparsec as well: https://hackage.haskell.org/package/megaparsec-4.4.0/docs/Text-Megaparsec-Combinator.html

Upvotes: 3

Related Questions