module Data.Attoparsec.Internal
(
Parser
, Result(..)
, parse
, parseOnly
, (<?>)
, try
, module Data.Attoparsec.Combinator
, satisfy
, satisfyWith
, anyWord8
, skip
, word8
, notWord8
, inClass
, notInClass
, storable
, skipWhile
, string
, stringTransform
, take
, scan
, takeWhile
, takeWhile1
, takeTill
, takeByteString
, takeLazyByteString
, endOfInput
, atEnd
, ensure
, endOfLine
) where
import Control.Applicative (Alternative(..), Applicative(..), (<$>))
import Control.Monad (MonadPlus(..), when)
import Data.Attoparsec.Combinator
import Data.Attoparsec.FastSet (charClass, memberWord8)
import Data.Monoid (Monoid(..))
import Data.Word (Word8)
import Foreign.ForeignPtr (withForeignPtr)
import Foreign.Ptr (castPtr, plusPtr)
import Foreign.Storable (Storable(peek, sizeOf), peekByteOff)
import Prelude hiding (getChar, take, takeWhile)
import System.IO.Unsafe (unsafePerformIO)
import qualified Data.ByteString as B8
import qualified Data.ByteString.Char8 as B
import qualified Data.ByteString.Internal as B
import qualified Data.ByteString.Unsafe as B
import qualified Data.ByteString.Lazy as L
data Result r = Fail B.ByteString [String] String
| Partial (B.ByteString -> Result r)
| Done B.ByteString r
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
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
newtype Input = I {unI :: B.ByteString}
newtype Added = A {unA :: B.ByteString}
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
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
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
returnP :: a -> Parser a
returnP a = Parser (\i0 a0 m0 _kf ks -> ks i0 a0 m0 a)
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
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
instance MonadPlus Parser where
mzero = failDesc "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)
instance Functor Parser where
fmap = fmapP
apP :: Parser (a -> b) -> Parser a -> Parser b
apP d e = do
b <- d
a <- e
return (b a)
instance Applicative Parser where
pure = returnP
(<*>) = apP
(*>) = (>>)
x <* y = x >>= \a -> y >> return a
instance Monoid (Parser a) where
mempty = failDesc "mempty"
mappend = plus
instance Alternative Parser where
empty = failDesc "empty"
(<|>) = plus
failDesc :: String -> Parser a
failDesc err = Parser (\i0 a0 m0 kf _ks -> kf i0 a0 m0 [] msg)
where msg = "Failed reading: " ++ err
ensure :: Int -> Parser B.ByteString
ensure !n = Parser $ \i0 a0 m0 kf ks ->
if B.length (unI i0) >= n
then ks i0 a0 m0 (unI i0)
else runParser (demandInput >> ensure n) i0 a0 m0 kf ks
prompt :: Input -> Added -> More
-> (Input -> Added -> More -> Result r)
-> (Input -> Added -> More -> Result r)
-> Result r
prompt i0 a0 _m0 kf ks = Partial $ \s ->
if B.null s
then kf i0 a0 Complete
else ks (I (unI i0 +++ s)) (A (unA a0 +++ s)) Incomplete
demandInput :: Parser ()
demandInput = Parser $ \i0 a0 m0 kf ks ->
if m0 == Complete
then kf i0 a0 m0 ["demandInput"] "not enough bytes"
else let kf' i a m = kf i a m ["demandInput"] "not enough bytes"
ks' i a m = ks i a m ()
in prompt i0 a0 m0 kf' ks'
wantInput :: Parser Bool
wantInput = Parser $ \i0 a0 m0 _kf ks ->
case () of
_ | not (B.null (unI i0)) -> ks i0 a0 m0 True
| m0 == Complete -> ks i0 a0 m0 False
| otherwise -> let kf' i a m = ks i a m False
ks' i a m = ks i a m True
in prompt i0 a0 m0 kf' ks'
get :: Parser B.ByteString
get = Parser $ \i0 a0 m0 _kf ks -> ks i0 a0 m0 (unI i0)
put :: B.ByteString -> Parser ()
put s = Parser $ \_i0 a0 m0 _kf ks -> ks (I s) a0 m0 ()
(+++) :: B.ByteString -> B.ByteString -> B.ByteString
(+++) = B.append
try :: Parser a -> Parser a
try p = Parser $ \i0 a0 m0 kf ks ->
noAdds i0 a0 m0 $ \i1 a1 m1 ->
let kf' i2 a2 m2 = addS i0 a0 m0 i2 a2 m2 kf
in runParser p i1 a1 m1 kf' ks
satisfy :: (Word8 -> Bool) -> Parser Word8
satisfy p = do
s <- ensure 1
let w = B.unsafeHead s
if p w
then put (B.unsafeTail s) >> return w
else fail "satisfy"
skip :: (Word8 -> Bool) -> Parser ()
skip p = do
s <- ensure 1
if p (B.unsafeHead s)
then put (B.unsafeTail s)
else fail "skip"
satisfyWith :: (Word8 -> a) -> (a -> Bool) -> Parser a
satisfyWith f p = do
s <- ensure 1
let c = f (B.unsafeHead s)
if p c
then put (B.unsafeTail s) >> return c
else fail "satisfyWith"
storable :: Storable a => Parser a
storable = hack undefined
where
hack :: Storable b => b -> Parser b
hack dummy = do
(fp,o,_) <- B.toForeignPtr `fmapP` take (sizeOf dummy)
return . B.inlinePerformIO . withForeignPtr fp $ \p ->
peek (castPtr $ p `plusPtr` o)
takeWith :: Int -> (B.ByteString -> Bool) -> Parser B.ByteString
takeWith n p = do
s <- ensure n
let (h,t) = B.splitAt n s
if p h
then put t >> return h
else failDesc "takeWith"
take :: Int -> Parser B.ByteString
take n = takeWith n (const True)
string :: B.ByteString -> Parser B.ByteString
string s = takeWith (B.length s) (==s)
stringTransform :: (B.ByteString -> B.ByteString) -> B.ByteString
-> Parser B.ByteString
stringTransform f s = takeWith (B.length s) ((==f s) . f)
skipWhile :: (Word8 -> Bool) -> Parser ()
skipWhile p = go
where
go = do
t <- B8.dropWhile p <$> get
put t
when (B.null t) $ do
input <- wantInput
when input go
takeTill :: (Word8 -> Bool) -> Parser B.ByteString
takeTill p = takeWhile (not . p)
takeWhile :: (Word8 -> Bool) -> Parser B.ByteString
takeWhile p = (B.concat . reverse) `fmap` go []
where
go acc = do
(h,t) <- B8.span p <$> get
put t
if B.null t
then do
input <- wantInput
if input
then go (h:acc)
else return (h:acc)
else return (h:acc)
takeRest :: Parser [B.ByteString]
takeRest = go []
where
go acc = do
input <- wantInput
if input
then do
s <- get
put B.empty
go (s:acc)
else return (reverse acc)
takeByteString :: Parser B.ByteString
takeByteString = B.concat `fmap` takeRest
takeLazyByteString :: Parser L.ByteString
takeLazyByteString = L.fromChunks `fmap` takeRest
scan :: s -> (s -> Word8 -> Maybe s) -> Parser B.ByteString
scan s0 p = do
chunks <- go [] s0
case chunks of
[x] -> return x
xs -> return . B.concat . reverse $ xs
where
go acc s1 = do
let scanner (B.PS fp off len) =
withForeignPtr fp $ \ptr -> do
let inner !i !s | i == off+len = done (ioff) s
| otherwise = do
w <- peekByteOff ptr i
case p s w of
Just s' -> inner (i+1) s'
Nothing -> done (ioff) s
done !i !s = return (B.PS fp off i, B.PS fp (off+i) (leni),s)
inner off s1
(h,t,s') <- (unsafePerformIO . scanner) <$> get
put t
if B.null t
then do
input <- wantInput
if input
then go (h:acc) s'
else return (h:acc)
else return (h:acc)
takeWhile1 :: (Word8 -> Bool) -> Parser B.ByteString
takeWhile1 p = do
(`when` demandInput) =<< B.null <$> get
(h,t) <- B8.span p <$> get
when (B.null h) $ failDesc "takeWhile1"
put t
if B.null t
then (h+++) `fmapP` takeWhile p
else return h
inClass :: String -> Word8 -> Bool
inClass s = (`memberWord8` mySet)
where mySet = charClass s
notInClass :: String -> Word8 -> Bool
notInClass s = not . inClass s
anyWord8 :: Parser Word8
anyWord8 = satisfy $ const True
word8 :: Word8 -> Parser Word8
word8 c = satisfy (== c) <?> show c
notWord8 :: Word8 -> Parser Word8
notWord8 c = satisfy (/= c) <?> "not " ++ show c
endOfInput :: Parser ()
endOfInput = Parser $ \i0 a0 m0 kf ks ->
if B.null (unI i0)
then if m0 == Complete
then ks i0 a0 m0 ()
else let kf' i1 a1 m1 _ _ = addS i0 a0 m0 i1 a1 m1 $
\ i2 a2 m2 -> ks i2 a2 m2 ()
ks' i1 a1 m1 _ = addS i0 a0 m0 i1 a1 m1 $
\ i2 a2 m2 -> kf i2 a2 m2 []
"endOfInput"
in runParser demandInput i0 a0 m0 kf' ks'
else kf i0 a0 m0 [] "endOfInput"
atEnd :: Parser Bool
atEnd = not <$> wantInput
endOfLine :: Parser ()
endOfLine = (word8 10 >> return ()) <|> (string "\r\n" >> return ())
(<?>) :: Parser a
-> String
-> Parser a
p <?> msg0 = Parser $ \i0 a0 m0 kf ks ->
let kf' i a m strs msg = kf i a m (msg0:strs) msg
in runParser p i0 a0 m0 kf' ks
infix 0 <?>
failK :: Failure a
failK i0 _a0 _m0 stack msg = Fail (unI i0) stack msg
successK :: Success a a
successK i0 _a0 _m0 a = Done (unI i0) a
parse :: Parser a -> B.ByteString -> Result a
parse m s = runParser m (I s) (A B.empty) Incomplete failK successK
parseOnly :: Parser a -> B.ByteString -> Either String a
parseOnly m s = case runParser m (I s) (A B.empty) Complete failK successK of
Fail _ _ err -> Left err
Done _ a -> Right a
_ -> error "parseOnly: impossible error!"