-----------------------------------------------------------------------------
-- |
-- Module      :  Parsimony.Combinator
-- Copyright   :  (c) Daan Leijen 1999-2001, (c) Iavor S. Diatchki 2009
--
-- Maintainer  :  iavor.diatchki@gmail.com
-- Stability   :  provisional
--
-- Commonly used generic combinators
--
-----------------------------------------------------------------------------

module Parsimony.Combinator where

import Parsimony.Prim
import Parsimony.Error
import Parsimony.Pos
import Parsimony.Stream
import Control.Applicative hiding (many)

-- | The resulting parser behaves like one of the parsers in the list.
-- The chosen parser is the first one that (i) consumes some input,
-- or (ii) succeeds with a result.
choice             :: [Parser t a] -> Parser t a
choice ps           = foldr (<|>) empty ps

-- | Behaves like the parameter parser, unless it fails without consuming
-- any input.  In that case we succeed with the given value.
option             :: a -> Parser t a -> Parser t a
option x p          = p <|> pure x

skip               :: Parser t a -> Parser t ()
skip p              = (p *> pure ()) <|> return ()

between            :: Parser t open -> Parser t close
-> Parser t a -> Parser t a
between o c p       = o *> p <* c

-- | Skip at leats one occurance of input recognized by the parser.
skipMany1          :: Parser t a -> Parser t ()
skipMany1 p         = p *> skipMany p

-- | Apply a parser repeatedly, and collect the results in a list.
many               :: Parser t a -> Parser t [a]
many p              = reverse <\$> foldMany (flip (:)) [] p

-- | Apply a parser repeatedly, and collect the results in a list.
-- The resulting list is guaranteed to be at leats of length one.
many1              :: Parser t a -> Parser t [a]
many1 p             = (:) <\$> p <*> many p

sepBy              :: Parser t a -> Parser t sep -> Parser t [a]
sepBy p sep         = option [] (sepBy1 p sep)

sepBy1             :: Parser t a -> Parser t sep -> Parser t [a]
sepBy1 p sep        = (:) <\$> p <*> many (sep *> p)

endBy1,endBy       :: Parser t  a -> Parser t  sep -> Parser t  [a]
endBy1 p sep        = many1 (p <* sep)
endBy p sep         = many  (p <* sep)

sepEndBy           :: Parser t a -> Parser t sep -> Parser t [a]
sepEndBy p sep      = option [] (sepEndBy1 p sep)

sepEndBy1          :: Parser t a -> Parser t sep -> Parser t [a]
sepEndBy1 p sep     = do x <- p
reverse <\$> foldManyWhile (flip (:)) [x] loopP
where
loopP = option Nothing (sep >> option Nothing (Just <\$> p))

-- directly recursive
count              :: Int -> Parser t  a -> Parser t  [a]
count n p           = sequence (replicate n p)

--------------------------------------------------------------------------------

-- | Matches any token.  Fails if there are no more tokens left.
anyToken           :: Stream s t => Parser s t
anyToken            = primParser getToken

-- | Matches the end of the input (i.e., when there are no more tokens
-- to extract).
eof                :: Stream s t => Parser s ()
eof                 = notFollowedBy' showToken anyToken <?> "end of input"

-- | Succeeds if the given parser fails.  The function is used
-- to display the result in error messages.
notFollowedBy'     :: (a -> String) -> Parser t a -> Parser t ()
notFollowedBy' sh p = skip (try p >>= unexpected . sh)

-- | Succeeds if the given parser fails.
-- Uses the 'Show' instance of the result type in error messages.
notFollowedBy      :: Show a => Parser t a -> Parser t ()
notFollowedBy       = notFollowedBy' show

-- | Parse a list of values recognized by the given parser.
-- The sequence of values should be terminated by a pattern recognized
-- by the terminator patser.
-- The terminator is tried before the value pattern, so if there
-- is overlap between the two, the terminator is recognized.
manyTill :: Parser t a -> Parser t end -> Parser t [a]
manyTill p end = scan
where scan  =  (end *> return []) <|> ((:) <\$> p <*> scan)

getInput           :: Parser t t
getInput            = stateInput <\$> getState

setInput           :: t -> Parser t ()
setInput i          = updateState (\s -> s { stateInput = i })

updateInput        :: (t -> t) -> Parser t ()
updateInput f       = updateState (\s -> s { stateInput = f (stateInput s) })

getPosition        :: Parser t SourcePos
getPosition         = statePos <\$> getState

setPosition        :: SourcePos -> Parser t ()
setPosition i       = updateState (\s -> s { statePos = i })

updatePosition     :: (SourcePos -> SourcePos) -> Parser t ()
updatePosition f    = updateState (\s -> s { statePos = f (statePos s)})

setState           :: State t -> Parser t ()
setState s          = updateState (\_ -> s)

infix  0 <?>

-- | Specify the name to be used if the given parser fails.
(<?>)              :: Parser t a -> String -> Parser t a
p <?> l             = labels p [l]

unexpected         :: String -> Parser t a
unexpected x        = parseError \$ newErrorMessage \$ UnExpect x

-- | Apply a parser to the given named input.
parseSource        :: Parser t a    -- ^ The parser to apply
-> SourceName    -- ^ A name for the input (used in errors)
-> t             -- ^ The input
-> Either ParseError a

parseSource p s i   = case runParser p \$ State i \$ initialPos s of
Error err   -> Left err
Ok a _      -> Right a

-- | Apply a parser to the given input.
parse              :: Parser t a           -- ^ The parser to apply
-> t                    -- ^ The input
-> Either ParseError a

parse p             = parseSource p ""