module Text.ParserCombinators.Poly.ByteStringChar
  ( 
    Parser(P)
  , Result(..)
  , runParser
    
  , next
  , eof
  , satisfy
  , onFail
    
  , manySatisfy
  , many1Satisfy
    
  , reparse
    
  , module Text.ParserCombinators.Poly.Base
  ) where
import Text.ParserCombinators.Poly.Base
import Text.ParserCombinators.Poly.Result
import qualified Data.ByteString.Lazy.Char8 as BS
import Data.ByteString.Lazy.Char8 (ByteString)
import Control.Applicative
import qualified Control.Monad.Fail as Fail
newtype Parser a = P (ByteString -> Result ByteString a)
runParser :: Parser a -> ByteString -> (Either String a, ByteString)
runParser (P p) = resultToEither . p
instance Functor Parser where
    fmap f (P p) = P (fmap f . p)
instance Monad Parser where
    return       = pure
    fail         = Fail.fail
    (P f) >>= g  = P (continue . f)
      where
        continue (Success ts x)             = let (P g') = g x in g' ts
        continue (Committed r)              = Committed (continue r)
        continue (Failure ts e)             = Failure ts e
instance Fail.MonadFail Parser where
    fail e       = P (\ts-> Failure ts e)
instance Commitment Parser where
    commit (P p)         = P (Committed . squash . p)
      where
        squash (Committed r) = squash r
        squash r             = r
    (P p) `adjustErr` f  = P (adjust . p)
      where
        adjust (Failure z e) = Failure z (f e)
        adjust (Committed r) = Committed (adjust r)
        adjust  good         = good
    oneOf' = accum []
      where accum errs [] =
                fail ("failed to parse any of the possible choices:\n"
                            ++indent 2 (concatMap showErr (reverse errs)))
            accum errs ((e,P p):ps) =
                P (\ts-> case p ts of
                           Failure _ err ->
                                       let (P p') = accum ((e,err):errs) ps
                                       in p' ts
                           r@(Success _ _)    -> r
                           r@(Committed _)    -> r )
            showErr (name,err) = name++":\n"++indent 2 err
instance Applicative Parser where
    pure x    = P (\ts-> Success ts x)
    pf <*> px = do { f <- pf; x <- px; return (f x) }
#if defined(GLASGOW_HASKELL) && GLASGOW_HASKELL > 610
    p  <*  q  = p `discard` q
#endif
instance Alternative Parser where
    empty     = fail "no parse"
    p <|> q   = p `onFail` q
instance PolyParse Parser
next :: Parser Char
next = P (\bs-> case BS.uncons bs of
                Nothing     -> Failure bs "Ran out of input (EOF)"
                Just (h, t) -> Success t h )
eof :: Parser ()
eof = P (\bs -> if BS.null bs
                then Success bs ()
                else Failure bs "Expected end of input (EOF)" )
satisfy :: (Char -> Bool) -> Parser Char
satisfy f = do { x <- next
               ; if f x then return x else fail "Parse.satisfy: failed"
               }
onFail :: Parser a -> Parser a -> Parser a
(P p) `onFail` (P q) = P (\ts-> continue ts $ p ts)
  where continue ts (Failure _ _) = q ts
    
        continue _  r             = r
manySatisfy :: (Char->Bool) -> Parser ByteString
manySatisfy f = P (\bs-> let (pre,suf) = BS.span f bs in Success suf pre)
many1Satisfy :: (Char->Bool) -> Parser ByteString
many1Satisfy f = do x <- manySatisfy f
                    if BS.null x then fail "Parse.many1Satisfy: failed"
                                 else return x
reparse    :: ByteString -> Parser ()
reparse ts  = P (\inp-> Success (ts `BS.append` inp) ())