module Bio.Iteratee.ListLike (
isFinished
,stream2list
,stream2stream
,dropWhileStream
,dropStream
,headStream
,tryHead
,lastStream
,heads
,peekStream
,roll
,lengthStream
,chunkLength
,takeFromChunk
,breakStream
,breakE
,takeStream
,takeUpTo
,takeWhileE
,mapStream
,concatMapStream
,concatMapStreamM
,mapMaybeStream
,rigidMapStream
,filterStream
,filterStreamM
,groupStreamBy
,groupStreamOn
,mergeStreams
,mergeByChunks
,foldStream
,enumPureNChunk
,enumWith
,zipStreams
,zipStreams3
,zipStreams4
,zipStreams5
,sequenceStreams_
,countConsumed
,mapStreamM
,mapStreamM_
,foldStreamM
,module Bio.Iteratee.Iteratee
)
where
import Bio.Iteratee.Iteratee
import Bio.Prelude
import Control.Monad.Trans.Class
import qualified Data.ByteString as B
import qualified Data.ListLike as LL
import qualified Data.ListLike.FoldableLL as FLL
isFinished :: (Nullable s) => Iteratee s m Bool
isFinished = liftI check
where
check c@(Chunk xs)
| nullC xs = liftI check
| otherwise = idone False c
check s@(EOF _) = idone True s
stream2list :: (Monad m, Nullable s, LL.ListLike s el) => Iteratee s m [el]
stream2list = liftM (concatMap LL.toList) getChunks
stream2stream :: (Monad m, Nullable s, Monoid s) => Iteratee s m s
stream2stream = liftM mconcat getChunks
headStream :: (LL.ListLike s el) => Iteratee s m el
headStream = liftI step
where
step (Chunk vec)
| LL.null vec = icont step Nothing
| otherwise = idone (LL.head vec) (Chunk $ LL.tail vec)
step stream = icont step (Just (setEOF stream))
tryHead :: (LL.ListLike s el) => Iteratee s m (Maybe el)
tryHead = liftI step
where
step (Chunk vec)
| LL.null vec = liftI step
| otherwise = idone (Just $ LL.head vec) (Chunk $ LL.tail vec)
step stream = idone Nothing stream
lastStream :: (LL.ListLike s el, Nullable s) => Iteratee s m el
lastStream = liftI (step Nothing)
where
step l (Chunk xs)
| nullC xs = liftI (step l)
| otherwise = liftI $ step (Just $ LL.last xs)
step l s@(EOF _) = case l of
Nothing -> icont (step l) . Just . setEOF $ s
Just x -> idone x s
heads :: (Monad m, Nullable s, LL.ListLike s el, Eq el) => s -> Iteratee s m Int
heads st | nullC st = return 0
heads st = loopE 0 st
where
loopE cnt xs
| nullC xs = return cnt
| otherwise = liftI (step cnt xs)
step cnt str (Chunk xs) | nullC xs = liftI (step cnt str)
step cnt str stream | nullC str = idone cnt stream
step cnt str s@(Chunk xs) =
if LL.head str == LL.head xs
then step (succ cnt) (LL.tail str) (Chunk $ LL.tail xs)
else idone cnt s
step cnt _ stream = idone cnt stream
peekStream :: (LL.ListLike s el) => Iteratee s m (Maybe el)
peekStream = liftI step
where
step s@(Chunk vec)
| LL.null vec = liftI step
| otherwise = idone (Just $ LL.head vec) s
step stream = idone Nothing stream
roll
:: (Monad m, Nullable s, LL.ListLike s el, LL.ListLike s' s)
=> Int
-> Int
-> Iteratee s m s'
roll t d | t > d = liftI step
where
step (Chunk vec)
| LL.length vec >= t =
idone (LL.singleton $ LL.take t vec) (Chunk $ LL.drop d vec)
| LL.null vec = liftI step
| otherwise = liftI (step' vec)
step stream = idone LL.empty stream
step' v1 (Chunk vec) = step . Chunk $ v1 `mappend` vec
step' v1 stream = idone (LL.singleton v1) stream
roll t d = do r <- joinI (takeStream t stream2stream)
dropStream (dt)
return $ LL.singleton r
dropStream :: (Nullable s, LL.ListLike s el) => Int -> Iteratee s m ()
dropStream 0 = idone () (Chunk emptyP)
dropStream n' = liftI (step n')
where
step n (Chunk str)
| LL.length str < n = liftI (step (n LL.length str))
| otherwise = idone () (Chunk (LL.drop n str))
step _ stream = idone () stream
dropWhileStream :: (LL.ListLike s el) => (el -> Bool) -> Iteratee s m ()
dropWhileStream p = liftI step
where
step (Chunk str)
| LL.null rest = liftI step
| otherwise = idone () (Chunk rest)
where
rest = LL.dropWhile p str
step stream = idone () stream
lengthStream :: (Num a, LL.ListLike s el) => Iteratee s m a
lengthStream = liftI (step 0)
where
step !i (Chunk xs) = liftI (step $ i + fromIntegral (LL.length xs))
step !i stream = idone i stream
chunkLength :: (LL.ListLike s el) => Iteratee s m (Maybe Int)
chunkLength = liftI step
where
step s@(Chunk xs) = idone (Just $ LL.length xs) s
step stream = idone Nothing stream
takeFromChunk ::
(Nullable s, LL.ListLike s el)
=> Int
-> Iteratee s m s
takeFromChunk n | n <= 0 = idone emptyP (Chunk emptyP)
takeFromChunk n = liftI step
where
step (Chunk xs) = let (h,t) = LL.splitAt n xs in idone h $ Chunk t
step stream = idone emptyP stream
breakStream :: (LL.ListLike s el) => (el -> Bool) -> Iteratee s m s
breakStream cpred = icont (step mempty) Nothing
where
step bfr (Chunk str)
| LL.null str = icont (step bfr) Nothing
| otherwise = case LL.break cpred str of
(str', tail')
| LL.null tail' -> icont (step (bfr `mappend` str)) Nothing
| otherwise -> idone (bfr `mappend` str') (Chunk tail')
step bfr stream = idone bfr stream
breakE
:: (LL.ListLike s el, NullPoint s)
=> (el -> Bool)
-> Enumeratee s s m a
breakE cpred = eneeCheckIfDonePass (icont . step)
where
step k (Chunk s)
| LL.null s = liftI (step k)
| otherwise = case LL.break cpred s of
(str', tail')
| LL.null tail' -> eneeCheckIfDonePass (icont . step) . k $ Chunk str'
| otherwise -> idone (k $ Chunk str') (Chunk tail')
step k stream = idone (liftI k) stream
takeStream ::
(Monad m, Nullable s, LL.ListLike s el)
=> Int
-> Enumeratee s s m a
takeStream n' iter
| n' <= 0 = return iter
| otherwise = Iteratee $ \od oc -> runIter iter (on_done od oc) (on_cont od oc)
where
on_done od oc x _ = runIter (dropStream n' >> return (return x)) od oc
on_cont od oc k Nothing = if n' == 0 then od (liftI k) (Chunk mempty)
else runIter (liftI (step n' k)) od oc
on_cont od oc _ (Just e) = runIter (dropStream n' >> throwErr e) od oc
step n k (Chunk str)
| LL.null str = liftI (step n k)
| LL.length str <= n = takeStream (n LL.length str) $ k (Chunk str)
| otherwise = idone (k (Chunk s1)) (Chunk s2)
where (s1, s2) = LL.splitAt n str
step _n k stream = idone (liftI k) stream
takeUpTo :: (Monad m, Nullable s, LL.ListLike s el) => Int -> Enumeratee s s m a
takeUpTo i iter
| i <= 0 = idone iter (Chunk emptyP)
| otherwise = Iteratee $ \od oc ->
runIter iter (onDone od oc) (onCont od oc)
where
onDone od oc x str = runIter (idone (return x) str) od oc
onCont od oc k Nothing = if i == 0 then od (liftI k) (Chunk mempty)
else runIter (liftI (step i k)) od oc
onCont od oc _ (Just e) = runIter (throwErr e) od oc
step n k (Chunk str)
| LL.null str = liftI (step n k)
| LL.length str < n = takeUpTo (n LL.length str) $ k (Chunk str)
| otherwise =
let (s1, s2) = LL.splitAt n str
in Iteratee $ \od' _ -> do
res <- runIter (k (Chunk s1)) (\a s -> return $ Left (a, s))
(\k' e -> return $ Right (k',e))
case res of
Left (a,Chunk s1') -> od' (return a)
(Chunk $ s1' `LL.append` s2)
Left (a,s') -> od' (idone a s') (Chunk s2)
Right (k',e) -> od' (icont k' e) (Chunk s2)
step _ k stream = idone (liftI k) stream
takeWhileE
:: (LL.ListLike s el, NullPoint s)
=> (el -> Bool)
-> Enumeratee s s m a
takeWhileE = breakE . (not .)
mapStream :: (LL.ListLike (s el) el, LL.ListLike (s el') el', NullPoint (s el))
=> (el -> el') -> Enumeratee (s el) (s el') m a
mapStream = mapChunks . LL.map
rigidMapStream
:: (LL.ListLike s el, NullPoint s)
=> (el -> el)
-> Enumeratee s s m a
rigidMapStream f = mapChunks (LL.rigidMap f)
concatMapStream :: (Monad m, LL.ListLike s a, NullPoint s) => (a -> t) -> Enumeratee s t m r
concatMapStream f = eneeCheckIfDone (liftI . go)
where
go k (EOF mx) = idone (liftI k) (EOF mx)
go k (Chunk xs) | LL.null xs = liftI (go k)
| otherwise = eneeCheckIfDone (flip go (Chunk (LL.tail xs))) . k . Chunk . f $ LL.head xs
concatMapStreamM :: (Monad m, LL.ListLike s a, NullPoint s) => (a -> m t) -> Enumeratee s t m r
concatMapStreamM f = eneeCheckIfDone (liftI . go)
where
go k (EOF mx) = idone (liftI k) (EOF mx)
go k (Chunk xs) | LL.null xs = liftI (go k)
| otherwise = f (LL.head xs) `mBind`
eneeCheckIfDone (flip go (Chunk (LL.tail xs))) . k . Chunk
mapMaybeStream :: (LL.ListLike s a, NullPoint s, LL.ListLike t b) => (a -> Maybe b) -> Enumeratee s t m r
mapMaybeStream f = mapChunks mm
where
mm l = if LL.null l then LL.empty else
case f (LL.head l) of Nothing -> mm (LL.tail l)
Just b -> LL.cons b $ mm (LL.tail l)
filterStream
:: (NullPoint s, LL.ListLike s el)
=> (el -> Bool)
-> Enumeratee s s m a
filterStream p = mapChunks (LL.filter p)
filterStreamM :: (Monad m, LL.ListLike s a, Nullable s) => (a -> m Bool) -> Enumeratee s s m r
filterStreamM k = mapChunksM (go id)
where
go acc s | LL.null s = return $! acc LL.empty
| otherwise = do p <- k (LL.head s)
let acc' = if p then LL.cons (LL.head s) . acc else acc
go acc' (LL.tail s)
groupStreamOn :: (Monad m, LL.ListLike l e, Eq t1, Nullable l)
=> (e -> t1)
-> (t1 -> m (Iteratee l m t2))
-> Enumeratee l [(t1, t2)] m a
groupStreamOn proj inner = eneeCheckIfDonePass (icont . step)
where
step outer (EOF mx) = idone (liftI outer) $ EOF mx
step outer c@(Chunk as)
| LL.null as = liftI $ step outer
| otherwise = let x = proj (LL.head as)
in lift (inner x) >>= \i -> step' x i outer c
step' c it outer (Chunk as)
| LL.null as = liftI $ step' c it outer
| (l,r) <- LL.span ((==) c . proj) as, not (LL.null l) =
let od a _str = idoneM a $ EOF Nothing
oc k Nothing = return $ k (Chunk l)
oc k m = icontM k m
in lift (runIter it od oc) >>= \it' -> step' c it' outer (Chunk r)
step' c it outer str =
lift (run it) >>= \b -> eneeCheckIfDone (`step` str) . outer $ Chunk [(c,b)]
groupStreamBy :: (Monad m, LL.ListLike l t, Nullable l)
=> (t -> t -> Bool)
-> m (Iteratee l m t2)
-> Enumeratee l [t2] m a
groupStreamBy cmp inner = eneeCheckIfDonePass (icont . step)
where
step outer (EOF mx) = idone (liftI outer) $ EOF mx
step outer c@(Chunk as)
| LL.null as = liftI $ step outer
| otherwise = lift inner >>= \i -> step' (LL.head as) i outer c
step' c it outer (Chunk as)
| LL.null as = liftI $ step' c it outer
| (l,r) <- LL.span (cmp c) as, not (LL.null l) =
let od a _str = idoneM a $ EOF Nothing
oc k Nothing = return $ k (Chunk l)
oc k m = icontM k m
in lift (runIter it od oc) >>= \it' -> step' (LL.head l) it' outer (Chunk r)
step' _ it outer str =
lift (run it) >>= \b -> eneeCheckIfDone (`step` str) . outer $ Chunk [b]
mergeStreams ::
(LL.ListLike s1 el1
,LL.ListLike s2 el2
,Nullable s1
,Nullable s2
,Monad m)
=> (el1 -> el2 -> b)
-> Enumeratee s2 b (Iteratee s1 m) a
mergeStreams f = convStream $ liftM2 f (lift headStream) headStream
mergeByChunks ::
(Nullable c2, Nullable c1
,LL.ListLike c1 el1, LL.ListLike c2 el2
, Monad m)
=> (c1 -> c2 -> c3)
-> (c1 -> c3)
-> (c2 -> c3)
-> Enumeratee c2 c3 (Iteratee c1 m) a
mergeByChunks f f1 f2 = unfoldConvStream iter (0 :: Int)
where
iter 1 = (\x -> (1,f1 x)) `liftM` lift getChunk
iter 2 = (\x -> (2,f2 x)) `liftM` getChunk
iter _ = do
ml1 <- lift chunkLength
ml2 <- chunkLength
case (ml1, ml2) of
(Just l1, Just l2) -> do
let tval = min l1 l2
c1 <- lift $ takeFromChunk tval
c2 <- takeFromChunk tval
return (0, f c1 c2)
(Just _, Nothing) -> iter 1
(Nothing, _) -> iter 2
foldStream
:: LL.ListLike s el
=> (a -> el -> a)
-> a
-> Iteratee s m a
foldStream f i = liftI (step i)
where
step acc (Chunk xs)
| LL.null xs = liftI (step acc)
| otherwise = liftI (step $! FLL.foldl' f acc xs)
step acc stream = idone acc stream
zipStreams
:: (Monad m, Nullable s, LL.ListLike s el)
=> Iteratee s m a
-> Iteratee s m b
-> Iteratee s m (a, b)
zipStreams x0 y0 = do
(a', x') <- lift $ runIter x0 od oc
(b', y') <- lift $ runIter y0 od oc
case checkDone a' b' of
Just (Right (a,b,s)) -> idone (a,b) s
Just (Left (Left a)) -> liftM (a,) y'
Just (Left (Right b)) -> liftM (,b) x'
Nothing -> liftI (step x' y')
where
step x y (Chunk xs) | nullC xs = liftI (step x y)
step x y (Chunk xs) = do
(a', x') <- lift $ (\i -> runIter i od oc) =<< enumPure1Chunk xs x
(b', y') <- lift $ (\i -> runIter i od oc) =<< enumPure1Chunk xs y
case checkDone a' b' of
Just (Right (a,b,s)) -> idone (a,b) s
Just (Left (Left a)) -> liftM (a,) y'
Just (Left (Right b)) -> liftM (,b) x'
Nothing -> liftI (step x' y')
step x y (EOF err) = joinIM $ case err of
Nothing -> (liftM2.liftM2) (,) (enumEof x) (enumEof y)
Just e -> (liftM2.liftM2) (,) (enumErr e x) (enumErr e y)
od a s = return (Just (a, s), idone a s)
oc k e = return (Nothing , icont k e)
checkDone r1 r2 = case (r1, r2) of
(Just (a, s1), Just (b,s2)) -> Just $ Right (a, b, shorter s1 s2)
(Just (a, _), Nothing) -> Just . Left $ Left a
(Nothing, Just (b, _)) -> Just . Left $ Right b
(Nothing, Nothing) -> Nothing
shorter c1@(Chunk xs) c2@(Chunk ys)
| LL.length xs < LL.length ys = c1
| otherwise = c2
shorter e@(EOF _) _ = e
shorter _ e@(EOF _) = e
zipStreams3
:: (Monad m, Nullable s, LL.ListLike s el)
=> Iteratee s m a -> Iteratee s m b
-> Iteratee s m c -> Iteratee s m (a, b, c)
zipStreams3 a b c = zipStreams a (zipStreams b c) >>=
\(r1, (r2, r3)) -> return (r1, r2, r3)
zipStreams4
:: (Monad m, Nullable s, LL.ListLike s el)
=> Iteratee s m a -> Iteratee s m b
-> Iteratee s m c -> Iteratee s m d
-> Iteratee s m (a, b, c, d)
zipStreams4 a b c d = zipStreams a (zipStreams3 b c d) >>=
\(r1, (r2, r3, r4)) -> return (r1, r2, r3, r4)
zipStreams5
:: (Monad m, Nullable s, LL.ListLike s el)
=> Iteratee s m a -> Iteratee s m b
-> Iteratee s m c -> Iteratee s m d
-> Iteratee s m e -> Iteratee s m (a, b, c, d, e)
zipStreams5 a b c d e = zipStreams a (zipStreams4 b c d e) >>=
\(r1, (r2, r3, r4, r5)) -> return (r1, r2, r3, r4, r5)
enumWith
:: (Monad m, Nullable s, LL.ListLike s el)
=> Iteratee s m a
-> Iteratee s m b
-> Iteratee s m (a, b)
enumWith i1 i2 = do
(a', x') <- lift $ runIter i1 od oc
(_, y') <- lift $ runIter i2 od oc
case a' of
Just (a, s) -> flip idone s =<< lift (liftM (a,) $ run i2)
Nothing -> go x' y'
where
od a s = return (Just (a, s), idone a s)
oc k e = return (Nothing , icont k e)
getUsed xs (Chunk ys) = LL.take (LL.length xs LL.length ys) xs
getUsed xs (EOF _) = xs
go x y = liftI step
where
step (Chunk xs) | nullC xs = liftI step
step (Chunk xs) = do
(a', x') <- lift $ (\i -> runIter i od oc) =<< enumPure1Chunk xs x
case a' of
Just (a, s) -> do
b <- lift $ run =<< enumPure1Chunk (getUsed xs s) y
idone (a, b) s
Nothing -> lift (enumPure1Chunk xs y) >>= go x'
step (EOF err) = joinIM $ case err of
Nothing -> (liftM2.liftM2) (,) (enumEof x) (enumEof y)
Just e -> (liftM2.liftM2) (,) (enumErr e x) (enumErr e y)
sequenceStreams_
:: (Monad m, LL.ListLike s el, Nullable s)
=> [Iteratee s m a]
-> Iteratee s m ()
sequenceStreams_ = self
where
self is = liftI step
where
step (Chunk xs) | LL.null xs = liftI step
step s@(Chunk _) = do
is' <- lift $ mapM (enumChunk s) is
(done, notDone) <- lift $ partition fst `liftM` mapM enumCheckIfDone is'
if null notDone
then idone () <=< remainingStream $ map snd done
else self $ map snd notDone
step s@(EOF _) = do
s' <- remainingStream <=< lift $ mapM (enumChunk s) is
case s' of
EOF (Just e) -> throwErr e
_ -> idone () s'
remainingStream
:: (Monad m, Nullable s, LL.ListLike s el)
=> [Iteratee s m a] -> Iteratee s m (Stream s)
remainingStream is = lift $
return . foldl1 shorter <=< mapM (\i -> runIter i od oc) $ is
where
od _ s = return s
oc _ e = return $ case e of
Nothing -> mempty
_ -> EOF e
shorter c1@(Chunk xs) c2@(Chunk ys)
| LL.length xs < LL.length ys = c1
| otherwise = c2
shorter (EOF e1 ) (EOF e2 ) = EOF (e1 `mplus` e2)
shorter e@(EOF _) _ = e
shorter _ e@(EOF _) = e
countConsumed :: forall a s el m n.
(Monad m, LL.ListLike s el, Nullable s, Integral n) =>
Iteratee s m a
-> Iteratee s m (a, n)
countConsumed i = go 0 (const i) (Chunk emptyP)
where
go :: n -> (Stream s -> Iteratee s m a) -> Stream s
-> Iteratee s m (a, n)
go !n f str@(EOF _) = (, n) `liftM` f str
go !n f str@(Chunk c) = Iteratee rI
where
newLen = n + fromIntegral (LL.length c)
rI od oc = runIter (f str) onDone onCont
where
onDone a str'@(Chunk c') =
od (a, newLen fromIntegral (LL.length c')) str'
onDone a str'@(EOF _) = od (a, n) str'
onCont f' mExc = oc (go newLen f') mExc
enumPureNChunk :: (Monad m, LL.ListLike s el) => s -> Int -> Enumerator s m a
enumPureNChunk str n iter
| LL.null str = return iter
| n > 0 = enum' str iter
| otherwise = error $ "enumPureNChunk called with n==" ++ show n
where
enum' str' iter'
| LL.null str' = return iter'
| otherwise = let (s1, s2) = LL.splitAt n str'
on_cont k Nothing = enum' s2 . k $ Chunk s1
on_cont k e = return $ icont k e
in runIter iter' idoneM on_cont
mapStreamM_ :: (Monad m, Nullable s, LL.ListLike s el) => (el -> m b) -> Iteratee s m ()
mapStreamM_ = mapChunksM_ . LL.mapM_
mapStreamM :: (Monad m, LL.ListLike (s el) el, LL.ListLike (s el') el', NullPoint (s el))
=> (el -> m el') -> Enumeratee (s el) (s el') m a
mapStreamM = mapChunksM . LL.mapM
foldStreamM :: (Monad m, Nullable s, LL.ListLike s a) => (b -> a -> m b) -> b -> Iteratee s m b
foldStreamM k = foldChunksM go
where
go b s | LL.null s = return b
| otherwise = k b (LL.head s) >>= \b' -> go b' (LL.tail s)