{-# LANGUAGE BangPatterns, Haskell2010, Rank2Types, Safe #-} -- | -- Module : Data.Picoparsec.Internal.Types -- Copyright : Bryan O'Sullivan 2007-2011, Mario Blažević 2014 -- License : BSD3 -- -- Maintainer : Mario Blažević -- Stability : experimental -- Portability : unknown -- -- Simple, efficient parser combinators, loosely based on the Parsec -- library. module Data.Picoparsec.Internal.Types ( Parser(..) , Failure , Success , IResult(..) , Input(..) , Added(..) , More(..) , addS ) where import Control.Applicative -- (Alternative(..), Applicative(..), (<$>)) import Control.DeepSeq (NFData(rnf)) import Control.Monad (MonadPlus(..)) import Data.Monoid -- (Monoid(..), (<>)) import Prelude -- | The result of a parse. This is parameterised over the type @t@ -- of string that was processed. -- -- This type is an instance of 'Functor', where 'fmap' transforms the -- value in a 'Done' result. data IResult i r = Fail i [String] String -- ^ The parse failed. The @i@ parameter is the input that had -- not yet been consumed when the failure occurred. The -- @[@'String'@]@ is a list of contexts in which the error -- occurred. The 'String' is the message describing the error, if -- any. | Partial (i -> IResult i r) -- ^ Supply this continuation with more input so that the parser -- can resume. To indicate that no more input is available, pass -- an empty string to the continuation. -- -- __Note__: if you get a 'Partial' result, do not call its -- continuation more than once. | Done i r -- ^ The parse succeeded. The @i@ parameter is the input that had -- not yet been consumed (if any) when the parse succeeded. instance (Show i, Show r) => Show (IResult i r) where showsPrec d ir = showParen (d > 10) $ case ir of (Fail t stk msg) -> showString "Fail" . f t . f stk . f msg (Partial _) -> showString "Partial _" (Done t r) -> showString "Done" . f t . f r where f :: Show a => a -> ShowS f x = showChar ' ' . showsPrec 11 x instance (NFData i, NFData r) => NFData (IResult i r) where rnf (Fail t stk msg) = rnf t `seq` rnf stk `seq` rnf msg rnf (Partial _) = () rnf (Done t r) = rnf t `seq` rnf r {-# INLINE rnf #-} instance Functor (IResult i) where fmap _ (Fail t stk msg) = Fail t stk msg fmap f (Partial k) = Partial (fmap f . k) fmap f (Done t r) = Done t (f r) newtype Input t = I {unI :: t} newtype Added t = A {unA :: t} instance Monoid t => Monoid (Input t) where mempty = I mempty I a `mappend` I b = I (mappend a b) instance Monoid t => Monoid (Added t) where mempty = A mempty A a `mappend` A b = A (mappend a b) -- | The core parser type. This is parameterised over the type @t@ of -- string being processed. -- -- This type is an instance of the following classes: -- -- * 'Monad', where 'fail' throws an exception (i.e. fails) with an -- error message. -- -- * 'Functor' and 'Applicative', which follow the usual definitions. -- -- * 'MonadPlus', where 'mzero' fails (with no error message) and -- 'mplus' executes the right-hand parser if the left-hand one -- fails. When the parser on the right executes, the input is reset -- to the same state as the parser on the left started with. (In -- other words, Picoparsec is a backtracking parser that supports -- arbitrary lookahead.) -- -- * 'Alternative', which follows 'MonadPlus'. newtype Parser t a = Parser { runParser :: forall r. Input t -> Added t -> More -> Failure t r -> Success t a r -> IResult t r } type Failure t r = Input t -> Added t -> More -> [String] -> String -> IResult t r type Success t a r = Input t -> Added t -> More -> a -> IResult t r -- | Have we read all available input? data More = Complete | Incomplete deriving (Eq, Show) instance Monoid More where mappend c@Complete _ = c mappend _ m = m mempty = Incomplete addS :: (Monoid t) => Input t -> Added t -> More -> Input t -> Added t -> More -> (Input t -> Added t -> More -> r) -> r addS i0 a0 m0 _i1 a1 m1 f = let !i = i0 <> I (unA a1) a = a0 <> a1 !m = m0 <> m1 in f i a m {-# INLINE addS #-} bindP :: Parser t a -> (a -> Parser t b) -> Parser t b bindP m g = Parser $ \i0 a0 m0 kf ks -> runParser m i0 a0 m0 kf $ \i1 a1 m1 a -> runParser (g a) i1 a1 m1 kf ks {-# INLINE bindP #-} returnP :: a -> Parser t a returnP a = Parser (\i0 a0 m0 _kf ks -> ks i0 a0 m0 a) {-# INLINE returnP #-} instance Monad (Parser t) where return = returnP (>>=) = bindP fail = failDesc plus :: (Monoid t) => Parser t a -> Parser t a -> Parser t a plus a b = Parser $ \i0 a0 m0 kf ks -> let kf' i1 a1 m1 _ _ = addS i0 a0 m0 i1 a1 m1 $ \ i2 a2 m2 -> runParser b i2 a2 m2 kf ks ks' i1 a1 m1 = ks i1 (a0 <> a1) m1 in runParser a i0 mempty m0 kf' ks' instance (Monoid t) => MonadPlus (Parser t) where mzero = failDesc "mzero" {-# INLINE mzero #-} mplus = plus fmapP :: (a -> b) -> Parser t a -> Parser t b fmapP p m = Parser $ \i0 a0 m0 f k -> runParser m i0 a0 m0 f $ \i1 a1 s1 a -> k i1 a1 s1 (p a) {-# INLINE fmapP #-} instance Functor (Parser t) where fmap = fmapP {-# INLINE fmap #-} apP :: Parser i (a -> b) -> Parser i a -> Parser i b apP d e = do b <- d a <- e return (b a) {-# INLINE apP #-} instance Applicative (Parser i) where pure = return {-# INLINE pure #-} (<*>) = apP {-# INLINE (<*>) #-} -- These definitions are equal to the defaults, but this -- way the optimizer doesn't have to work so hard to figure -- that out. (*>) = (>>) {-# INLINE (*>) #-} x <* y = x >>= \a -> y >> return a {-# INLINE (<*) #-} instance Monoid i => Alternative (Parser i) where empty = fail "empty" {-# INLINE empty #-} (<|>) = plus {-# INLINE (<|>) #-} many v = many_v where many_v = some_v <|> pure [] some_v = (:) <$> v <*> many_v {-# INLINE many #-} some v = some_v where many_v = some_v <|> pure [] some_v = (:) <$> v <*> many_v {-# INLINE some #-} failDesc :: String -> Parser t a failDesc err = Parser (\i0 a0 m0 kf _ks -> kf i0 a0 m0 [] msg) where msg = "Failed reading: " ++ err {-# INLINE failDesc #-}