module ReadPCombinators where
-- FIXME: Should any of these +++:s be <++ instead?

import Control.Monad (liftM2)
import Text.ParserCombinators.ReadP

count :: Int -> ReadP a -> ReadP [a]
count n p = sequence (replicate n p)

between :: ReadP open -> ReadP close -> ReadP a -> ReadP a
between open close p = do open
                          x <- p
                          close
                          return x

option :: a -> ReadP a -> ReadP a
option x p = p +++ return x

optional :: ReadP a -> ReadP ()
optional p = (p >> return ()) +++ return ()

many, many1 :: ReadP a -> ReadP [a]
many p = return [] +++ many1 p
many1 p = liftM2 (:) p (many p)

skipMany, skipMany1 :: ReadP a -> ReadP ()
skipMany p = many p >> return () -- FIXME: What about stack behaviour?
skipMany1 p = p >> skipMany p

sepBy, sepBy1 :: ReadP a -> ReadP sep -> ReadP [a]
sepBy p sep = sepBy1 p sep +++ return []
sepBy1 p sep = liftM2 (:) p (many (sep >> p))

endBy, endBy1 :: ReadP a -> ReadP sep -> ReadP [a]
endBy p sep = many (do x <- p ; sep ; return x)
endBy1 p sep = many1 (do x <- p ; sep ; return x)

chainr, chainl :: ReadP a -> ReadP (a -> a -> a) -> a -> ReadP a
chainr p op x = chainr1 p op +++ return x
chainl p op x = chainl1 p op +++ return x

chainl1, chainr1 :: ReadP a -> ReadP (a -> a -> a) -> ReadP a

chainl1 p op = p >>= rest
  where rest x = do f <- op
                    y <- p
                    return (f x y)
                 +++ return x

chainr1 p op = scan
  where scan   = p >>= rest
        rest x = do f <- op
                    y <- scan
                    return (f x y)
                 +++ return x

manyTill :: ReadP a -> ReadP end -> ReadP [a]
manyTill p end = scan
  where scan = (end >> return []) <++ (liftM2 (:) p scan)
