attoparsec-0.9.1.2: Data/Attoparsec/Internal/Types.hs
{-# LANGUAGE BangPatterns, Rank2Types, OverloadedStrings, RecordWildCards #-}
-- |
-- Module : Data.Attoparsec.Internal.Types
-- Copyright : Bryan O'Sullivan 2007-2011
-- License : BSD3
--
-- Maintainer : bos@serpentine.com
-- Stability : experimental
-- Portability : unknown
--
-- Simple, efficient parser combinators for 'B.ByteString' strings,
-- loosely based on the Parsec library.
module Data.Attoparsec.Internal.Types
(
Parser(..)
, Failure
, Success
, Result(..)
, Input(..)
, Added(..)
, More(..)
, addS
, noAdds
, (+++)
) where
import Control.Applicative (Alternative(..), Applicative(..))
import Control.DeepSeq (NFData(rnf))
import Control.Monad (MonadPlus(..))
import Data.Monoid (Monoid(..))
import Prelude hiding (getChar, take, takeWhile)
import qualified Data.ByteString.Char8 as B
-- | The result of a parse.
data Result r = Fail B.ByteString [String] String
-- ^ The parse failed. The 'B.ByteString' 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 (B.ByteString -> Result r)
-- ^ Supply this continuation with more input so that
-- the parser can resume. To indicate that no more
-- input is available, use an 'B.empty' string.
| Done B.ByteString r
-- ^ The parse succeeded. The 'B.ByteString' is the
-- input that had not yet been consumed (if any) when
-- the parse succeeded.
instance Show r => Show (Result r) where
show (Fail bs stk msg) =
"Fail " ++ show bs ++ " " ++ show stk ++ " " ++ show msg
show (Partial _) = "Partial _"
show (Done bs r) = "Done " ++ show bs ++ " " ++ show r
instance (NFData r) => NFData (Result r) where
rnf (Fail _ _ _) = ()
rnf (Partial _) = ()
rnf (Done _ r) = rnf r
{-# INLINE rnf #-}
fmapR :: (a -> b) -> Result a -> Result b
fmapR _ (Fail st stk msg) = Fail st stk msg
fmapR f (Partial k) = Partial (fmapR f . k)
fmapR f (Done bs r) = Done bs (f r)
instance Functor Result where
fmap = fmapR
{-# INLINE fmap #-}
newtype Input = I {unI :: B.ByteString}
newtype Added = A {unA :: B.ByteString}
-- | The 'Parser' type is a monad.
newtype Parser a = Parser {
runParser :: forall r. Input -> Added -> More
-> Failure r
-> Success a r
-> Result r
}
type Failure r = Input -> Added -> More -> [String] -> String -> Result r
type Success a r = Input -> Added -> More -> a -> Result r
-- | Have we read all available input?
data More = Complete | Incomplete
deriving (Eq, Show)
addS :: Input -> Added -> More
-> Input -> Added -> More
-> (Input -> Added -> More -> r) -> r
addS i0 a0 m0 _i1 a1 m1 f =
let !i = I (unI i0 +++ unA a1)
a = A (unA a0 +++ unA a1)
!m = m0 <> m1
in f i a m
where
Complete <> _ = Complete
_ <> Complete = Complete
_ <> _ = Incomplete
{-# INLINE addS #-}
bindP :: Parser a -> (a -> Parser b) -> Parser 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 a
returnP a = Parser (\i0 a0 m0 _kf ks -> ks i0 a0 m0 a)
{-# INLINE returnP #-}
instance Monad Parser where
return = returnP
(>>=) = bindP
fail = failDesc
noAdds :: Input -> Added -> More
-> (Input -> Added -> More -> r) -> r
noAdds i0 _a0 m0 f = f i0 (A B.empty) m0
{-# INLINE noAdds #-}
plus :: Parser a -> Parser a -> Parser 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
in noAdds i0 a0 m0 $ \i2 a2 m2 -> runParser a i2 a2 m2 kf' ks
{-# INLINE plus #-}
instance MonadPlus Parser where
mzero = failDesc "mzero"
{-# INLINE mzero #-}
mplus = plus
fmapP :: (a -> b) -> Parser a -> Parser 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 where
fmap = fmapP
{-# INLINE fmap #-}
apP :: Parser (a -> b) -> Parser a -> Parser b
apP d e = do
b <- d
a <- e
return (b a)
{-# INLINE apP #-}
instance Applicative Parser where
pure = returnP
{-# INLINE pure #-}
(<*>) = apP
{-# INLINE (<*>) #-}
#if MIN_VERSION_base(4,2,0)
-- 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 (<*) #-}
#endif
instance Monoid (Parser a) where
mempty = failDesc "mempty"
{-# INLINE mempty #-}
mappend = plus
{-# INLINE mappend #-}
instance Alternative Parser where
empty = failDesc "empty"
{-# INLINE empty #-}
(<|>) = plus
{-# INLINE (<|>) #-}
failDesc :: String -> Parser a
failDesc err = Parser (\i0 a0 m0 kf _ks -> kf i0 a0 m0 [] msg)
where msg = "Failed reading: " ++ err
{-# INLINE failDesc #-}
(+++) :: B.ByteString -> B.ByteString -> B.ByteString
(+++) = B.append
{-# INLINE (+++) #-}