module Control.Concurrent.SCC.Combinators
(
(->>), (<<-),
(>->), join,
snot, (>&), (>|),
(&&), (||),
ifs, wherever, unless, select,
while, nestedIn,
foreach, having, havingOnly, followedBy, even,
first, uptoFirst, prefix,
last, lastAndAfter, suffix,
between, (...))
where
import Control.Concurrent.SCC.Foundation
import Control.Concurrent.SCC.ComponentTypes
import Prelude hiding (even, last, (||), (&&))
import qualified Prelude
import Control.Exception (assert)
import Control.Monad (liftM, when)
import qualified Control.Monad as Monad
import Data.Maybe (isJust, isNothing, fromJust)
import Data.Typeable (Typeable)
import qualified Data.Foldable as Foldable
import qualified Data.Sequence as Seq
import Data.Sequence (Seq, (|>), (><), ViewL (EmptyL, (:<)))
import Debug.Trace (trace)
infixr ->>
(->>) :: forall x y m r. (Monad m, Typeable x, Typeable y) => Transducer m x y -> Consumer m y r -> Consumer m x r
Transducer t ->> consumer = consumer'
where consumer' source = liftM snd $ pipeD "->>" (t source) consumer
(<<-) :: forall x y m r c c1. (Monad m, Typeable x, Typeable y) => Transducer m x y -> Producer m x r -> Producer m y r
Transducer t <<- producer = producer'
where producer' sink = liftM fst $ pipeD "<<-" producer (flip t sink)
(>->) :: forall m x y z. Monad m => Transducer m x y -> Transducer m y z -> Transducer m x z
Transducer t1 >-> Transducer t2 = Transducer t
where t source sink = liftM fst $ pipeD ">->" (t1 source) (flip t2 sink)
join :: (Monad m, Typeable x) => Transducer m x y -> Transducer m x y -> Transducer m x y
join (Transducer t1) (Transducer t2) = Transducer t
where t source sink = do (((), l), extra) <- pipeD "join 1"
(\sink1-> pipeD "join 2" (\sink2-> tee source sink1 sink2) getList)
(flip t1 sink)
pipeD "join 3" (putList l) (flip t2 sink)
return extra
snot :: (Monad m, Typeable x) => Splitter m x -> Splitter m x
snot splitter = liftSectionSplitter (\source true false-> splitSections splitter source false true)
(>&) :: (Monad m, Typeable x) => Splitter m x -> Splitter m x -> Splitter m x
s1 >& s2 = liftSimpleSplitter (\source true false->
liftM fst $ pipeD ">&" (\true-> split s1 source true false) (\source-> split s2 source true false))
(>|) :: (Monad m, Typeable x) => Splitter m x -> Splitter m x -> Splitter m x
s1 >| s2 = liftSimpleSplitter (\source true false->
liftM fst $ pipeD ">|" (split s1 source true) (\source-> split s2 source true false))
(&&) :: (Monad m, Typeable x) => Splitter m x -> Splitter m x -> Splitter m x
(&&) = zipSplittersWith (Prelude.&&)
(||) :: (Monad m, Typeable x) => Splitter m x -> Splitter m x -> Splitter m x
(||) = zipSplittersWith (Prelude.||)
ifs :: (Monad m, Typeable x) => Splitter m x -> Transducer m x y -> Transducer m x y -> Transducer m x y
ifs s (Transducer t1) (Transducer t2) = Transducer t
where t source sink = liftM fst3 $ splitConsumer "ifs" s (flip t1 sink) (flip t2 sink) source
wherever :: (Monad m, Typeable x) => Transducer m x x -> Splitter m x -> Transducer m x x
wherever (Transducer t) s = Transducer wherever'
where wherever' source sink = liftM fst3 $ splitConsumer "wherever" s (flip t sink) (flip pour sink) source
unless :: (Monad m, Typeable x) => Transducer m x x -> Splitter m x -> Transducer m x x
unless (Transducer t) s = Transducer unless'
where unless' source sink = liftM fst3 $ splitConsumer "unless" s (flip pour sink) (flip t sink) source
select :: (Monad m, Typeable x) => Splitter m x -> Transducer m x x
select s = Transducer (\source sink-> liftM fst3 $ splitConsumer "select" s (flip pour sink) consumeAndSuppress source)
while :: (Monad m, Typeable x) => Transducer m x x -> Splitter m x -> Transducer m x x
while t s = Transducer while'
where while' source sink = liftM fst3 $ splitConsumer "while" s (t ->> while t s ->> flip pour sink) (flip pour sink) source
nestedIn :: (Monad m, Typeable x) => Splitter m x -> Splitter m x -> Splitter m x
nestedIn s1 s2 = s
where s = liftSimpleSplitter (\source true false->
liftM fst $
pipe (\false-> split s1 source true false)
(\source-> pipe (\true-> split s2 source true false)
(\source-> split (nestedIn s1 s2) source true false)))
foreach :: (Monad m, Typeable x, Typeable y) => Splitter m x -> Transducer m x y -> Transducer m x y -> Transducer m x y
foreach s t1 t2 = Transducer t
where t source sink = liftM fst $
pipeD "foreach"
(transduce (splitterToMarker s) source)
(\source-> groupMarks source (\b chunk-> transduce (if b then t1 else t2) chunk sink))
having :: (Monad m, Typeable x) => Splitter m x -> Splitter m x -> Splitter m x
having s1 s2 = liftSectionSplitter s
where s source true false = liftM fst $
pipeD "having"
(transduce (splitterToMarker s1) source)
(\source-> groupMarks source (\b chunk-> if b then test chunk else pourMaybe chunk false))
where test chunk = pipe (\sink1-> pipe (\sink2-> tee chunk sink1 sink2) getList)
(\chunk-> pipe (\sink-> suppressProducer (split s2 chunk sink)) getList)
>>= \(((), chunk), (_, truePart))-> let chunk' = if null chunk
then [Nothing]
else map Just chunk
in (if null truePart
then putList chunk' false
else putList chunk' true)
>> return ()
havingOnly :: (Monad m, Typeable x) => Splitter m x -> Splitter m x -> Splitter m x
havingOnly s1 s2 = liftSectionSplitter s
where s source true false = liftM fst $
pipeD "havingOnly"
(transduce (splitterToMarker s1) source)
(\source-> groupMarks source (\b chunk-> if b then test chunk else pourMaybe chunk false))
where test chunk = pipe (\sink1-> pipe (\sink2-> tee chunk sink1 sink2) getList)
(\chunk-> pipe (\sink-> suppressProducer (\suppress-> split s2 chunk suppress sink))
getList)
>>= \(((), chunk), (_, falsePart))-> let chunk' = if null chunk
then [Nothing]
else map Just chunk
in (if null falsePart
then putList chunk' true
else putList chunk' false)
>> return ()
first :: (Monad m, Typeable x) => Splitter m x -> Splitter m x
first splitter = liftSectionSplitter s
where s source true false = liftM (\(x, y)-> y ++ x) $
pipeD "first" (transduce (splitterToMarker splitter) source)
(\source-> let get1 (x, False) = p false x get1
get1 (x, True) = p true x get2
get2 (x, True) = p true x get2
get2 (x, False) = p false x get3
get3 (x, _) = p false x get3
p sink x succeed = put sink x
>>= cond (get source >>= maybe (return []) succeed)
(return $ maybe [] (:[]) x)
in get source >>= maybe (return []) get1)
uptoFirst :: (Monad m, Typeable x) => Splitter m x -> Splitter m x
uptoFirst splitter = liftSectionSplitter s
where s source true false = liftM (\(x, y)-> concatMap (maybe [] (:[])) y ++ x) $
pipeD "uptoFirst" (transduce (splitterToMarker splitter) source)
(\source-> let get1 q (x, False) = let q' = q |> x
in get source
>>= maybe
(putQueue q' false)
(get1 q')
get1 q p@(x, True) = do rest <- putQueue q true
if null rest then get2 p else return rest
get2 (x, True) = p true x get2
get2 (x, False) = p false x get3
get3 (x, _) = p false x get3
p sink x succeed = put sink x
>>= cond (get source >>= maybe (return []) succeed)
(return [x])
in get source >>= maybe (return []) (get1 Seq.empty))
last :: (Monad m, Typeable x) => Splitter m x -> Splitter m x
last splitter = liftSectionSplitter s
where s source true false = liftM (\(x, y)-> concatMap (maybe [] (:[])) y ++ x) $
pipeD "last" (transduce (splitterToMarker splitter) source)
(\source-> let get1 (x, False) = put false x
>>= cond (get source >>= maybe (return []) get1)
(return [x])
get1 p@(x, True) = get2 Seq.empty p
get2 q (x, True) = let q' = q |> x
in get source
>>= maybe
(putQueue q' true)
(get2 q')
get2 q p@(x, False) = get3 q Seq.empty p
get3 qt qf (x, False) = let qf' = qf |> x
in get source
>>= maybe
(putQueue qt true >> putQueue qf' false)
(get3 qt qf')
get3 qt qf p@(x, True) = do rest1 <- putQueue qt false
rest2 <- putQueue qf false
if null rest1 Prelude.&& null rest2
then get2 Seq.empty p
else return (rest1 ++ rest2)
p succeed = get source >>= maybe (return []) succeed
in p get1)
lastAndAfter :: (Monad m, Typeable x) => Splitter m x -> Splitter m x
lastAndAfter splitter = liftSectionSplitter s
where s source true false = liftM (\(x, y)-> concatMap (maybe [] (:[])) y ++ x) $
pipeD "lastAndAfter" (transduce (splitterToMarker splitter) source)
(\source-> let get1 (x, False) = put false x >>= cond (p get1) (return [x])
get1 p@(x, True) = get2 Seq.empty p
get2 q (x, True) = let q' = q |> x
in get source
>>= maybe
(putQueue q' true)
(get2 q')
get2 q p@(x, False) = get3 q p
get3 q (x, False) = let q' = q |> x
in get source
>>= maybe
(putQueue q' true)
(get3 q')
get3 q p@(x, True) = putQueue q false >>= whenNull (get1 p)
p succeed = get source >>= maybe (return []) succeed
in p get1)
prefix :: (Monad m, Typeable x) => Splitter m x -> Splitter m x
prefix splitter = liftSectionSplitter s
where s source true false = liftM (\(x, y)-> y ++ x) $
pipeD "prefix" (transduce (splitterToMarker splitter) source)
(\source-> let get1 (x, False) = p false x get2
get1 (x, True) = p true x get1
get2 (x, _) = p false x get2
p sink x succeed = put sink x
>>= cond (get source >>= maybe (return []) succeed)
(return $ maybe [] (:[]) x)
in get source >>= maybe (return []) get1)
suffix :: (Monad m, Typeable x) => Splitter m x -> Splitter m x
suffix splitter = liftSectionSplitter s
where s source true false = liftM (\(x, y)-> concatMap (maybe [] (:[])) y ++ x) $
pipeD "suffix" (transduce (splitterToMarker splitter) source)
(\source-> let get1 (x, False) = put false x >>= cond (p get1) (return [x])
get1 (x, True) = get2 (Seq.singleton x)
get2 q = get source
>>= maybe (putQueue q true) (get3 q)
get3 q (x, True) = get2 (q |> x)
get3 q p@(x, False) = putQueue q false >>= whenNull (get1 p)
p succeed = get source >>= maybe (return []) succeed
in p get1)
even :: (Monad m, Typeable x) => Splitter m x -> Splitter m x
even splitter = liftSectionSplitter s
where s source true false = liftM (\(x, y)-> concatMap (maybe [] (:[])) y ++ x) $
pipeD "even"
(transduce (splitterToMarker splitter) source)
(\source-> let get1 (x, False) = put false x
>>= cond (get source >>= maybe (return []) get1)
(return [x])
get1 p@(x, True) = get2 p
get2 (x, True) = put false x
>>= cond (get source >>= maybe (return []) get2)
(return [x])
get2 p@(x, False) = get3 p
get3 (x, False) = put false x
>>= cond (get source >>= maybe (return []) get3)
(return [x])
get3 p@(x, True) = get4 p
get4 (x, True) = put true x
>>= cond (get source >>= maybe (return []) get4)
(return [x])
get4 p@(x, False) = get1 p
in get source >>= maybe (return []) get1)
followedBy :: forall m x. (Monad m, Typeable x) => Splitter m x -> Splitter m x -> Splitter m x
followedBy s1 s2 = liftSectionSplitter s
where s source true false
= liftM (\(x, y)-> concatMap (maybe [] (:[])) y ++ x) $
pipeD "followedBy"
(transduce (splitterToMarker s1) source)
(\source-> let get0 q = case Seq.viewl q
of Seq.EmptyL -> get source >>= maybe (return []) get1
(x, False) :< rest -> put false x
>>= cond (get0 rest)
(return $ Foldable.toList $ Seq.viewl $ fmap fst q)
(x, True) :< rest -> get2 Seq.empty q
get1 (x, False) = put false x
>>= cond (get source >>= maybe (return []) get1)
(return [x])
get1 p@(x, True) = get2 Seq.empty (Seq.singleton p)
get2 q q' = case Seq.viewl q'
of Seq.EmptyL -> get source
>>= maybe (testEnd q) (get2 q . Seq.singleton)
(x, True) :< rest -> get2 (q |> x) rest
(x, False) :< rest -> do ((q1, q2), n) <- pipeD "followedBy tail"
(get3 Seq.empty q') (test q)
case n of Nothing -> putQueue q false
>>= whenNull (get0 (q1 >< q2))
Just n -> do put false Nothing
get0 (dropJust n q1 >< q2)
get3 q1 q2 sink = canPut sink
>>= cond (case Seq.viewl q2
of Seq.EmptyL -> get source
>>= maybe (return (q1, q2))
(\p-> maybe (return True) (put sink) (fst p)
>> get3 (q1 |> p)