packages feed

scc 0.1 → 0.2

raw patch · 9 files changed

+2676/−1788 lines, 9 filesdep +mtldep +paralleldep ~parsec

Dependencies added: mtl, parallel

Dependency ranges changed: parsec

Files

Control/Concurrent/SCC/Combinators.hs view
@@ -14,652 +14,1080 @@     <http://www.gnu.org/licenses/>. -} --- | The "Combinators" module defines combinators applicable to 'Transducer' and 'Splitter' components defined in the--- "ComponentTypes" module.--{-# LANGUAGE ScopedTypeVariables, Rank2Types #-}--module Control.Concurrent.SCC.Combinators-   (-- * Consumer and producer combinators-    (->>), (<<-),-    -- * Transducer combinators-    (>->), join,-    -- * Pseudo-logic splitter combinators-    -- | Combinators '>&' and '>|' are only /pseudo/-logic. While the laws of double negation and De Morgan's laws hold,-    -- '>&' and '>|' are in general not commutative, associative, nor idempotent. In the special case when all argument-    -- splitters are stateless, such as those produced by 'Components.liftStatelessSplitter', these combinators do satisfy-    -- all laws of Boolean algebra.-    snot, (>&), (>|),-    -- ** Zipping logic combinators-    -- | The '&&' and '||' combinators run the argument splitters in parallel and combine their logical outputs using-    -- the corresponding logical operation on each output pair, in a manner similar to 'Prelude.zipWith'.-    (&&), (||),-    -- * Flow-control combinators-    -- | The following combinators resemble the common flow-control programming language constructs. Combinators -    -- 'wherever', 'unless', and 'select' are just the special cases of the combinator 'ifs'.-    ---    --    * /transducer/ ``wherever`` /splitter/ = 'ifs' /splitter/ /transducer/ 'Components.asis'-    ---    --    * /transducer/ ``unless`` /splitter/ = 'ifs' /splitter/ 'Components.asis' /transducer/-    ---    --    * 'select' /splitter/ = 'ifs' /splitter/ 'Components.asis' 'Components.suppress'-    ---    ifs, wherever, unless, select,-    -- ** Recursive-    while, nestedIn,-    -- * Section-based combinators-    -- | All combinators in this section use their 'Splitter' argument to determine the-    -- structure of the input. Every contiguous portion of the input that gets passed to one or the other sink of the-    -- splitter is treated as one section in the logical structure of the input stream. What is done with the section-    -- depends on the combinator, but the sections, and therefore the logical structure of the input stream, are-    -- determined by the argument splitter alone.-    foreach, having, havingOnly, followedBy, even,-    -- ** first and its variants-    first, uptoFirst, prefix,-    -- ** last and its variants-    last, lastAndAfter, suffix,-    -- ** input ranges-    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 ->>---- | The result of combinator '->>' is a consumer that acts as a composition of the given transducer and consumer--- arguments.-(->>) :: 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---- | The result of combinator '<<-' is a producer that acts as a composition of the given transducer and producer--- arguments.-(<<-) :: 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)----- | The '>->' combinator composes its argument transducers. The resulting composition /t1 >-> t2/ passes its input through the--- first transducer /t1/, the output of /t1/ is passed to the other transducer /t2/, and its output becomes the output of the--- composition.-(>->) :: 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)---- | The 'join' combinator arranges the two transducer arguments in parallel. The input of the resulting transducer is replicated--- to both component transducers in parallel, and the output of the resulting transducer is a concatenation of the two component--- transducers' outputs.-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--- | The 'snot' (streaming not) combinator simply reverses the outputs of the argument splitter.--- In other words, data that the argument splitter sends to its /true/ sink goes to the /false/ sink of the result, and vice versa.-snot :: (Monad m, Typeable x) => Splitter m x -> Splitter m x-snot splitter = liftSectionSplitter (\source true false-> splitSections splitter source false true)---- | The '>&' combinator sends the /true/ sink output of its left operand to the input of its right operand for further--- splitting. Both operands' /false/ sinks are connected to the /false/ sink of the combined splitter, but any input--- value to reach the /true/ sink of the combined component data must be deemed true by both splitters.-(>&) :: (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))---- | A '>|' combinator's input value can reach its /false/ sink only by going through both argument splitters' /false/--- sinks.-(>|) :: (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))---- | Combinator '&&' is a pairwise logical conjunction of two splitters run in parallel on the same input.-(&&) :: (Monad m, Typeable x) => Splitter m x -> Splitter m x -> Splitter m x-(&&) = zipSplittersWith (Prelude.&&)---- | Combinator '||' is a pairwise logical disjunction of two splitters run in parallel on the same input.-(||) :: (Monad m, Typeable x) => Splitter m x -> Splitter m x -> Splitter m x-(||) = zipSplittersWith (Prelude.||)---- | The result of the combinator 'ifs' is a transducer that applies one argument transducer to one portion of--- the input and the other transducer to the other portion of input, depending on where the splitter argument routes the data.-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)---- | The recursive combinator 'while' feeds the true sink of the argument splitter back to itself, modified by the--- argument transducer. Data fed to the splitter's false sink is passed on unmodified.-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---- | The recursive combinator 'nestedIn' combines two splitters into a mutually recursive loop acting as a single splitter.--- The true  sink of one of the argument splitters and false sink of the other become the true and false sinks of the loop.--- The other two sinks are bound to the other splitter's source.--- The use of 'nestedIn' makes sense only on hierarchically structured streams. If we gave it some input containing--- a flat sequence of values, and assuming both component splitters are deterministic and stateless,--- a value would either not loop at all or it would loop forever.-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)))---- | The 'foreach' combinator is similar to the combinator 'ifs' in that it combines a splitter and two transducers into--- another transducer. However, in this case the transducers are re-instantiated for each consecutive portion of the--- input as the splitter chunks it up. Each contiguous portion of the input that the splitter sends to one of its two--- sinks gets transducered through the appropriate argument transducer as that transducer's whole input. As soon as the--- contiguous portion is finished, the transducer gets terminated.-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))---- | The 'having' combinator combines two pure splitters into a pure splitter. One splitter is used to chunk the input--- into contiguous portions. Its /false/ sink is routed directly to the /false/ sink of the combined splitter. The--- second splitter is instantiated and run on each portion of the input that goes to first splitter's /true/ sink. If--- the second splitter sends any output at all to its /true/ sink, the whole input portion is passed on to the /true/--- sink of the combined splitter, otherwise it goes to its /false/ 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 ()---- | The 'havingOnly' combinator is analogous to the 'having' combinator, but it succeeds and passes each chunk of the--- input to its /true/ sink only if the second splitter sends no part of it to its /false/ sink.-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 ()---- | The result of combinator 'first' behaves the same as the argument splitter up to and including the first portion of--- the input which goes into the argument's /true/ sink. All input following the first true portion goes into the--- /false/ sink.-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)---- | The result of combinator 'uptoFirst' takes all input up to and including the first portion of the input which goes--- into the argument's /true/ sink and feeds it to the result splitter's /true/ sink. All the rest of the input goes--- into the /false/ sink. The only difference between 'last' and 'lastAndAfter' combinators is in where they direct the--- /false/ portion of the input preceding the first /true/ part.-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))---- | The result of the combinator 'last' is a splitter which directs all input to its /false/ sink, up to the last--- portion of the input which goes to its argument's /true/ sink. That portion of the input is the only one that goes to--- the resulting component's /true/ sink.  The splitter returned by the combinator 'last' has to buffer the previous two--- portions of its input, because it cannot know if a true portion of the input is the last one until it sees the end of--- the input or another portion succeeding the previous one.-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)---- | The result of the combinator 'lastAndAfter' is a splitter which directs all input to its /false/ sink, up to the--- last portion of the input which goes to its argument's /true/ sink. That portion and the remainder of the input is fed--- to the resulting component's /true/ sink. The difference between 'last' and 'lastAndAfter' combinators is where they--- feed the /false/ portion of the input, if any, remaining after the last /true/ part.-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)---- | The 'prefix' combinator feeds its /true/ sink only the prefix of the input that its argument feeds to its /true/ sink.--- All the rest of the input is dumped into the /false/ sink of the result.-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)---- | The 'suffix' combinator feeds its /true/ sink only the suffix of the input that its argument feeds to its /true/ sink.--- All the rest of the input is dumped into the /false/ sink of the result.-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)---- | The 'even' combinator takes every input section that its argument splitters deems /true/, and feeds even ones into--- its /true/ sink. The odd sections and parts of input that are /false/ according to its argument splitter are fed to--- 'even' splitter's /false/ sink.-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)---- | Combinator 'followedBy' treats its argument 'Splitter's as patterns components and returns a 'Splitter' that--- matches their concatenation. A section of input is considered /true/ by the result iff its prefix is considered--- /true/ by argument /s1/ and the rest of the section is considered /true/ by /s2/. The splitter /s2/ is started anew--- after every section split to /true/ sink by /s1/.-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) q2 sink)-                                                               p :< rest -> maybe (return True) (put sink) (fst p)-                                                                            >> get3 (q1 |> p) rest sink)-                                                           (return (q1, q2))-                                testEnd q = do ((), n) <- pipeD "testEnd" (const $ return ()) (test q)-                                               case n of Nothing -> putQueue q false-                                                         _ -> return []-                                test q source = liftM snd $-                                                pipeD "follower"-                                                   (transduce (splitterToMarker s2) source)-                                                   (\source-> let get4 (_, False) = return Nothing-                                                                  get4 p@(_, True) = putQueue q true >> get5 0 p-                                                                  get5 n (x, False) = return (Just n)-                                                                  get5 n (Nothing, True) = get6 n-                                                                  get5 n (x, True) = put true x >> get6 (succ n)-                                                                  get6 n = get source-                                                                           >>= maybe-                                                                                  (return $ Just n)-                                                                                  (get5 n)-                                                              in get source >>= maybe (return Nothing) get4)-                                dropJust 0 q = q-                                dropJust n q = case Seq.viewl q of (Nothing, _) :< rest -> dropJust n rest-                                                                   (Just _, _) :< rest -> dropJust (pred n) rest-                           in get0 Seq.empty)---- | Combinator 'between' passes to its /true/ sink all input that follows a section considered true by its first--- argument splitter but not a section considered true by its second argument. The section delimiter pairs can nest to--- arbitrary depth.-between :: forall m x. (Monad m, Typeable x) => Splitter m x -> Splitter m x -> Splitter m x-between s1 s2 = liftSectionSplitter s-   where s source true false = liftM (\(x, y)-> concatMap (maybe [] (:[])) y ++ x) $-                               pipeD "between"-                                  (transduce (pairMarkerToMaybePairMarker $ splittersToPairMarker s1 s2) source)-                                  (\source-> let next state = get source >>= maybe (return []) state-                                                 pass sink x state = put sink x >>= cond (next state) (return [x])-                                                 state0 t@(x, True, False) = state1 t-                                                 state0 (x, _, _) = pass false x state0-                                                 state1 t@(x, _, True) = state0 t-                                                 state1 (x, True, False) = pass false x state1-                                                 state1 t@(x, False, False) = state2 1 t-                                                 state2 n (x, False, False) = pass true x (state2 n)-                                                 state2 n t@(x, _, True) = state4 (pred n) t-                                                 state2 n t@(x, True, False) = state3 (succ n) t-                                                 state3 n (x, True, _) = pass true x (state3 n)-                                                 state3 n t@(x, False, False) = state2 n t-                                                 state3 n t@(x, False, True) = state4 (pred n) t-                                                 state4 0 t = state0 t-                                                 state4 n (x, _, True) = pass true x (state4 n)-                                                 state4 n t@(x, True, False) = state3 (succ n) t-                                                 state4 n t@(x, False, False) = state2 n t-                                             in next state0)---- | Combinator '...' is similar to 'between', except it passes to /true/ the delimiting sections as well--- as all input between them.-(...) :: forall m x. (Monad m, Typeable x) => Splitter m x -> Splitter m x -> Splitter m x-s1 ... s2 = liftSectionSplitter s-   where s source true false = liftM (\(x, y)-> concatMap (maybe [] (:[])) y ++ x) $-                               pipeD "..."-                                  (transduce (pairMarkerToMaybePairMarker $ splittersToPairMarker s1 s2) source)-                                  (\source-> let next state = get source >>= maybe (return []) state-                                                 pass sink x state = put sink x >>= cond (next state) (return [x])-                                                 state0 (x, False, _) = pass false x state0-                                                 state0 t@(x, True, _) = state1 1 t-                                                 state1 0 t = state0 t-                                                 state1 n (x, True, False) = pass true x (state1 n)-                                                 state1 n t@(x, False, False) = state2 n t-                                                 state1 n t@(x, _, True) = state3 (pred n) t-                                                 state2 n (x, False, False) = pass true x (state2 n)-                                                 state2 n t@(x, _, True) = state3 (pred n) t-                                                 state2 n t@(x, True, False) = state1 (succ n) t-                                                 state3 n (x, _, True) = pass true x (state3 n)-                                                 state3 n t@(x, True, False) = put false Nothing >> state1 (succ n) t-                                                 state3 0 t@(x, False, False) = state0 t-                                                 state3 n t@(x, False, False) = state2 n t-                                             in next state0)---- Helper functions--type Marker m x = Transducer m x (Maybe x, Bool)--splitterToMarker :: forall m x. (Monad m, Typeable x) => Splitter m x -> Marker m x-splitterToMarker s = Transducer t-   where t source sink = liftM (\((x, y), z)-> z ++ y ++ x) $-                         pipeD "splitterToMarker true"-                            (\trueSink-> pipeD "splitterToMarker false" (splitSections s source trueSink) (mark False))-                            (mark True)-            where mark b source = canPut sink-                                  >>= cond (get source-                                            >>= maybe (return [])-                                                      (\x-> put sink (x, b) >>= cond (mark b source) (return $ maybe [] (: []) x)))-                                           (return [])---splittersToPairMarker :: forall m x. (Monad m, Typeable x)-                         => Splitter m x -> Splitter m x -> Transducer m x (Either (x, Bool, Bool) (Either Bool Bool))-splittersToPairMarker s1 s2 = Transducer t-   where t source sink = liftM (\((((((((), l1), l2), l3), l4), l5), l6), l7)-> l7 ++ l6 ++ l5 ++ l4 ++ l3 ++ l2 ++ l1) $-                         pipeD "splittersToMarker synchronize"-                         (\sync->-                          pipeD "splittersToMarker true1"-                          (\true1->-                           pipeD "splittersToMarker false1"-                           (\false1->-                            pipeD "splitterssToMarker true2"-                            (\true2->-                             pipeD "splittersToMarker false2"-                             (\false2->-                              pipeD "splittersToMarker sink1"-                              (\sink1->-                               pipeD "splittersToMarker sink2"-                               (\sink2-> tee source sink1 sink2)-                               (\source2-> splitSections s2 source2 true2 false2))-                              (\source1-> splitSections s1 source1 true1 false1))-                             (mark sync False False))-                            (mark sync False True))-                           (mark sync True False))-                          (mark sync True True))-                         (synchronizeMarks Nothing)-            where synchronizeMarks :: Maybe (Seq (x, Bool), Bool) -> Source c (Maybe x, Bool, Bool) -> Pipe c m [x]-                  synchronizeMarks state source-                     = get source---                       >>= \t-> trace (show t ++ "@" ++ show state) (return t)-                       >>= maybe-                              (assert (isNothing state) (return []))-                              (\(x, pos, b) ->-                                  maybe-                                     (put sink (Right $ if pos then Left b else Right b)-                                      >> synchronizeMarks state source)-                                     (\x-> case state-                                           of Nothing -> synchronizeMarks (Just (Seq.singleton (x, b), pos)) source-                                              Just (q, pos') -> if pos == pos'-                                                                then synchronizeMarks (Just (q |> (x, b), pos')) source-                                                                else case Seq.viewl q-                                                                     of Seq.EmptyL -> synchronizeMarks-                                                                                         (Just (Seq.singleton (x, b), pos))-                                                                                         source-                                                                        (y, b') :< rest -> put sink (Left $ if pos-                                                                                                            then (x, b, b')-                                                                                                            else (x, b', b))-                                                                                           >>= cond-                                                                                                  (synchronizeMarks-                                                                                                     (if Seq.null rest-                                                                                                      then Nothing-                                                                                                      else Just (rest, pos'))-                                                                                                     source)-                                                                                                  (returnQueuedList q))-                                     x)-         returnQueuedList q = return $ map fst $ Foldable.toList $ Seq.viewl q-         mark sink first b source = let mark' = canPut sink-                                                >>= cond-                                                       (get source-                                                        >>= maybe-                                                               (return [])-                                                               (\x-> put sink (x, first, b)-                                                                        >>= cond mark' (return $ maybe [] (: []) x)))-                                                       (return [])-                                    in mark'--pairMarkerToMaybePairMarker :: forall m x. (Monad m, Typeable x)-                               => Transducer m x (Either (x, Bool, Bool) (Either Bool Bool)) -> Transducer m x (Maybe x, Bool, Bool)-pairMarkerToMaybePairMarker t = Transducer t'-   where t' source sink = liftM (\(x, y)-> y ++ x) $-                          pipeD "pairMarkerToMaybePairMarker"-                             (transduce t source)-                             (\source-> let next state = get source >>= maybe (return []) state-                                            nextState2 l r d = get source-                                                               >>= maybe (put sink (Nothing, l, r) >> return []) (state2 l r d)-                                            state0 (Left (x, l, r)) = put sink (Just x, l, r)-                                                                      >>= cond (next $ state1 l r) (return [x])-                                            state0 v@(Right d) = state2 False False d v-                                            state1 _ _ (Left (x, l, r)) = put sink (Just x, l, r)-                                                                          >>= cond (next $ state1 l r) (return [x])-                                            state1 l r v@(Right d) = state2 l r d v-                                            state2 l r Left{} (Right d@(Left l')) = nextState2 l' r d-                                            state2 l r Left{} (Right (Right r')) = put sink (Nothing, l, r')-                                                                                   >>= cond (next $ state1 l r') (return [])-                                            state2 l r Left{} t@(Left (x, l', r')) | l == l' = state1 l r t-                                                                                   | otherwise = put sink (Nothing, l, r)-                                                                                                 >>= cond-                                                                                                        (state1 l' r' t)-                                                                                                        (return [])-                                            state2 l r Right{} (Right d@(Right r')) = nextState2 l r' d-                                            state2 l r Right{} (Right (Left l')) = put sink (Nothing, l', r)-                                                                                   >>= cond (next $ state1 l' r) (return [])-                                            state2 l r Right{} t@(Left (x, l', r')) | r == r' = state1 l r t-                                                                                    | otherwise = put sink (Nothing, l, r)-                                                                                                  >>= cond-                                                                                                         (state1 l' r' t)-                                                                                                         (return [])-                                        in next state0)--zipSplittersWith :: (Monad m, Typeable x) => (Bool -> Bool -> Bool) -> Splitter m x -> Splitter m x -> Splitter m x-zipSplittersWith f s1 s2-   = liftSectionSplitter (\source true false->-                          liftM (\(x, y)-> y ++ x) $-                          pipeD "&"-                             (transduce (pairMarkerToMaybePairMarker $ splittersToPairMarker s1 s2) source)-                             (\source-> let split = get source >>= maybe (return []) test-                                            test (x, b1, b2) = (if f b1 b2 then put true x else put false x)-                                                               >>= cond split (return $ maybe [] (:[]) x)-                                        in split))--groupMarks :: forall c1 c m x y z. (Monad m, Typeable x, Typeable y, Eq y)-              => Source c1 (Maybe x, y) -> (y -> Consumer m x z) -> Pipe c m ()-groupMarks source getConsumer = getSuccess source startNew-   where startNew (mx, y) = do (nextPair, _) <- pipeD "groupMarks" (\sink-> pass sink mx y) (getConsumer y)-                               case nextPair of Just p -> startNew p-                                                Nothing -> return ()-         pass sink Nothing y = next sink y-         pass sink (Just x) y = put sink x >> next sink y-         next sink y = get source >>= maybe (return Nothing) (continue sink y)-         continue sink y (x, y') | y == y' = pass sink x y-         continue sink y p@(x, y') | y /= y' = return (Just p)--splitConsumer :: forall x m r1 r2 c c1. (Monad m, Typeable x)-                 => String -> Splitter m x -> Consumer m x r1 -> Consumer m x r2 -> Source c1 x -> Pipe c m ([x], r1, r2)-splitConsumer description s trueConsumer falseConsumer = consumer'-   where consumer' source = pipeD (description ++ " false")-                               (\false-> pipeD (description ++ " true") (\true-> split s source true false) trueConsumer)-                               falseConsumer-                            >>= \((extra, r1), r2)-> return (extra, r1, r2)--splitConsumerSections :: forall x m r1 r2 c c1. (Monad m, Typeable x)-                         => String -> Splitter m x -> Consumer m (Maybe x) r1 -> Consumer m (Maybe x) r2 -> Source c1 x-                                   -> Pipe c m ([x], r1, r2)-splitConsumerSections description s trueConsumer falseConsumer = consumer'-   where consumer' source = pipeD (description ++ " false")-                               (\false-> pipeD (description ++ " true") (\true-> splitSections s source true false) trueConsumer)-                               falseConsumer-                            >>= \((extra, r1), r2)-> return (extra, r1, r2)--putQueue :: forall context r m x. (Monad m, Typeable x) => Seq x -> Sink context x -> Pipe r m [x]-putQueue q sink = putList (Foldable.toList (Seq.viewl q)) sink--getQueue :: forall x c c1 m. (Monad m, Typeable x) => Source c1 x -> Pipe c m (Seq x)-getQueue source = let getOne q = get source >>= maybe (return q) (\x-> getOne (q |> x))-                  in getOne Seq.empty--pourMaybe :: forall c c1 c2 x m. (Monad m, Typeable x) => Source c1 x -> Sink c2 (Maybe x) -> Pipe c m ()-pourMaybe source sink = pour0-   where pour0 = canPut sink >>= flip when (get source >>= maybe (put sink Nothing >> return ()) pass)-         pour1 = canPut sink >>= flip when (getSuccess source pass)-         pass x = put sink (Just x) >> pour1---suppressProducer :: forall x c m r. (Monad m, Typeable x) => Producer m x r -> Pipe c m r-suppressProducer producer = liftM fst $ pipeD "suppress" producer consumeAndSuppress+{-# LANGUAGE ScopedTypeVariables, Rank2Types, KindSignatures, EmptyDataDecls,+             MultiParamTypeClasses, FunctionalDependencies, FlexibleContexts, FlexibleInstances #-}++-- | The "Combinators" module defines combinators applicable to 'Transducer' and 'Splitter' components defined in the+-- "ComponentTypes" module.++module Control.Concurrent.SCC.Combinators+   (-- * Consumer, producer, and transducer combinators+    consumeBy, prepend, append, substitute,+    PipeableComponentPair ((>->)), JoinableComponentPair (join, sequence),+    -- * Pseudo-logic splitter combinators+    -- | Combinators '>&' and '>|' are only /pseudo/-logic. While the laws of double negation and De Morgan's laws hold,+    -- '>&' and '>|' are in general not commutative, associative, nor idempotent. In the special case when all argument+    -- splitters are stateless, such as those produced by 'Components.liftStatelessSplitter', these combinators do satisfy+    -- all laws of Boolean algebra.+    snot, (>&), (>|),+    -- ** Zipping logic combinators+    -- | The '&&' and '||' combinators run the argument splitters in parallel and combine their logical outputs using+    -- the corresponding logical operation on each output pair, in a manner similar to 'Prelude.zipWith'. They fully+    -- satisfy the laws of Boolean algebra.+    (&&), (||),+    -- * Flow-control combinators+    -- | The following combinators resemble the common flow-control programming language constructs. Combinators +    -- 'wherever', 'unless', and 'select' are just the special cases of the combinator 'ifs'.+    --+    --    * /transducer/ ``wherever`` /splitter/ = 'ifs' /splitter/ /transducer/ 'Components.asis'+    --+    --    * /transducer/ ``unless`` /splitter/ = 'ifs' /splitter/ 'Components.asis' /transducer/+    --+    --    * 'select' /splitter/ = 'ifs' /splitter/ 'Components.asis' 'Components.suppress'+    --+    ifs, wherever, unless, select,+    -- ** Recursive+    while, nestedIn,+    -- * Section-based combinators+    -- | All combinators in this section use their 'Splitter' argument to determine the+    -- structure of the input. Every contiguous portion of the input that gets passed to one or the other sink of the+    -- splitter is treated as one section in the logical structure of the input stream. What is done with the section+    -- depends on the combinator, but the sections, and therefore the logical structure of the input stream, are+    -- determined by the argument splitter alone.+    foreach, having, havingOnly, followedBy, even,+    -- ** first and its variants+    first, uptoFirst, prefix,+    -- ** last and its variants+    last, lastAndAfter, suffix,+    -- ** positional splitters+    startOf, endOf,+    -- ** input ranges+    (...))+where++import Control.Concurrent.SCC.Foundation+import Control.Concurrent.SCC.ComponentTypes++import Prelude hiding (even, last, sequence, (||), (&&))+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)++consumeBy :: forall m x y r. (Monad m, Typeable x) => Consumer m x r -> Transducer m x y+consumeBy c = liftTransducer "consumeBy" (maxUsableThreads c) $+              \threads-> let c' = usingThreads threads c+                         in (ComponentConfiguration [AnyComponent c'] (usedThreads c') (cost c'),+                             \ source _sink -> consume c' source >> return [])++-- | Class 'PipeableComponentPair' applies to any two components that can be combined into a third component with the+-- | following properties:+-- |    * The input of the result, if any, becomes the input of the first component.+-- |    * The output produced by the first child component is consumed by the second child component.+-- |    * The result output, if any, is the output of the second component.+class PipeableComponentPair (m :: * -> *) w c1 c2 c3 | c1 c2 -> c3, c1 c3 -> c2, c2 c3 -> c2,+                                                       c1 -> m w, c2 -> m w, c3 -> m+   where (>->) :: c1 -> c2 -> c3++instance (ParallelizableMonad m, Typeable x)+   => PipeableComponentPair m x (Producer m x ()) (Consumer m x ()) (Performer m ())+   where p >-> c = liftPerformer ">->" (maxUsableThreads p `max` maxUsableThreads c) $+                   \threads-> let (configuration, p', c', parallel) = optimalTwoParallelConfigurations threads p c+                                  performPipe = (if parallel then pipeP else pipe) (produce p') (consume c') >> return ()+                              in (configuration, performPipe)++instance (ParallelizableMonad m, Typeable x, Typeable y)+   => PipeableComponentPair m y (Transducer m x y) (Consumer m y r) (Consumer m x r)+   where t >-> c = liftConsumer ">->" (maxUsableThreads t `max` maxUsableThreads c) $+                   \threads-> let (configuration, t', c', parallel) = optimalTwoParallelConfigurations threads t c+                                  consumePipe source = liftM snd $ (if parallel then pipeP else pipe)+                                                                      (transduce t' source)+                                                                      (consume c')+                              in (configuration, consumePipe)++instance (ParallelizableMonad m, Typeable x, Typeable y)+   => PipeableComponentPair m x (Producer m x r) (Transducer m x y) (Producer m y r)+      where p >-> t = liftProducer ">->" (maxUsableThreads t `max` maxUsableThreads p) $+                      \threads-> let (configuration, p', t', parallel) = optimalTwoParallelConfigurations threads p t+                                     producePipe sink = liftM fst $ (if parallel then pipeP else pipe)+                                                                       (produce p')+                                                                       (\source-> transduce t' source sink)+                                 in (configuration, producePipe)++instance ParallelizableMonad m => PipeableComponentPair m y (Transducer m x y) (Transducer m y z) (Transducer m x z)+   where t1 >-> t2 = liftTransducer ">->" (maxUsableThreads t1 + maxUsableThreads t2) $+                     \threads-> let (configuration, t1', t2', parallel) = optimalTwoParallelConfigurations threads t1 t2+                                    transducePipe source sink = liftM fst $ (if parallel then pipeP else pipe)+                                                                               (transduce t1' source)+                                                                               (\source-> transduce t2' source sink)+                                in (configuration, transducePipe)++class Component c => CompatibleSignature c cons (m :: * -> *) input output | c -> cons m++class AnyListOrUnit c++instance AnyListOrUnit [x]+instance AnyListOrUnit ()++instance (AnyListOrUnit x, AnyListOrUnit y) => CompatibleSignature (Performer m r)    (PerformerType r)  m x y+instance AnyListOrUnit y                    => CompatibleSignature (Consumer m x r)   (ConsumerType r)   m [x] y+instance AnyListOrUnit y                    => CompatibleSignature (Producer m x r)   (ProducerType r)   m y [x]+instance                                       CompatibleSignature (Transducer m x y)  TransducerType    m [x] [y]++data PerformerType r+data ConsumerType r+data ProducerType r+data TransducerType++-- | Class 'JoinableComponentPair' applies to any two components that can be combined into a third component with the+-- | following properties:+-- |    * if both argument components consume input, the input of the combined component gets distributed to both+-- |      components in parallel,+-- |    * if both argument components produce output, the output of the combined component is a concatenation of the+-- |      complete output from the first component followed by the complete output of the second component, and+-- |    * the 'join' method may apply the components in any order, the 'sequence' method makes sure its first argument+-- |      has completed before using the second one.+class (Monad m, CompatibleSignature c1 t1 m x y, CompatibleSignature c2 t2 m x y, CompatibleSignature c3 t3 m x y)+   => JoinableComponentPair t1 t2 t3 m x y c1 c2 c3 | c1 c2 -> c3, c1 -> t1 m, c2 -> t2 m, c3 -> t3 m x y,+                                                      t1 m x y -> c1, t2 m x y -> c2, t3 m x y -> c3+   where join :: c1 -> c2 -> c3+         sequence :: c1 -> c2 -> c3+         join = sequence++instance forall m x any r1 r2. (Monad m, Typeable x)+   => JoinableComponentPair (ProducerType r1) (ProducerType r2) (ProducerType r2) m () [x] (Producer m x r1) (Producer m x r2) (Producer m x r2)+   where sequence p1 p2 = liftProducer "sequence" (maxUsableThreads p1 `max` maxUsableThreads p2) $+                          \threads-> let (configuration, p1', p2') = optimalTwoSequentialConfigurations threads p1 p2+                                         produceJoin sink = produce p1' sink >> produce p2' sink+                                     in (configuration, produceJoin)++instance forall m x any. (ParallelizableMonad m, Typeable x)+   => JoinableComponentPair (ConsumerType ()) (ConsumerType ()) (ConsumerType ()) m [x] () (Consumer m x ()) (Consumer m x ()) (Consumer m x ())+   where join c1 c2 = liftConsumer "join" (maxUsableThreads c1 + maxUsableThreads c2) $+                      \threads-> let (configuration, c1', c2', parallel) = optimalTwoParallelConfigurations threads c1 c2+                                     consumeJoin source = do (if parallel then pipeP else pipe)+                                                                (\sink1-> pipe (tee source sink1) (consume c2'))+                                                                (consume c1')+                                                             return ()+                                 in (configuration, consumeJoin)+         sequence c1 c2 = liftConsumer "sequence" (maxUsableThreads c1 `max` maxUsableThreads c2) $+                          \threads-> let (configuration, c1', c2') = optimalTwoSequentialConfigurations threads c1 c2+                                         consumeJoin source = pipe+                                                                 (\buffer-> pipe (tee source buffer) (consume c1'))+                                                                 getList+                                                              >>= \(_, list)-> pipe (putList list) (consume c2')+                                                              >> return ()+                                     in (configuration, consumeJoin)++instance forall m x y. (ParallelizableMonad m, Typeable x, Typeable y)+   => JoinableComponentPair TransducerType TransducerType TransducerType m [x] [y] (Transducer m x y) (Transducer m x y) (Transducer m x y)+   where join t1 t2 = liftTransducer "join" (maxUsableThreads t1 + maxUsableThreads t2) $+                      \threads-> let (configuration, t1', t2', parallel) = optimalTwoParallelConfigurations threads t1 t2+                                     transduce' source sink = pipe+                                                                 (\buffer-> (if parallel then pipeP else pipe)+                                                                               (\sink1-> pipe+                                                                                            (\sink2-> tee source sink1 sink2)+                                                                                            (\src-> transduce t2' src buffer))+                                                                               (\source-> transduce t1' source sink))+                                                                 getList+                                                              >>= \(_, list)-> putList list sink+                                                              >> getList source+                                 in (configuration, transduce')+         sequence t1 t2 = liftTransducer "sequence" (maxUsableThreads t1 `max` maxUsableThreads t2) $+                          \threads-> let (configuration, t1', t2') = optimalTwoSequentialConfigurations threads t1 t2+                                         transduce' source sink = pipe+                                                                     (\buffer-> pipe+                                                                                   (tee source buffer)+                                                                                   (\source-> transduce t1 source sink))+                                                                     getList+                                                                  >>= \((extra, _), list)-> pipe+                                                                                               (putList list)+                                                                                               (\source-> transduce t2 source sink)+                                                                  >> return extra+                                     in (configuration, transduce')+++instance forall m r1 r2. ParallelizableMonad m+   => JoinableComponentPair (PerformerType r1) (PerformerType r2) (PerformerType r2) m () () (Performer m r1) (Performer m r2) (Performer m r2)+   where join p1 p2 = liftPerformer "join" (maxUsableThreads p1 + maxUsableThreads p2) $+                      \threads-> let (configuration, p1', p2', parallel) = optimalTwoParallelConfigurations threads p1 p2+                                 in (configuration, if parallel then liftM snd $ perform p1' `parallelize` perform p2'+                                                    else perform p1' >> perform p2')+         sequence p1 p2 = liftPerformer "sequence" (maxUsableThreads p1 `max` maxUsableThreads p2) $+                          \threads-> let (configuration, p1', p2') = optimalTwoSequentialConfigurations threads p1 p2+                                     in (configuration, perform p1' >> perform p2')++instance forall m x r1 r2. (ParallelizableMonad m, Typeable x)+   => JoinableComponentPair (PerformerType r1) (ProducerType r2) (ProducerType r2) m () [x] (Performer m r1) (Producer m x r2) (Producer m x r2)+   where join pe pr = liftProducer "join" (maxUsableThreads pe + maxUsableThreads pr) $+                      \threads-> let (configuration, pe', pr', parallel) = optimalTwoParallelConfigurations threads pe pr+                                     produceJoin sink = if parallel then liftM snd (perform pe' `parallelize` produce pr' sink)+                                                        else perform pe' >> produce pr' sink+                                 in (configuration, produceJoin)+         sequence pe pr = liftProducer "sequence" (maxUsableThreads pe `max` maxUsableThreads pr) $+                          \threads-> let (configuration, pe', pr') = optimalTwoSequentialConfigurations threads pe pr+                                         produceJoin sink = perform pe' >> produce pr' sink+                                     in (configuration, produceJoin)++instance forall m x r1 r2. (ParallelizableMonad m, Typeable x)+   => JoinableComponentPair (ProducerType r1) (PerformerType r2) (ProducerType r2) m () [x] (Producer m x r1) (Performer m r2) (Producer m x r2)+   where join pr pe = liftProducer "join" (maxUsableThreads pr + maxUsableThreads pe) $+                      \threads-> let (configuration, pr', pe', parallel) = optimalTwoParallelConfigurations threads pr pe+                                     produceJoin sink = if parallel then liftM snd (produce pr' sink `parallelize` perform pe')+                                                        else produce pr' sink >> perform pe'+                                 in (configuration, produceJoin)+         sequence pr pe = liftProducer "sequence" (maxUsableThreads pr `max` maxUsableThreads pe) $+                          \threads-> let (configuration, pr', pe') = optimalTwoSequentialConfigurations threads pr pe+                                         produceJoin sink = produce pr' sink >> perform pe'+                                     in (configuration, produceJoin)++instance forall m x r1 r2. (ParallelizableMonad m, Typeable x)+   => JoinableComponentPair (PerformerType r1) (ConsumerType r2) (ConsumerType r2) m [x] () (Performer m r1) (Consumer m x r2) (Consumer m x r2)+   where join p c = liftConsumer "join" (maxUsableThreads p + maxUsableThreads c) $+                    \threads-> let (configuration, p', c', parallel) = optimalTwoParallelConfigurations threads p c+                                   consumeJoin source = if parallel then liftM snd (perform p' `parallelize` consume c' source)+                                                        else perform p' >> consume c' source+                               in (configuration, consumeJoin)+         sequence p c = liftConsumer "sequence" (maxUsableThreads p `max` maxUsableThreads c) $+                        \threads-> let (configuration, p', c') = optimalTwoSequentialConfigurations threads p c+                                       consumeJoin source = perform p' >> consume c' source+                                   in (configuration, consumeJoin)++instance forall m x r1 r2. (ParallelizableMonad m, Typeable x)+   => JoinableComponentPair (ConsumerType r1) (PerformerType r2) (ConsumerType r2) m [x] () (Consumer m x r1) (Performer m r2) (Consumer m x r2)+   where join c p = liftConsumer "join" (maxUsableThreads c + maxUsableThreads p) $+                    \threads-> let (configuration, c', p', parallel) = optimalTwoParallelConfigurations threads c p+                                   consumeJoin source = if parallel then liftM snd (consume c' source `parallelize` perform p')+                                                        else consume c' source >> perform p'+                               in (configuration, consumeJoin)+         sequence c p = liftConsumer "sequence" (maxUsableThreads c `max` maxUsableThreads p) $+                        \threads-> let (configuration, c', p') = optimalTwoSequentialConfigurations threads c p+                                       consumeJoin source = consume c' source >> perform p'+                                   in (configuration, consumeJoin)++instance forall m x y r. (ParallelizableMonad m, Typeable x, Typeable y)+   => JoinableComponentPair (PerformerType r) TransducerType TransducerType m [x] [y] (Performer m r) (Transducer m x y) (Transducer m x y)+   where join p t = liftTransducer "join" (maxUsableThreads p + maxUsableThreads t) $+                    \threads-> let (configuration, p', t', parallel) = optimalTwoParallelConfigurations threads p t+                                   join' source sink = if parallel then liftM snd (perform p'+                                                                                   `parallelize` transduce t' source sink)+                                                       else perform p' >> transduce t' source sink+                               in (configuration, join')+         sequence p t = liftTransducer "sequence" (maxUsableThreads p `max` maxUsableThreads t) $+                        \threads-> let (configuration, p', t') = optimalTwoSequentialConfigurations threads p t+                                       join' source sink = perform p' >> transduce t' source sink+                                   in (configuration, join')++instance forall m x y r. (ParallelizableMonad m, Typeable x, Typeable y)+   => JoinableComponentPair TransducerType (PerformerType r) TransducerType m [x] [y] (Transducer m x y) (Performer m r) (Transducer m x y)+   where join t p = liftTransducer "join" (maxUsableThreads t + maxUsableThreads p) $+                    \threads-> let (configuration, t', p', parallel) = optimalTwoParallelConfigurations threads t p+                                   join' source sink = if parallel then liftM fst (transduce t' source sink+                                                                                   `parallelize` perform p')+                                                       else do result <- transduce t' source sink+                                                               perform p'+                                                               return result+                               in (configuration, join')+         sequence t p = liftTransducer "sequence" (maxUsableThreads t `max` maxUsableThreads p) $+                        \threads-> let (configuration, t', p') = optimalTwoSequentialConfigurations threads t p+                                       join' source sink = do result <- transduce t' source sink+                                                              perform p'+                                                              return result+                                   in (configuration, join')++instance forall m x y. (ParallelizableMonad m, Typeable x, Typeable y)+   => JoinableComponentPair (ProducerType ()) TransducerType TransducerType m [x] [y] (Producer m y ()) (Transducer m x y) (Transducer m x y)+   where join p t = liftTransducer "join" (maxUsableThreads p + maxUsableThreads t) $+                    \threads-> let (configuration, p', t', parallel) = optimalTwoParallelConfigurations threads p t+                                   join' source sink = if parallel+                                                       then do ((_, rest), out) <- pipe+                                                                                      (\buffer-> produce p' sink `parallelize`+                                                                                                 transduce t' source buffer)+                                                                                      getList+                                                               putList out sink+                                                               return rest +                                                       else produce p' sink >> transduce t' source sink+                               in (configuration, join')+         sequence p t = liftTransducer "sequence" (maxUsableThreads p `max` maxUsableThreads t) $+                        \threads-> let (configuration, p', t') = optimalTwoSequentialConfigurations threads p t+                                       join' source sink = produce p' sink >> transduce t' source sink+                                   in (configuration, join')++instance forall m x y. (ParallelizableMonad m, Typeable x, Typeable y)+   => JoinableComponentPair TransducerType (ProducerType ()) TransducerType m [x] [y] (Transducer m x y) (Producer m y ()) (Transducer m x y)+   where join t p = liftTransducer "join" (maxUsableThreads t `max` maxUsableThreads p) $+                    \threads-> let (configuration, t', p', parallel) = optimalTwoParallelConfigurations threads t p+                                   join' source sink = if parallel+                                                       then do ((rest, ()), out) <- pipe+                                                                                       (\buffer-> transduce t' source sink+                                                                                                  `parallelize` produce p' buffer)+                                                                                       getList+                                                               putList out sink+                                                               return rest +                                                       else do result <- transduce t' source sink+                                                               produce p' sink+                                                               return result+                               in (configuration, join')+         sequence t p = liftTransducer "sequence" (maxUsableThreads t `max` maxUsableThreads p) $+                        \threads-> let (configuration, t', p') = optimalTwoSequentialConfigurations threads t p+                                       join' source sink = do result <- transduce t' source sink+                                                              produce p' sink+                                                              return result+                                   in (configuration, join')++instance forall m x y. (ParallelizableMonad m, Typeable x, Typeable y)+   => JoinableComponentPair (ConsumerType ()) TransducerType TransducerType m [x] [y] (Consumer m x ()) (Transducer m x y) (Transducer m x y)+   where join c t = liftTransducer "join" (maxUsableThreads c + maxUsableThreads t) $+                    \threads-> let (configuration, c', t', parallel) = optimalTwoParallelConfigurations threads c t+                                   join' source sink = liftM (snd . fst) $+                                                       (if parallel then pipeP else pipe)+                                                          (\sink1-> pipe+                                                                       (tee source sink1)+                                                                       (\source-> transduce t' source sink))+                                                          (consume c')+                               in (configuration, join')+         sequence c t = liftTransducer "sequence" (maxUsableThreads c `max` maxUsableThreads t) $+                        \threads-> let (configuration, c', t') = optimalTwoSequentialConfigurations threads c t+                                       sequence' source sink = pipe+                                                                  (\buffer-> pipe+                                                                                (tee source buffer)+                                                                                (consume c'))+                                                                  getList+                                                               >>= \((rest, _), list)-> pipe+                                                                                           (putList list)+                                                                                           (\source-> transduce t' source sink)+                                                               >> return rest+                                   in (configuration, sequence')++instance forall m x y. (ParallelizableMonad m, Typeable x, Typeable y)+   => JoinableComponentPair TransducerType (ConsumerType ()) TransducerType m [x] [y] (Transducer m x y) (Consumer m x ()) (Transducer m x y)+   where join t c = join c t+         sequence t c = liftTransducer "sequence" (maxUsableThreads t `max` maxUsableThreads c) $+                        \threads-> let (configuration, t', c') = optimalTwoSequentialConfigurations threads t c+                                       sequence' source sink = pipe+                                                                  (\buffer-> pipe+                                                                                (tee source buffer)+                                                                                (\source-> transduce t' source sink))+                                                                  getList+                                                               >>= \((rest, _), list)-> pipe+                                                                                           (putList list)+                                                                                           (consume c')+                                                               >> return rest+                                   in (configuration, sequence')++instance forall m x y. (ParallelizableMonad m, Typeable x, Typeable y)+   => JoinableComponentPair (ProducerType ()) (ConsumerType ()) TransducerType m [x] [y] (Producer m y ()) (Consumer m x ()) (Transducer m x y)+   where join p c = liftTransducer "sequence" (maxUsableThreads p + maxUsableThreads c) $+                    \threads-> let (configuration, p', c', parallel) = optimalTwoParallelConfigurations threads p c+                                   join' source sink = if parallel then produce p' sink >> consume c' source >> return []+                                                       else parallelize (produce p' sink) (consume c' source) >> return []+                               in (configuration, join')+         sequence p c = liftTransducer "sequence" (maxUsableThreads p `max` maxUsableThreads c) $+                        \threads-> let (configuration, p', c') = optimalTwoSequentialConfigurations threads p c+                                       join' source sink = produce p' sink >> consume c' source >> return []+                                   in (configuration, join')++instance forall m x y. (ParallelizableMonad m, Typeable x, Typeable y)+   => JoinableComponentPair (ConsumerType ()) (ProducerType ()) TransducerType m [x] [y] (Consumer m x ()) (Producer m y ()) (Transducer m x y)+   where join c p = join p c+         sequence c p = liftTransducer "sequence" (maxUsableThreads c `max` maxUsableThreads p) $+                        \threads-> let (configuration, c', p') = optimalTwoSequentialConfigurations threads c p+                                       join' source sink = consume c' source >> produce p' sink >> return []+                                   in (configuration, join')++-- | Combinator 'prepend' converts the given producer to transducer that passes all its input through unmodified, except+-- | for prepending the output of the argument producer to it.+-- | 'prepend' /prefix/ = 'join' ('substitute' /prefix/) 'asis'+prepend :: forall m x r. (Monad m, Typeable x) => Producer m x r -> Transducer m x x+prepend prefix = liftTransducer "prepend" (maxUsableThreads prefix) $+                 \threads-> let prefix' = usingThreads threads prefix+                                prepend' source sink = produce prefix' sink >> pour source sink >> return []+                            in (ComponentConfiguration [AnyComponent prefix] threads (cost prefix'), prepend')++-- | Combinator 'append' converts the given producer to transducer that passes all its input through unmodified, finally+-- | appending to it the output of the argument producer.+-- | 'append' /suffix/ = 'join' 'asis' ('substitute' /suffix/)+append :: forall m x r. (Monad m, Typeable x) => Producer m x r -> Transducer m x x+append suffix = liftTransducer "append" (maxUsableThreads suffix) $+                \threads-> let suffix' = usingThreads threads suffix+                               append' source sink = pour source sink >> produce suffix' sink >> return []+                           in (ComponentConfiguration [AnyComponent suffix] threads (cost suffix'), append')++-- | The 'substitute' combinator converts its argument producer to a transducer that produces the same output, while+-- | consuming its entire input and ignoring it.+substitute :: forall m x y r. (Monad m, Typeable x, Typeable y) => Producer m y r -> Transducer m x y+substitute feed = liftTransducer "substitute" (maxUsableThreads feed) $+                  \threads-> let feed' = usingThreads threads feed+                                 substitute' source sink = consumeAndSuppress source >> produce feed' sink >> return []+                             in (ComponentConfiguration [AnyComponent feed] threads (cost feed'), substitute')++-- | The 'snot' (streaming not) combinator simply reverses the outputs of the argument splitter.+-- In other words, data that the argument splitter sends to its /true/ sink goes to the /false/ sink of the result, and vice versa.+snot :: (ParallelizableMonad m, Typeable x) => Splitter m x -> Splitter m x+snot splitter = liftSectionSplitter "not" (maxUsableThreads splitter) $+                \threads-> let splitter' = usingThreads threads splitter+                               not source true false = splitSections splitter source false true+                           in (ComponentConfiguration [AnyComponent splitter'] threads (cost splitter'), not)++-- | The '>&' combinator sends the /true/ sink output of its left operand to the input of its right operand for further+-- splitting. Both operands' /false/ sinks are connected to the /false/ sink of the combined splitter, but any input+-- value to reach the /true/ sink of the combined component data must be deemed true by both splitters.+(>&) :: (ParallelizableMonad m, Typeable x) => Splitter m x -> Splitter m x -> Splitter m x+s1 >& s2 = liftSimpleSplitter ">&" (maxUsableThreads s1 + maxUsableThreads s2) $+           \threads-> let (configuration, s1', s2', parallel) = optimalTwoParallelConfigurations threads s1 s2+                          s source true false = liftM fst $+                                                (if parallel then pipeP else pipe)+                                                   (\true-> split s1 source true false)+                                                   (\source-> split s2 source true false)+                      in (configuration, s)++-- | A '>|' combinator's input value can reach its /false/ sink only by going through both argument splitters' /false/+-- sinks.+(>|) :: (ParallelizableMonad m, Typeable x) => Splitter m x -> Splitter m x -> Splitter m x+s1 >| s2 = liftSimpleSplitter ">|" (maxUsableThreads s1 + maxUsableThreads s2) $+           \threads-> let (configuration, s1', s2', parallel) = optimalTwoParallelConfigurations threads s1 s2+                          s source true false = liftM fst $+                                                (if parallel then pipeP else pipe)+                                                   (split s1 source true)+                                                   (\source-> split s2 source true false)+                      in (configuration, s)++-- | Combinator '&&' is a pairwise logical conjunction of two splitters run in parallel on the same input.+(&&) :: (ParallelizableMonad m, Typeable x) => Splitter m x -> Splitter m x -> Splitter m x+(&&) = zipSplittersWith (Prelude.&&)++-- | Combinator '||' is a pairwise logical disjunction of two splitters run in parallel on the same input.+(||) :: (ParallelizableMonad m, Typeable x) => Splitter m x -> Splitter m x -> Splitter m x+(||) = zipSplittersWith (Prelude.||)++ifs :: (ParallelizableMonad m, Typeable x, BranchComponent cc m x [x]) => Splitter m x -> cc -> cc -> cc+ifs s = combineBranches "if" (cost s) (\ parallel c1 c2 -> \source-> liftM fst3 $ splitConsumer "ifs" parallel s c1 c2 source)++wherever :: (ParallelizableMonad m, Typeable x) => Transducer m x x -> Splitter m x -> Transducer m x x+wherever t s = liftTransducer "wherever" (maxUsableThreads s + maxUsableThreads t) $+               \threads-> let (configuration, s', t', parallel) = optimalTwoParallelConfigurations threads s t+                              wherever' source sink = liftM fst3 $ splitConsumer "wherever" parallel s+                                                                      (\source-> transduce t source sink)+                                                                      (\source-> pour source sink)+                                                                      source+                          in (configuration, wherever')++unless :: (ParallelizableMonad m, Typeable x) => Transducer m x x -> Splitter m x -> Transducer m x x+unless t s = liftTransducer "unless" (maxUsableThreads s + maxUsableThreads t) $+             \threads-> let (configuration, s', t', parallel) = optimalTwoParallelConfigurations threads s t+                            unless' source sink = liftM fst3 $ splitConsumer "unless" parallel s+                                                                  (\source-> pour source sink)+                                                                  (\source-> transduce t source sink)+                                                                  source+                        in (configuration, unless')++select :: (ParallelizableMonad m, Typeable x) => Splitter m x -> Transducer m x x+select s = liftTransducer "select" (maxUsableThreads s) $+           \threads-> let s' = usingThreads threads s+                          transduce' source sink = liftM fst3 $ splitConsumer "select" False s'+                                                                   (\source-> pour source sink)+                                                                   consumeAndSuppress+                                                                   source+                      in (ComponentConfiguration [AnyComponent s'] threads (cost s' + 1), transduce')++-- | The recursive combinator 'while' feeds the true sink of the argument splitter back to itself, modified by the+-- argument transducer. Data fed to the splitter's false sink is passed on unmodified.+while :: (ParallelizableMonad m, Typeable x) => Transducer m x x -> Splitter m x -> Transducer m x x+while t s = liftTransducer "while" (maxUsableThreads t + maxUsableThreads s) $+            \threads-> let (configuration, s', while'', parallel) = optimalTwoParallelConfigurations threads s while'+                           transduce' source sink = liftM fst3 $ splitConsumer "while" parallel s'+                                                                    (\source-> transduce while' source sink)+                                                                    (\source-> pour source sink)+                                                                    source+                           while' = t >-> while t s+                       in (configuration, transduce')++-- | The recursive combinator 'nestedIn' combines two splitters into a mutually recursive loop acting as a single splitter.+-- The true  sink of one of the argument splitters and false sink of the other become the true and false sinks of the loop.+-- The other two sinks are bound to the other splitter's source.+-- The use of 'nestedIn' makes sense only on hierarchically structured streams. If we gave it some input containing+-- a flat sequence of values, and assuming both component splitters are deterministic and stateless,+-- a value would either not loop at all or it would loop forever.+nestedIn :: (ParallelizableMonad m, Typeable x) => Splitter m x -> Splitter m x -> Splitter m x+nestedIn s1 s2 = liftSimpleSplitter "nestedIn" (maxUsableThreads s1 + maxUsableThreads s2) $+                 \threads-> let (configuration, s1', s2', parallel) = optimalTwoParallelConfigurations threads s1 s2+                                s source true false = liftM fst $+                                                      (if parallel then pipeP else pipe)+                                                         (\false-> split s1' source true false)+                                                         (\source-> pipe (\true-> split s2' source true false)+                                                                         (\source-> split (nestedIn s1' s2') source true false))+                            in (configuration,s)++-- | The 'foreach' combinator is similar to the combinator 'ifs' in that it combines a splitter and two transducers into+-- another transducer. However, in this case the transducers are re-instantiated for each consecutive portion of the+-- input as the splitter chunks it up. Each contiguous portion of the input that the splitter sends to one of its two+-- sinks gets transducered through the appropriate argument transducer as that transducer's whole input. As soon as the+-- contiguous portion is finished, the transducer gets terminated.+foreach :: (ParallelizableMonad m, Typeable x, BranchComponent cc m x [x]) => Splitter m x -> cc -> cc -> cc+foreach s = combineBranches "foreach" (cost s)+               (\ parallel c1 c2 source-> liftM fst $ (if parallel then pipeP else pipe)+                                                         (transduce (splitterToMarker s) source)+                                                         (\source-> groupMarks source (\b-> if b then c1 else c2)))++-- | The 'having' combinator combines two pure splitters into a pure splitter. One splitter is used to chunk the input+-- into contiguous portions. Its /false/ sink is routed directly to the /false/ sink of the combined splitter. The+-- second splitter is instantiated and run on each portion of the input that goes to first splitter's /true/ sink. If+-- the second splitter sends any output at all to its /true/ sink, the whole input portion is passed on to the /true/+-- sink of the combined splitter, otherwise it goes to its /false/ sink.+having :: (ParallelizableMonad m, Typeable x) => Splitter m x -> Splitter m x -> Splitter m x+having s1 s2 = liftSectionSplitter "having" (maxUsableThreads s1 + maxUsableThreads s2) $+               \threads-> let (configuration, s1', s2', parallel) = optimalTwoParallelConfigurations threads s1 s2+                              s source true false = liftM fst $+                                                    (if parallel then pipeP else pipe)+                                                       (transduce (splitterToMarker s1') source)+                                                       (\source-> groupMarks source (\b chunk-> if b then test chunk+                                                                                                else pourMaybe chunk false))+                                 where test chunk = pipe (\sink1-> pipe (tee chunk sink1) 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 ()+                            in (configuration, s)++-- | The 'havingOnly' combinator is analogous to the 'having' combinator, but it succeeds and passes each chunk of the+-- input to its /true/ sink only if the second splitter sends no part of it to its /false/ sink.+havingOnly :: (ParallelizableMonad m, Typeable x) => Splitter m x -> Splitter m x -> Splitter m x+havingOnly s1 s2 = liftSectionSplitter "havingOnly" (maxUsableThreads s1 + maxUsableThreads s2) $+                   \threads-> let (configuration, s1', s2', parallel) = optimalTwoParallelConfigurations threads s1 s2+                                  s source true false = liftM fst $+                                                        (if parallel then pipeP else pipe)+                                                           (transduce (splitterToMarker s1') source)+                                                           (\source-> groupMarks source (\b chunk-> if b then test chunk+                                                                                                    else pourMaybe chunk false))+                                     where test chunk = pipe (\sink1-> pipe (tee chunk sink1) 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 ()+                            in (configuration, s)++-- | The result of combinator 'first' behaves the same as the argument splitter up to and including the first portion of+-- the input which goes into the argument's /true/ sink. All input following the first true portion goes into the+-- /false/ sink.+first :: (ParallelizableMonad m, Typeable x) => Splitter m x -> Splitter m x+first splitter = liftSectionSplitter "first" (maxUsableThreads splitter) $+                 \threads-> let splitter' = usingThreads threads splitter+                                configuration = ComponentConfiguration [AnyComponent splitter'] threads (cost splitter' + 2)+                                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)+                            in (configuration, s)++-- | The result of combinator 'uptoFirst' takes all input up to and including the first portion of the input which goes+-- into the argument's /true/ sink and feeds it to the result splitter's /true/ sink. All the rest of the input goes+-- into the /false/ sink. The only difference between 'last' and 'lastAndAfter' combinators is in where they direct the+-- /false/ portion of the input preceding the first /true/ part.+uptoFirst :: (ParallelizableMonad m, Typeable x) => Splitter m x -> Splitter m x+uptoFirst splitter = liftSectionSplitter "uptoFirst" (maxUsableThreads splitter) $+                     \threads-> let splitter' = usingThreads threads splitter+                                    configuration = ComponentConfiguration [AnyComponent splitter'] threads (cost splitter' + 2)+                                    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) = putQueue q true+                                                                                              >>= whenNull (get2 p)+                                                                         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))+                            in (configuration, s)++-- | The result of the combinator 'last' is a splitter which directs all input to its /false/ sink, up to the last+-- portion of the input which goes to its argument's /true/ sink. That portion of the input is the only one that goes to+-- the resulting component's /true/ sink.  The splitter returned by the combinator 'last' has to buffer the previous two+-- portions of its input, because it cannot know if a true portion of the input is the last one until it sees the end of+-- the input or another portion succeeding the previous one.+last :: (ParallelizableMonad m, Typeable x) => Splitter m x -> Splitter m x+last splitter = liftSectionSplitter "last" (maxUsableThreads splitter) $+                \threads-> let splitter' = usingThreads threads splitter+                               configuration = ComponentConfiguration [AnyComponent splitter'] threads (cost splitter' + 2)+                               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)+                            in (configuration, s)++-- | The result of the combinator 'lastAndAfter' is a splitter which directs all input to its /false/ sink, up to the+-- last portion of the input which goes to its argument's /true/ sink. That portion and the remainder of the input is fed+-- to the resulting component's /true/ sink. The difference between 'last' and 'lastAndAfter' combinators is where they+-- feed the /false/ portion of the input, if any, remaining after the last /true/ part.+lastAndAfter :: (ParallelizableMonad m, Typeable x) => Splitter m x -> Splitter m x+lastAndAfter splitter = liftSectionSplitter "lastAndAfter" (maxUsableThreads splitter) $+                        \threads-> let splitter' = usingThreads threads splitter+                                       configuration = ComponentConfiguration [AnyComponent splitter'] threads (cost splitter' + 2)+                                       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)+                                   in (configuration, s)++-- | The 'prefix' combinator feeds its /true/ sink only the prefix of the input that its argument feeds to its /true/ sink.+-- All the rest of the input is dumped into the /false/ sink of the result.+prefix :: (ParallelizableMonad m, Typeable x) => Splitter m x -> Splitter m x+prefix splitter = liftSectionSplitter "prefix" (maxUsableThreads splitter) $+                  \threads-> let splitter' = usingThreads threads splitter+                                 configuration = ComponentConfiguration [AnyComponent splitter'] threads (cost splitter' + 2)+                                 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)+                             in (configuration, s)++-- | The 'suffix' combinator feeds its /true/ sink only the suffix of the input that its argument feeds to its /true/ sink.+-- All the rest of the input is dumped into the /false/ sink of the result.+suffix :: (ParallelizableMonad m, Typeable x) => Splitter m x -> Splitter m x+suffix splitter = liftSectionSplitter "suffix" (maxUsableThreads splitter) $+                  \threads-> let splitter' = usingThreads threads splitter+                                 configuration = ComponentConfiguration [AnyComponent splitter'] threads (cost splitter' + 2)+                                 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)+                             in (configuration, s)++-- | The 'even' combinator takes every input section that its argument splitters deems /true/, and feeds even ones into+-- its /true/ sink. The odd sections and parts of input that are /false/ according to its argument splitter are fed to+-- 'even' splitter's /false/ sink.+even :: (ParallelizableMonad m, Typeable x) => Splitter m x -> Splitter m x+even splitter = liftSectionSplitter "even" (maxUsableThreads splitter) $+                   \threads-> let splitter' = usingThreads threads splitter+                                  configuration = ComponentConfiguration [AnyComponent splitter'] threads (cost splitter' + 2)+                                  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 (next get1) (return [x])+                                                                          get1 p@(x, True) = get2 p+                                                                          get2 (x, True) = put false x+                                                                                           >>= cond (next get2) (return [x])+                                                                          get2 p@(x, False) = get3 p+                                                                          get3 (x, False) = put false x+                                                                                            >>= cond (next get3) (return [x])+                                                                          get3 p@(x, True) = get4 p+                                                                          get4 (x, True) = put true x+                                                                                           >>= cond (next get4) (return [x])+                                                                          get4 p@(x, False) = get1 p+                                                                          next g = get source >>= maybe (return []) g+                                                                      in next get1)+                             in (configuration, s)++-- | Splitter 'startOf' issues an empty /true/ section at the beginning of every section considered /true/ by its+-- | argument splitter, otherwise the entire input goes into its /false/ sink.+startOf :: (ParallelizableMonad m, Typeable x) => Splitter m x -> Splitter m x+startOf splitter = liftSectionSplitter "startOf" (maxUsableThreads splitter) $+                   \threads-> let splitter' = usingThreads threads splitter+                                  configuration = ComponentConfiguration [AnyComponent splitter'] threads (cost splitter' + 2)+                                  s source true false = liftM (\(x, y)-> concatMap (maybe [] (:[])) y ++ x) $+                                                        pipeD "startOf"+                                                           (transduce (splitterToMarker splitter') source)+                                                           (\source-> let get1 (x, False) = put false x+                                                                                            >>= cond (next get1) (return [x])+                                                                          get1 p@(x, True) = put true Nothing >> get2 p+                                                                          get2 (x, True) = put false x+                                                                                           >>= cond (next get2) (return [x])+                                                                          get2 p@(x, False) = get1 p+                                                                          next g = get source >>= maybe (return []) g+                                                                      in next get1)+                              in (configuration, s)++-- | Splitter 'endOf' issues an empty /true/ section at the end of every section considered /true/ by its argument+-- | splitter, otherwise the entire input goes into its /false/ sink.+endOf :: (ParallelizableMonad m, Typeable x) => Splitter m x -> Splitter m x+endOf splitter = liftSectionSplitter "endOf" (maxUsableThreads splitter) $+                 \threads-> let splitter' = usingThreads threads splitter+                                configuration = ComponentConfiguration [AnyComponent splitter'] threads (cost splitter' + 2)+                                s source true false = liftM (\(x, y)-> concatMap (maybe [] (:[])) y ++ x) $+                                                      pipeD "endOf"+                                                         (transduce (splitterToMarker splitter') source)+                                                         (\source-> let get1 (x, False) = put false x+                                                                                          >>= cond (next get1) (return [x])+                                                                        get1 p@(x, True) = get2 p+                                                                        get2 (x, True) = put false x+                                                                                         >>= cond (next get2) (return [x])+                                                                        get2 p@(x, False) = put true Nothing >> get1 p+                                                                        next g = get source >>= maybe (return []) g+                                                                    in next get1)+                            in (configuration, s)++-- | Combinator 'followedBy' treats its argument 'Splitter's as patterns components and returns a 'Splitter' that+-- matches their concatenation. A section of input is considered /true/ by the result iff its prefix is considered+-- /true/ by argument /s1/ and the rest of the section is considered /true/ by /s2/. The splitter /s2/ is started anew+-- after every section split to /true/ sink by /s1/.+followedBy :: forall m x. (ParallelizableMonad m, Typeable x) => Splitter m x -> Splitter m x -> Splitter m x+followedBy s1 s2 = liftSectionSplitter "followedBy" (maxUsableThreads s1 + maxUsableThreads s2) $+                   \threads-> let (configuration, s1', s2', parallel) = optimalTwoParallelConfigurations threads s1 s2+                              in (configuration, followedBy' parallel s1' s2')+   where followedBy' parallel s1 s2 source true false+            = liftM (\(x, y)-> concatMap (maybe [] (:[])) y ++ x) $+              (if parallel then pipeP else pipe)+                 (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) q2 sink)+                                                               p :< rest -> maybe (return True) (put sink) (fst p)+                                                                            >> get3 (q1 |> p) rest sink)+                                                           (return (q1, q2))+                                testEnd q = do ((), n) <- pipeD "testEnd" (const $ return ()) (test q)+                                               case n of Nothing -> putQueue q false+                                                         _ -> return []+                                test q source = liftM snd $+                                                pipeD "follower"+                                                   (transduce (splitterToMarker s2) source)+                                                   (\source-> let get4 (_, False) = return Nothing+                                                                  get4 p@(_, True) = putQueue q true >> get5 0 p+                                                                  get5 n (x, False) = return (Just n)+                                                                  get5 n (Nothing, True) = get6 n+                                                                  get5 n (x, True) = put true x >> get6 (succ n)+                                                                  get6 n = get source+                                                                           >>= maybe+                                                                                  (return $ Just n)+                                                                                  (get5 n)+                                                              in get source >>= maybe (return Nothing) get4)+                                dropJust 0 q = q+                                dropJust n q = case Seq.viewl q of (Nothing, _) :< rest -> dropJust n rest+                                                                   (Just _, _) :< rest -> dropJust (pred n) rest+                           in get0 Seq.empty)++-- | Combinator '...' tracks the running balance of difference between the numbers of preceding inputs considered /true/+-- according to its first argument and the ones according to its second argument. The combinator passes to /true/ all+-- input values for which the difference balance is positive. This combinator is typically used with 'startOf' and+-- 'endOf' in order to count entire input sections and ignore their lengths.+(...) :: forall m x. (ParallelizableMonad m, Typeable x) => Splitter m x -> Splitter m x -> Splitter m x+s1 ... s2 = liftSectionSplitter "..." (maxUsableThreads s1 + maxUsableThreads s2) $+            \threads-> let (configuration, s1', s2', parallel) = optimalTwoParallelConfigurations threads s1 s2+                           s source true false = liftM (\(x, y)-> concatMap (maybe [] (:[])) y ++ x) $+                                                 (if parallel then pipeP else pipe)+                                                    (transduce (splittersToPairMarker s1 s2) source)+                                                    (\source-> let next n = get source >>= maybe (return []) (state n)+                                                                   pass n x = (if n > 0 then put true x else put false x)+                                                                              >>= cond (next n) (return [x])+                                                                   pass' n x = (if n >= 0 then put true x else put false x)+                                                                               >>= cond (next n) (return [x])+                                                                   state n (Left (x, True, False)) = pass (succ n) (Just x)+                                                                   state n (Left (x, False, True)) = pass' (pred n) (Just x)+                                                                   state n (Left (x, True, True)) = pass' n (Just x)+                                                                   state n (Left (x, False, False)) = pass n (Just x)+                                                                   state n (Right (Left True)) = pass (succ n) Nothing+                                                                   state n (Right (Right True)) = pass (pred n) Nothing+                                                                   state n (Right _) = next n+                                                               in next 0)+                       in (configuration, s)++-- Helper functions++type Marker m x = Transducer m x (Maybe x, Bool)++splitterToMarker :: forall m x. (ParallelizableMonad m, Typeable x) => Splitter m x -> Marker m x+splitterToMarker s = liftTransducer "splitterToMarker" (maxUsableThreads s) $+                     \threads-> let s' = usingThreads threads s+                                    t source sink = liftM (\((x, y), z)-> z ++ y ++ x) $+                                                    pipeD "splitterToMarker true"+                                                       (\trueSink-> pipeD "splitterToMarker false"+                                                                       (splitSections s' source trueSink)+                                                                       (mark False))+                                                       (mark True)+                                             where mark b source = canPut sink+                                                                   >>= cond (get source+                                                                             >>= maybe (return [])+                                                                                       (\x-> put sink (x, b)+                                                                                             >>= cond+                                                                                                    (mark b source)+                                                                                                    (return $ maybe [] (: []) x)))+                                                                            (return [])+                                in (ComponentConfiguration [AnyComponent s'] threads (cost s' + 1), t)+++splittersToPairMarker :: forall m x. (ParallelizableMonad m, Typeable x)+                         => Splitter m x -> Splitter m x -> Transducer m x (Either (x, Bool, Bool) (Either Bool Bool))+splittersToPairMarker s1 s2+   = liftTransducer "splittersToPairMarker" (maxUsableThreads s1 + maxUsableThreads s2) $+     \threads-> let (configuration, s1', s2', parallelize) = optimalTwoParallelConfigurations threads s1 s2+                    t source sink = liftM (\((((((([], l1), l2), l3), l4), l5), l6), l7)-> l7 ++ l6 ++ l5 ++ l4 ++ l3 ++ l2 ++ l1) $+                                    pipeD "splittersToPairMarker synchronize"+                                    (\sync->+                                     pipeD "splittersToPairMarker true1"+                                     (\true1->+                                      pipeD "splittersToPairMarker false1"+                                      (\false1->+                                       pipeD "splittersToPairMarker true2"+                                       (\true2->+                                        pipeD "splittersToPairMarker false2"+                                        (\false2->+                                         pipeD "splittersToPairMarker sink1"+                                         (\sink1->+                                          (if parallelize then pipeP else pipe)+                                          (\sink2-> tee source sink1 sink2)+                                          (\source2-> splitSections s2 source2 true2 false2))+                                         (\source1-> splitSections s1 source1 true1 false1))+                                        (mark sync False False))+                                       (mark sync False True))+                                      (mark sync True False))+                                     (mark sync True True))+                                    (synchronizeMarks Nothing sink)+                    synchronizeMarks :: Maybe (Seq (Maybe x, Bool), Bool)+                                     -> Sink c (Either (x, Bool, Bool) (Either Bool Bool)) -> Source c (Maybe x, Bool, Bool)+                                     -> Pipe c m [x]+                    synchronizeMarks state sink source = get source+                                                         >>= maybe+                                                                (assert (isNothing state) (return []))+                                                                (handleMark state sink source)+                    handleMark :: Maybe (Seq (Maybe x, Bool), Bool)+                               -> Sink c (Either (x, Bool, Bool) (Either Bool Bool)) -> Source c (Maybe x, Bool, Bool)+                               -> (Maybe x, Bool, Bool) -> Pipe c m [x]+                    handleMark Nothing sink source (x, pos, b)+                       = case x of Nothing -> put sink (Right $ if pos then Left b else Right b)+                                              >> synchronizeMarks Nothing sink source+                                   _ -> synchronizeMarks (Just (Seq.singleton (x, b), pos)) sink source+                    handleMark state@(Just (q, pos')) sink source mark@(x, pos, b)+                       | pos == pos' = synchronizeMarks (Just (q |> (x, b), pos')) sink source+                       | isNothing x = put sink (Right $ if pos then Left b else Right b)+                                       >> synchronizeMarks state sink source+                       | otherwise = case Seq.viewl q+                                     of Seq.EmptyL -> synchronizeMarks (Just (Seq.singleton (x, b), pos)) sink source+                                        (Nothing, b') :< rest -> put sink (Right $ if pos then Right b' else Left b')+                                                                 >>= cond+                                                                        (handleMark+                                                                           (if Seq.null rest then Nothing else Just (rest, pos'))+                                                                           sink+                                                                           source+                                                                           mark)+                                                                        (returnQueuedList q)+                                        (Just y, b') :< rest -> put sink (Left $ if pos then (y, b, b') else (y, b', b))+                                                                >>= cond+                                                                       (synchronizeMarks+                                                                           (if Seq.null rest then Nothing else Just (rest, pos'))+                                                                           sink+                                                                           source)+                                                                       (returnQueuedList q)+                    returnQueuedList q = return $ concatMap (maybe [] (:[]) . fst) $ Foldable.toList $ Seq.viewl q+                    mark sink first b source = let mark' = canPut sink+                                                           >>= cond+                                                                  (get source+                                                                   >>= maybe+                                                                          (return [])+                                                                          (\x-> put sink (x, first, b)+                                                                                   >>= cond mark' (return $ maybe [] (: []) x)))+                                                                  (return [])+                                               in mark'+                in (configuration, t)++pairMarkerToMaybePairMarker :: forall m x. (ParallelizableMonad m, Typeable x)+                               => Transducer m x (Either (x, Bool, Bool) (Either Bool Bool)) -> Transducer m x (Maybe x, Bool, Bool)+pairMarkerToMaybePairMarker t = liftTransducer "pairMarkerToMaybePairMarker" (maxUsableThreads t + 1) $+   \threads-> let t's = usingThreads threads t+                  t'p = usingThreads (threads - 1) t+                  parallel = threads > 1 Prelude.&& cost t'p <= cost t's+                  t' = if parallel then t'p else t's+                  cost' = if parallel then (cost t'p `max` 1) + 1 else cost t's + 1+                  transduce' source sink+                     = liftM (\(x, y)-> y ++ x) $+                       (if parallel then pipeP else pipe)+                          (transduce t source)+                          (\source-> let next state = get source >>= maybe (return []) state+                                         nextState2 l r d = get source+                                                            >>= maybe (put sink (Nothing, l, r) >> return []) (state2 l r d)+                                         state0 (Left (x, l, r)) = put sink (Just x, l, r)+                                                                   >>= cond (next $ state1 l r) (return [x])+                                         state0 v@(Right d) = state2 False False d v+                                         state1 _ _ (Left (x, l, r)) = put sink (Just x, l, r)+                                                                       >>= cond (next $ state1 l r) (return [x])+                                         state1 l r v@(Right d) = state2 l r d v+                                         state2 l r Left{} (Right d@(Left l')) = nextState2 l' r d+                                         state2 l r Left{} (Right (Right r')) = put sink (Nothing, l, r')+                                                                                >>= cond (next $ state1 l r') (return [])+                                         state2 l r Left{} t@(Left (x, l', r')) | l == l' = state1 l r t+                                                                                | otherwise = put sink (Nothing, l, r)+                                                                                              >>= cond+                                                                                                     (state1 l' r' t)+                                                                                                     (return [])+                                         state2 l r Right{} (Right d@(Right r')) = nextState2 l r' d+                                         state2 l r Right{} (Right (Left l')) = put sink (Nothing, l', r)+                                                                                >>= cond (next $ state1 l' r) (return [])+                                         state2 l r Right{} t@(Left (x, l', r')) | r == r' = state1 l r t+                                                                                 | otherwise = put sink (Nothing, l, r)+                                                                                               >>= cond+                                                                                                      (state1 l' r' t)+                                                                                                      (return [])+                                     in next state0)+              in (ComponentConfiguration [AnyComponent t'] threads cost', transduce')++zipSplittersWith :: (ParallelizableMonad m, Typeable x) => (Bool -> Bool -> Bool) -> Splitter m x -> Splitter m x -> Splitter m x+zipSplittersWith f s1 s2+   = liftSectionSplitter "zip" (maxUsableThreads s1 + maxUsableThreads s2) $+     \threads-> let (configuration, s1', s2', parallel) = optimalTwoParallelConfigurations threads s1 s2+                    s source true false = liftM (\(x, y)-> y ++ x) $+                                          (if parallel then pipeP else pipe)+                                             (transduce (pairMarkerToMaybePairMarker $ splittersToPairMarker s1 s2) source)+                                             (\source-> let split = get source >>= maybe (return []) test+                                                            test (x, b1, b2) = (if f b1 b2 then put true x else put false x)+                                                                               >>= cond split (return $ maybe [] (:[]) x)+                                                        in split)+                in (configuration, s)++groupMarks :: forall c m x y z. (ParallelizableMonad m, Typeable x, Typeable y, Eq y)+              => Source c (Maybe x, y) -> (y -> Source c x -> Pipe c m z) -> Pipe c m ()+groupMarks source getConsumer = getSuccess source startNew+   where startNew (mx, y) = do (nextPair, _) <- pipeD "groupMarks" (\sink-> pass sink mx y) (getConsumer y)+                               case nextPair of Just p -> startNew p+                                                Nothing -> return ()+         pass sink Nothing y = next sink y+         pass sink (Just x) y = put sink x >> next sink y+         next sink y = get source >>= maybe (return Nothing) (continue sink y)+         continue sink y (x, y') | y == y' = pass sink x y+         continue sink y p@(x, y') | y /= y' = return (Just p)++splitConsumer :: forall c m x r1 r2. (ParallelizableMonad m, Typeable x)+                 => String -> Bool -> Splitter m x -> (Source c x -> Pipe c m r1) -> (Source c x -> Pipe c m r2)+                           -> (Source c x -> Pipe c m ([x], r1, r2))+splitConsumer description parallel s trueConsumer falseConsumer = consumer'+   where consumer' source = (if parallel then pipeP else pipe)+                               (\false-> pipeD (description ++ " true") (\true-> split s source true false) trueConsumer)+                               falseConsumer+                            >>= \((extra, r1), r2)-> return (extra, r1, r2)++splitConsumerSections :: forall m x r1 r2. (ParallelizableMonad m, Typeable x) =>+                         String -> Splitter m x -> Consumer m (Maybe x) r1 -> Consumer m (Maybe x) r2 -> Consumer m x ([x], r1, r2)+splitConsumerSections description s trueConsumer falseConsumer+   = liftConsumer description (maxUsableThreads s + maxUsableThreads trueConsumer + maxUsableThreads falseConsumer) usingThreads+   where usingThreads :: Int -> (ComponentConfiguration, forall c. Source c x -> Pipe c m ([x], r1, r2))+         usingThreads threadCount = (configuration', consumer')+            where (configuration', (splitter', forkSplitter), (trueConsumer', forkTrue), (falseConsumer', forkFalse))+                     = optimalThreeParallelConfigurations threadCount s trueConsumer falseConsumer+                  consumer' source = (if forkFalse then pipeP else pipe)+                                        (\false-> (if forkTrue Prelude.|| forkSplitter then pipeP else pipe)+                                                     (\true-> splitSections s source true false)+                                                     (consume trueConsumer))+                                        (consume falseConsumer)+                                     >>= \((extra, r1), r2)-> return (extra, r1, r2)++putQueue :: forall c m x. (Monad m, Typeable x) => Seq x -> Sink c x -> Pipe c m [x]+putQueue q sink = putList (Foldable.toList (Seq.viewl q)) sink++getQueue :: forall c m x. (Monad m, Typeable x) => Source c x -> Pipe c m (Seq x)+getQueue source = let getOne q = get source >>= maybe (return q) (\x-> getOne (q |> x))+                  in getOne Seq.empty++pourMaybe :: forall c x m. (Monad m, Typeable x) => Source c x -> Sink c (Maybe x) -> Pipe c m ()+pourMaybe source sink = pour0+   where pour0 = canPut sink >>= flip when (get source >>= maybe (put sink Nothing >> return ()) pass)+         pour1 = canPut sink >>= flip when (getSuccess source pass)+         pass x = put sink (Just x) >> pour1+++suppressProducer :: forall c m x r. (ParallelizableMonad m, Typeable x) => (Sink c x -> Pipe c m r) -> Pipe c m r+suppressProducer p = liftM fst $ pipeD "suppress" p consumeAndSuppress  fst3 :: (a, b, c) -> a fst3 (a, b, c) = a
Control/Concurrent/SCC/ComponentTypes.hs view
@@ -14,100 +14,415 @@     <http://www.gnu.org/licenses/>. -} -{-# LANGUAGE ScopedTypeVariables, Rank2Types #-}+{-# LANGUAGE ScopedTypeVariables, MultiParamTypeClasses, FlexibleInstances, FunctionalDependencies,+             ExistentialQuantification, KindSignatures, Rank2Types, PatternSignatures #-}  module Control.Concurrent.SCC.ComponentTypes-   (-- * Types-    Splitter(..), Transducer(..),+   (-- * Classes+    Component (..), BranchComponent (combineBranches),+    -- * Types+    AnyComponent (AnyComponent), Performer (..), Consumer (..), Producer(..), Splitter(..), Transducer(..),+    ComponentConfiguration(..),     -- * Lifting functions-    lift121Transducer, liftStatelessTransducer, liftFoldTransducer, liftStatefulTransducer,-    liftSimpleSplitter, liftSectionSplitter, liftStatelessSplitter)+    liftPerformer, liftConsumer, liftAtomicConsumer, liftProducer, liftAtomicProducer,+    liftTransducer, liftAtomicTransducer, lift121Transducer, liftStatelessTransducer, liftFoldTransducer, liftStatefulTransducer,+    liftSimpleSplitter, liftSectionSplitter, liftAtomicSimpleSplitter, liftAtomicSectionSplitter, liftStatelessSplitter,+    -- * Utility functions+    showComponentTree, optimalTwoParallelConfigurations, optimalTwoSequentialConfigurations, optimalThreeParallelConfigurations+   ) where  import Control.Concurrent.SCC.Foundation  import Control.Monad (liftM, when)-import Data.Maybe (maybe)+import Data.List (minimumBy)+import Data.Maybe import Data.Typeable (Typeable, cast) +-- | 'AnyComponent' is an existential type wrapper around a 'Component'.+data AnyComponent = forall a. Component a => AnyComponent a++-- | The types of 'Component' class carry metadata and can be configured to use a specific number of threads.+class Component c where+   name :: c -> String+   -- | Returns the list of all children components.+   subComponents :: c -> [AnyComponent]+   -- | Returns the maximum number of threads that can be used by the component.+   maxUsableThreads :: c -> Int+   -- | Configures the component to use the specified number of threads. This function affects 'usedThreads', 'cost',+   -- and 'subComponents' methods of the result, while 'name' and 'maxUsableThreads' remain the same.+   usingThreads :: Int -> c -> c+   -- | The number of threads that the component is configured to use. By default the number is usually 1.+   usedThreads :: c -> Int+   -- | The cost of using the component as configured.+   cost :: c -> Int+   cost c = 1 + sum (map cost (subComponents c))++instance Component AnyComponent where+   name (AnyComponent c) = name c+   subComponents (AnyComponent c) = subComponents c+   maxUsableThreads (AnyComponent c) = maxUsableThreads c+   usingThreads n (AnyComponent c) = AnyComponent (usingThreads n c)+   usedThreads (AnyComponent c) = usedThreads c+   cost (AnyComponent c) = cost c++-- | Show details of the given component's configuration.+showComponentTree :: forall c. Component c => c -> String+showComponentTree c = showIndentedComponent 1 c++showIndentedComponent :: forall c. Component c => Int -> c -> String+showIndentedComponent depth c = showRightAligned 4 (cost c) ++ showRightAligned 3 (usedThreads c) ++ replicate depth ' '+                                ++ name c ++ "\n"+                                ++ concatMap (showIndentedComponent (succ depth)) (subComponents c)++showRightAligned :: Show x => Int -> x -> String+showRightAligned width x = let str = show x+                           in replicate (width - length str) ' ' ++ str++data ComponentConfiguration = ComponentConfiguration {componentChildren :: [AnyComponent],+                                                      componentThreads :: Int,+                                                      componentCost :: Int}++-- | A component that performs a computation with no inputs nor outputs is a 'Performer'.+data Performer m r = Performer {performerName :: String,+                                performerMaxThreads :: Int,+                                performerConfiguration :: ComponentConfiguration,+                                performerUsingThreads :: Int -> (ComponentConfiguration, forall c. Pipe c m r),+                                perform :: forall c. Pipe c m r}++-- | A component that consumes values from a 'Source' is called 'Consumer'.+-- data Consumer m x r = Consumer {consumerData :: ComponentData (forall c. Source c x -> Pipe c m r),+--                                 consume :: forall c. Source c x -> Pipe c m r}+data Consumer m x r = Consumer {consumerName :: String,+                                consumerMaxThreads :: Int,+                                consumerConfiguration :: ComponentConfiguration,+                                consumerUsingThreads :: Int -> (ComponentConfiguration, forall c. Source c x -> Pipe c m r),+                                consume :: forall c. Source c x -> Pipe c m r}++-- | A component that produces values and puts them into a 'Sink' is called 'Producer'.+data Producer m x r = Producer {producerName :: String,+                                producerMaxThreads :: Int,+                                producerConfiguration :: ComponentConfiguration,+                                producerUsingThreads :: Int -> (ComponentConfiguration, forall c. Sink c x -> Pipe c m r),+                                produce :: forall c. Sink c x -> Pipe c m r}+ -- | The 'Transducer' type represents computations that transform data and return no result. -- A transducer must continue consuming the given source and feeding the sink while there is data.-newtype Monad m => Transducer m x y = Transducer {transduce :: forall c1 c2 context. Source c1 x -> Sink c2 y -> Pipe context m [x]}+data Transducer m x y = Transducer {transducerName :: String,+                                    transducerMaxThreads :: Int,+                                    transducerConfiguration :: ComponentConfiguration,+                                    transducerUsingThreads :: Int -> (ComponentConfiguration,+                                                                      forall c. Source c x -> Sink c y -> Pipe c m [x]),+                                    transduce :: forall c. Source c x -> Sink c y -> Pipe c m [x]}  -- | The 'Splitter' type represents computations that distribute data acording to some criteria.  A splitter should -- distribute only the original input data, and feed it into the sinks in the same order it has been read from the -- source. If the two sink arguments of a splitter are the same, the splitter must act as an identity transform.-data Monad m => Splitter m x = Splitter {split :: forall c1 c2 c3 context.-                                                  Source c1 x -> Sink c2 x -> Sink c3 x -> Pipe context m [x],-                                         splitSections :: forall c1 c2 c3 context.-                                                          Source c1 x -> Sink c2 (Maybe x) -> Sink c3 (Maybe x)-                                                                      -> Pipe context m [x]}+data Splitter m x = Splitter {splitterName :: String,+                              splitterMaxThreads :: Int,+                              splitterConfiguration :: ComponentConfiguration,+                              splitterUsingThreads :: Int -> (ComponentConfiguration,+                                                              forall c. Source c x -> Sink c x -> Sink c x -> Pipe c m [x],+                                                              forall c. Source c x -> Sink c (Maybe x) -> Sink c (Maybe x)+                                                                                   -> Pipe c m [x]),+                              split :: forall c. Source c x -> Sink c x -> Sink c x -> Pipe c m [x],+                              splitSections :: forall c. Source c x -> Sink c (Maybe x) -> Sink c (Maybe x) -> Pipe c m [x]} +instance Component (Performer m r) where+   name = performerName+   subComponents = componentChildren . performerConfiguration+   maxUsableThreads = performerMaxThreads+   usedThreads = componentThreads . performerConfiguration+   usingThreads threads performer = let (configuration', perform' :: forall c. Pipe c m r) = performerUsingThreads performer threads+                                    in performer{performerConfiguration= configuration', perform= perform'}+   cost = componentCost . performerConfiguration++instance Component (Consumer m x r) where+   name = consumerName+   subComponents = componentChildren . consumerConfiguration+   maxUsableThreads = consumerMaxThreads+   usedThreads = componentThreads . consumerConfiguration+   usingThreads threads consumer = let (configuration',+                                        consume' :: forall c. Source c x -> Pipe c m r) = consumerUsingThreads consumer threads+                                   in consumer{consumerConfiguration= configuration', consume= consume'}+   cost = componentCost . consumerConfiguration++instance Component (Producer m x r) where+   name = producerName+   subComponents = componentChildren . producerConfiguration+   maxUsableThreads = producerMaxThreads+   usedThreads = componentThreads . producerConfiguration+   usingThreads threads producer = let (configuration',+                                        produce' :: forall c. Sink c x -> Pipe c m r) = producerUsingThreads producer threads+                                   in producer{producerConfiguration= configuration', produce= produce'}+   cost = componentCost . producerConfiguration++instance Component (Transducer m x y) where+   name = transducerName+   subComponents = componentChildren . transducerConfiguration+   maxUsableThreads = transducerMaxThreads+   usedThreads = componentThreads . transducerConfiguration+   usingThreads threads transducer = let (configuration', transduce' :: forall c. Source c x -> Sink c y -> Pipe c m [x])+                                            = transducerUsingThreads transducer threads+                                     in transducer{transducerConfiguration= configuration', transduce= transduce'}+   cost = componentCost . transducerConfiguration++instance Component (Splitter m x) where+   name = splitterName+   subComponents = componentChildren . splitterConfiguration+   maxUsableThreads = splitterMaxThreads+   usedThreads = componentThreads . splitterConfiguration+   usingThreads threads splitter = let (configuration',+                                        split' :: forall c. Source c x -> Sink c x -> Sink c x -> Pipe c m [x],+                                        splitSections' :: forall c. Source c x -> Sink c (Maybe x) -> Sink c (Maybe x)+                                                       -> Pipe c m [x])+                                            = splitterUsingThreads splitter threads+                                     in splitter{splitterConfiguration= configuration',+                                                 split= split', splitSections= splitSections'}+   cost = componentCost . splitterConfiguration+++-- | 'BranchComponent' is a type class representing all components that can act as consumers, namely 'Consumer',+-- 'Transducer', and 'Splitter'.+class BranchComponent cc m x r | cc -> m x where+   -- | 'combineBranches' is used to combine two components in 'BranchComponent' class into one, using the+   -- given 'Consumer' binary combinator.+   combineBranches :: String -> Int+                   -> (forall c. Bool -> (Source c x -> Pipe c m r) -> (Source c x -> Pipe c m r) -> (Source c x -> Pipe c m r))+                   -> cc -> cc -> cc++instance forall m x r. Monad m => BranchComponent (Consumer m x r) m x r where+   combineBranches name cost combinator c1 c2 = liftConsumer name 1 $+                                                \threads-> (ComponentConfiguration [AnyComponent c1, AnyComponent c2] 1 cost,+                                                            combinator False (consume c1) (consume c2))++instance forall m x. Monad m => BranchComponent (Consumer m x ()) m x [x] where+   combineBranches name cost combinator c1 c2 = liftConsumer name 1 $+                                                \threads-> (ComponentConfiguration [AnyComponent c1, AnyComponent c2] 1 cost,+                                                            liftM (const ())+                                                            . combinator False+                                                                 (\source-> consume c1 source >> return [])+                                                                 (\source-> consume c2 source >> return []))++instance forall m x y. BranchComponent (Transducer m x y) m x [x] where+   combineBranches name cost combinator t1 t2+      = liftTransducer name (maxUsableThreads t1 + maxUsableThreads t2) $+        \threads-> let (configuration, t1', t2', parallel) = optimalTwoParallelConfigurations threads t1 t2+                       transduce' source sink = combinator parallel+                                                   (\source-> transduce t1 source sink)+                                                   (\source-> transduce t2 source sink)+                                                   source+                   in (configuration, transduce')++instance forall m x. (ParallelizableMonad m, Typeable x) => BranchComponent (Splitter m x) m x [x] where+   combineBranches name cost combinator s1 s2+      = liftSimpleSplitter name (maxUsableThreads s1 + maxUsableThreads s2) $+        \threads-> let (configuration, s1', s2', parallel) = optimalTwoParallelConfigurations threads s1 s2+                       split' source true false = combinator parallel+                                                     (\source-> split s1 source true false)+                                                     (\source-> split s2 source true false)+                                                     source+                   in (configuration, split')++-- | Function 'liftPerformer' takes a component name, maximum number of threads it can use, and its 'usingThreads'+-- method, and returns a 'Performer' component.+liftPerformer :: String -> Int -> (Int -> (ComponentConfiguration, forall c. Pipe c m r)) -> Performer m r+liftPerformer name maxThreads usingThreads = case usingThreads 1+                                             of (configuration, perform) -> Performer name maxThreads configuration+                                                                                      usingThreads perform++-- | Function 'liftConsumer' takes a component name, maximum number of threads it can use, and its 'usingThreads'+-- method, and returns a 'Consumer' component.+liftConsumer :: String -> Int -> (Int -> (ComponentConfiguration, forall c. Source c x -> Pipe c m r)) -> Consumer m x r+liftConsumer name maxThreads usingThreads = case usingThreads 1+                                            of (configuration, consume) -> Consumer name maxThreads configuration+                                                                                    usingThreads consume++-- | Function 'liftProducer' takes a component name, maximum number of threads it can use, and its 'usingThreads'+-- method, and returns a 'Producer' component.+liftProducer :: String -> Int -> (Int -> (ComponentConfiguration, forall c. Sink c x -> Pipe c m r)) -> Producer m x r+liftProducer name maxThreads usingThreads = case usingThreads 1+                                            of (configuration, produce) -> Producer name maxThreads configuration+                                                                                    usingThreads produce++-- | Function 'liftTransducer' takes a component name, maximum number of threads it can use, and its 'usingThreads'+-- method, and returns a 'Transducer' component.+liftTransducer :: String -> Int -> (Int -> (ComponentConfiguration, forall c. Source c x -> Sink c y -> Pipe c m [x]))+               -> Transducer m x y+liftTransducer name maxThreads usingThreads = case usingThreads 1+                                              of (configuration, transduce) -> Transducer name maxThreads configuration+                                                                                          usingThreads transduce++-- | Function 'liftAtomicConsumer' lifts a single-threaded 'consume' function into a 'Consumer' component.+liftAtomicConsumer :: String -> Int -> (forall c. Source c x -> Pipe c m r) -> Consumer m x r+liftAtomicConsumer name cost consume = liftConsumer name 1 (\_threads-> (ComponentConfiguration [] 1 cost, consume))++-- | Function 'liftAtomicProducer' lifts a single-threaded 'produce' function into a 'Producer' component.+liftAtomicProducer :: String -> Int -> (forall c. Sink c x -> Pipe c m r) -> Producer m x r+liftAtomicProducer name cost produce = liftProducer name 1 (\_threads-> (ComponentConfiguration [] 1 cost, produce))++-- | Function 'liftAtomicTransducer' lifts a single-threaded 'transduce' function into a 'Transducer' component.+liftAtomicTransducer :: String -> Int -> (forall c. Source c x -> Sink c y -> Pipe c m [x]) -> Transducer m x y+liftAtomicTransducer name cost transduce = liftTransducer name 1 (\_threads-> (ComponentConfiguration [] 1 cost, transduce))+ -- | Function 'lift121Transducer' takes a function that maps one input value to one output value each, and lifts it into -- a 'Transducer'.-lift121Transducer :: (Monad m, Typeable x, Typeable y) => (x -> y) -> Transducer m x y-lift121Transducer f = Transducer (\source sink-> let t = canPut sink-                                                         >>= flip when (getSuccess source (\x-> put sink (f x) >> t))-                                                 in t >> return [])+lift121Transducer :: (Monad m, Typeable x, Typeable y) => String -> (x -> y) -> Transducer m x y+lift121Transducer name f = liftAtomicTransducer name 1 $+                           \source sink-> let t = canPut sink+                                                  >>= flip when (getSuccess source (\x-> put sink (f x) >> t))+                                          in t >> return []  -- | Function 'liftStatelessTransducer' takes a function that maps one input value into a list of output values, and -- lifts it into a 'Transducer'.-liftStatelessTransducer :: (Monad m, Typeable x, Typeable y) => (x -> [y]) -> Transducer m x y-liftStatelessTransducer f = Transducer (\source sink-> let t = canPut sink-                                                               >>= flip when (getSuccess source (\x-> putList (f x) sink >> t))-                                                       in t >> return [])+liftStatelessTransducer :: (Monad m, Typeable x, Typeable y) => String -> (x -> [y]) -> Transducer m x y+liftStatelessTransducer name f = liftAtomicTransducer name 1 $+                                 \source sink-> let t = canPut sink+                                                        >>= flip when (getSuccess source (\x-> putList (f x) sink >> t))+                                                in t >> return []+ -- | Function 'liftFoldTransducer' creates a stateful transducer that produces only one output value after consuming the -- entire input. Similar to 'Data.List.foldl'-liftFoldTransducer :: (Monad m, Typeable x, Typeable y) => (y -> x -> y) -> y -> Transducer m x y-liftFoldTransducer f y0 = Transducer (\source sink-> let t y = canPut sink-                                                               >>= flip when (get source-                                                                              >>= maybe (put sink y >> return ()) (t . f y))-                                                     in t y0 >> return [])+liftFoldTransducer :: (Monad m, Typeable x, Typeable y) => String -> (s -> x -> s) -> s -> (s -> y) -> Transducer m x y+liftFoldTransducer name f s0 w = liftAtomicTransducer name 1 $+                                 \source sink-> let t s = canPut sink+                                                          >>= flip when (get source+                                                                         >>= maybe+                                                                                (put sink (w s) >> return ())+                                                                                (t . f s))+                                                in t s0 >> return []  -- | Function 'liftStatefulTransducer' constructs a 'Transducer' from a state-transition function and the initial -- state. The transition function may produce arbitrary output at any transition step.-liftStatefulTransducer :: (Monad m, Typeable x, Typeable y) => (state -> x -> (state, [y])) -> state -> Transducer m x y-liftStatefulTransducer f s0 = Transducer (\source sink-> let t s = canPut sink-                                                                   >>= flip when (getSuccess source (\x-> let (s', ys) = f s x-                                                                                                          in putList ys sink-                                                                                                             >> t s'))-                                                         in t s0 >> return [])+liftStatefulTransducer :: (Monad m, Typeable x, Typeable y) => String -> (state -> x -> (state, [y])) -> state -> Transducer m x y+liftStatefulTransducer name f s0 = liftAtomicTransducer name 1 $+                                   \source sink-> let t s = canPut sink+                                                            >>= flip when (getSuccess source+                                                                              (\x-> let (s', ys) = f s x+                                                                                    in putList ys sink >> t s'))+                                                  in t s0 >> return []  -- | Function 'liftStatelessSplitter' takes a function that assigns a Boolean value to each input item and lifts it into--- a 'Splitter'-liftStatelessSplitter :: (Monad m, Typeable x) => (x -> Bool) -> Splitter m x-liftStatelessSplitter f = liftSimpleSplitter (\source true false-> let s = get source-                                                                           >>= maybe-                                                                                  (return [])-                                                                                  (\x-> (if f x-                                                                                         then put true x-                                                                                         else put false x)-                                                                                   >>= cond s (return [x]))-                                                                   in s)+-- a 'Splitter'.+liftStatelessSplitter :: (ParallelizableMonad m, Typeable x) => String -> (x -> Bool) -> Splitter m x+liftStatelessSplitter name f = liftAtomicSimpleSplitter name 1 $+                               \source true false-> let s = get source+                                                            >>= maybe+                                                                   (return [])+                                                                   (\x-> (if f x+                                                                          then put true x+                                                                          else put false x)+                                                                    >>= cond s (return [x]))+                                                    in s --- | Function 'liftSimpleSplitter' lifts a simple, non-sectioning splitter function into a full 'Splitter'-liftSimpleSplitter :: (Monad m, Typeable x) =>-                      (forall c1 c2 c3 context. Source c1 x -> Sink c2 x -> Sink c3 x -> Pipe context m [x]) -> Splitter m x-liftSimpleSplitter split = Splitter split splitSections-   where splitSections source true false+-- | Function 'liftSimpleSplitter' lifts a simple, non-sectioning splitter function into a full 'Splitter'.+liftSimpleSplitter :: forall m x. (ParallelizableMonad m, Typeable x) =>+                      String -> Int+                             -> (Int -> (ComponentConfiguration, forall c. Source c x -> Sink c x -> Sink c x -> Pipe c m [x]))+                             -> Splitter m x+liftSimpleSplitter name maxThreads usingThreads+   = case usingThreads 1+     of (configuration, split) -> Splitter name maxThreads configuration usingThreads' split (splitSections split)+   where usingThreads' :: Int -> (ComponentConfiguration,+                                  forall c. Source c x -> Sink c x -> Sink c x -> Pipe c m [x],+                                  forall c. Source c x -> Sink c (Maybe x) -> Sink c (Maybe x) -> Pipe c m [x])+         usingThreads' threads = case usingThreads threads+                                 of (configuration, splitValues) -> (configuration, splitValues, splitSections splitValues)+         splitSections split source true false             = liftM (fst . fst) $               pipeD "liftSimpleSplitter true"                     (\true'-> pipeD "liftSimpleSplitter false"                                     (\false'-> split source true' false')                                     (decorate false))                     (decorate true)-         decorate sink source = transduce (lift121Transducer Just) source sink+         decorate sink source = transduce (lift121Transducer "Just" Just) source sink + -- | Function 'liftSectionSplitter' lifts a sectioning splitter function into a full 'Splitter'-liftSectionSplitter :: (Monad m, Typeable x) =>-                      (forall c1 c2 c3 context. Source c1 x -> Sink c2 (Maybe x) -> Sink c3 (Maybe x) -> Pipe context m [x])-                         -> Splitter m x-liftSectionSplitter splitSections = Splitter splitValues splitSections-   where splitValues source true false+liftSectionSplitter :: forall m x. (ParallelizableMonad m, Typeable x) =>+                       String -> Int -> (Int -> (ComponentConfiguration,+                                                 forall c. Source c x -> Sink c (Maybe x) -> Sink c (Maybe x) -> Pipe c m [x]))+                              -> Splitter m x+liftSectionSplitter name maxThreads usingThreads+   = case usingThreads 1+     of (configuration, splitSections) -> Splitter name 1 configuration usingThreads' (splitValues splitSections) splitSections+   where usingThreads' :: Int -> (ComponentConfiguration,+                                  forall c. Source c x -> Sink c x -> Sink c x -> Pipe c m [x],+                                  forall c. Source c x -> Sink c (Maybe x) -> Sink c (Maybe x) -> Pipe c m [x])+         usingThreads' threads = case usingThreads threads+                                 of (configuration, splitSections) -> (configuration, splitValues splitSections, splitSections)+         splitValues splitSections source true false             = liftM (fst . fst) $               pipeD "liftSectionSplitter true"                     (\true'-> pipeD "liftSectionSplitter false" (\false'-> splitSections source true' false') (strip false))                     (strip true)+         strip sink source = canPut sink+                             >>= flip when (getSuccess source (\x-> maybe (return False) (put sink) x >> strip sink source))++-- | Function 'liftAtomicSimpleSplitter' lifts a single-threaded 'split' function into a 'Splitter' component.+liftAtomicSimpleSplitter :: forall m x. (ParallelizableMonad m, Typeable x) =>+                      String -> Int -> (forall c. Source c x -> Sink c x -> Sink c x -> Pipe c m [x]) -> Splitter m x+liftAtomicSimpleSplitter name cost split = liftSimpleSplitter name 1 (\_threads-> (ComponentConfiguration [] 1 cost, split))++-- | Function 'liftAtomicSectionSplitter' lifts a single-threaded 'splitSections' function into a full 'Splitter'+-- component.+liftAtomicSectionSplitter :: forall m x. (ParallelizableMonad m, Typeable x) =>+                             String -> Int -> (forall c. Source c x -> Sink c (Maybe x) -> Sink c (Maybe x) -> Pipe c m [x])+                                    -> Splitter m x+liftAtomicSectionSplitter name cost splitSections = liftSectionSplitter name 1 $+                                                    \_threads-> (ComponentConfiguration [] 1 cost, splitSections)+   where configuration = ComponentConfiguration [] 1 1+         usingThreads :: Int -> (ComponentConfiguration,+                                 forall c. Source c x -> Sink c x -> Sink c x -> Pipe c m [x],+                                 forall c. Source c x -> Sink c (Maybe x) -> Sink c (Maybe x) -> Pipe c m [x])+         usingThreads threads = (configuration, splitValues, splitSections)+         splitValues source true false+            = liftM (fst . fst) $+              pipeD "liftSectionSplitter true"+                    (\true'-> pipeD "liftSectionSplitter false" (\false'-> splitSections source true' false') (strip false))+                    (strip true) --         strip sink source = transduce (liftStatelessTransducer (maybe [] (:[]))) source sink          strip sink source = canPut sink                              >>= flip when (getSuccess source (\x-> maybe (return False) (put sink) x >> strip sink source))++-- | Function 'optimalTwoParallelConfigurations' configures two components, both of them with the full thread count, and+-- returns the components and a 'ComponentConfiguration' that can be used to build a new component from them.+optimalTwoSequentialConfigurations :: (Component c1, Component c2) => Int -> c1 -> c2 -> (ComponentConfiguration, c1, c2)+optimalTwoSequentialConfigurations threads c1 c2 = (configuration, c1', c2')+   where configuration = ComponentConfiguration+                            [AnyComponent c1', AnyComponent c2']+                            (usedThreads c1' `max` usedThreads c2')+                            (cost c1' + cost c2')+         c1' = usingThreads threads c1+         c2' = usingThreads threads c2++-- | Function 'optimalTwoParallelConfigurations' configures two components assuming they can be run in parallel,+-- splitting the given thread count between them, and returns the configured components, a 'ComponentConfiguration' that+-- can be used to build a new component from them, and a flag that indicates if they should be run in parallel or+-- sequentially for optimal resource usage.+optimalTwoParallelConfigurations :: (Component c1, Component c2) => Int -> c1 -> c2 -> (ComponentConfiguration, c1, c2, Bool)+optimalTwoParallelConfigurations threads c1 c2 = (configuration, c1', c2', parallelize)+   where parallelize = threads > 1 && parallelCost + 1 < sequentialCost+         configuration = ComponentConfiguration+                            [AnyComponent c1', AnyComponent c2']+                            (if parallelize then usedThreads c1' + usedThreads c2' else usedThreads c1' `max` usedThreads c2')+                            (if parallelize then parallelCost + 1 else sequentialCost)+         (c1', c2') = if parallelize then (c1p, c2p) else (c1s, c2s)+         (c1p, c2p, parallelCost) = minimumBy+                                       (\(_, _, cost1) (_, _, cost2)-> compare cost1 cost2)+                                       [let c2threads = threads - c1threads `min` maxUsableThreads c2+                                            c1i = usingThreads c1threads c1+                                            c2i = usingThreads c2threads c2+                                        in (c1i, c2i, cost c1i `max` cost c2i)+                                        | c1threads <- [1 .. threads - 1 `min` maxUsableThreads c1]]+         c1s = usingThreads threads c1+         c2s = usingThreads threads c2+         sequentialCost = cost c1s + cost c2s++-- | Function 'optimalThreeParallelConfigurations' configures three components assuming they can be run in parallel,+-- splitting the given thread count between them, and returns the components, a 'ComponentConfiguration' that can be+-- used to build a new component from them, and a flag per component that indicates if it should be run in parallel or+-- sequentially for optimal resource usage.+optimalThreeParallelConfigurations :: (Component c1, Component c2, Component c3) =>+                                      Int -> c1 -> c2 -> c3 -> (ComponentConfiguration, (c1, Bool), (c2, Bool), (c3, Bool))+optimalThreeParallelConfigurations threadCount c1 c2 c3 = undefined
Control/Concurrent/SCC/Components.hs view
@@ -20,14 +20,17 @@ {-# LANGUAGE ScopedTypeVariables, Rank2Types #-}  module Control.Concurrent.SCC.Components-   (-- * IO components+   (-- * List producers and consumers+    fromList, toList,+    -- * I/O producers and consumers     fromFile, fromHandle, fromStdIn,     appendFile, toFile, toHandle, toStdOut, toPrint,+    -- * Generic consumers+    suppress, erroneous,     -- * Generic transducers-    asis, suppress, erroneous,-    prepend, append, substitute,+    asis,     -- * Generic splitters-    allTrue, allFalse, one, substring, substringMatch,+    everything, nothing, one, substring, substringMatch,     -- * List transducers     -- | The following laws hold:     --@@ -38,7 +41,8 @@     -- * Character stream components     lowercase, uppercase, whitespace, letters, digits, line, nonEmptyLine,     -- * Oddballs-    count, toString+    count, toString,+    ioCost ) where @@ -59,142 +63,133 @@ import System.IO (Handle, IOMode (ReadMode, WriteMode, AppendMode), openFile, hClose,                   hGetChar, hPutChar, hFlush, hIsEOF, hClose, putChar, isEOF, stdout) +ioCost :: Int+ioCost = 5 +-- | Consumer 'toList' copies the given source into a list.+toList :: forall m x. (Monad m, Typeable x) => Consumer m x [x]+toList = liftAtomicConsumer "toList" 1 getList++-- | 'fromList' produces the contents of the given list argument.+fromList :: forall m x. (Monad m, Typeable x) => [x] -> Producer m x [x]+fromList l = liftAtomicProducer "fromList" 1 (putList l)+ -- | Consumer 'toStdOut' copies the given source into the standard output. toStdOut :: Consumer IO Char ()-toStdOut source = getSuccess source (\x-> liftPipe (putChar x) >> toStdOut source)+toStdOut = liftAtomicConsumer "toStdOut" ioCost $ \source-> let c = get source+                                                                    >>= maybe (return ()) (\x-> liftPipe (putChar x) >> c)+                                                            in c  toPrint :: forall x. (Show x, Typeable x) => Consumer IO x ()-toPrint source = getSuccess source (\x-> liftPipe (print x) >> toPrint source)+toPrint = liftAtomicConsumer "toPrint" ioCost $ \source-> let c = getSuccess source (\x-> liftPipe (print x) >> c)+                                                          in c  -- | Producer 'fromStdIn' feeds the given sink from the standard input. fromStdIn :: Producer IO Char ()-fromStdIn sink = do readyInput <- liftM not (liftPipe isEOF)-                    readyOutput <- canPut sink-                    when (readyInput && readyOutput) (liftPipe getChar >>= put sink >> fromStdIn sink)+fromStdIn = liftAtomicProducer "fromStdIn" ioCost $ \sink-> let p = do readyInput <- liftM not (liftPipe isEOF)+                                                                       readyOutput <- canPut sink+                                                                       when (readyInput && readyOutput) (liftPipe getChar+                                                                                                         >>= put sink+                                                                                                         >> p)+                                                            in p  -- | Producer 'fromFile' opens the named file and feeds the given sink from its contents. fromFile :: String -> Producer IO Char ()-fromFile path sink = liftPipe (openFile path ReadMode) >>= flip fromHandle sink+fromFile path = liftAtomicProducer "fromFile" ioCost $ \sink-> do handle <- liftPipe (openFile path ReadMode)+                                                                  produce (fromHandle handle True) sink --- | Producer 'fromHandle' feeds the given sink from the open file /handle/.-fromHandle :: Handle -> Producer IO Char ()-fromHandle handle sink = producer-   where producer = do readyInput <- liftM not (liftPipe (hIsEOF handle))-                       readyOutput <- canPut sink-                       when (readyInput && readyOutput) (liftPipe (hGetChar handle) >>= put sink >> producer)+-- | Producer 'fromHandle' feeds the given sink from the open file /handle/. The argument /doClose/ determines if+-- | /handle/ should be closed when the handle is consumed or the sink closed.+fromHandle :: Handle -> Bool -> Producer IO Char ()+fromHandle handle doClose = liftAtomicProducer "fromHandle" ioCost $+                            \sink-> (canPut sink+                                     >>= flip when (let p = do eof <- liftPipe (hIsEOF handle)+                                                               when (not eof) (liftPipe (hGetChar handle)+                                                                               >>= put sink+                                                                               >>= flip when p)+                                                    in p)+                                     >> when doClose (liftPipe $ hClose handle))  -- | Consumer 'toFile' opens the named file and copies the given source into it. toFile :: String -> Consumer IO Char ()-toFile path source = liftPipe (openFile path WriteMode) >>= flip toHandle source+toFile path = liftAtomicConsumer "toFile" ioCost $ \source-> do handle <- liftPipe (openFile path WriteMode)+                                                                consume (toHandle handle True) source  -- | Consumer 'appendFile' opens the name file and appends the given source to it. appendFile :: String -> Consumer IO Char ()-appendFile path source = liftPipe (openFile path AppendMode) >>= flip toHandle source+appendFile path = liftAtomicConsumer "appendFile" ioCost $ \source-> do handle <- liftPipe (openFile path AppendMode)+                                                                        consume (toHandle handle True) source --- | Consumer 'toHandle' copies the given source into the open file /handle/.-toHandle :: Handle -> Consumer IO Char ()-toHandle handle source = getSuccess source (\x-> liftPipe (hPutChar handle x) >> toHandle handle source)+-- | Consumer 'toHandle' copies the given source into the open file /handle/. The argument /doClose/ determines if+-- | /handle/ should be closed once the entire source is consumed and copied.+toHandle :: Handle -> Bool -> Consumer IO Char ()+toHandle handle doClose = liftAtomicConsumer "toHandle" ioCost $ \source-> let c = get source+                                                                                   >>= maybe+                                                                                          (when doClose $ liftPipe $ hClose handle)+                                                                                          (\x-> liftPipe (hPutChar handle x) >> c)+                                                                           in c  -- | Transducer 'asis' passes its input through unmodified.-asis :: (Monad m, Typeable x) => Transducer m x x-asis = Transducer (\source sink-> pour source sink >> return [])+asis :: forall m x. (Monad m, Typeable x) => Transducer m x x+asis = lift121Transducer "asis" id  -- | The 'suppress' transducer suppresses all input it receives. It is equivalent to 'substitute' []-suppress :: (Monad m, Typeable x, Typeable y) => Transducer m x y-suppress = liftStatelessTransducer (const [])+suppress :: forall m x y. (Monad m, Typeable x) => Consumer m x ()+suppress = liftAtomicConsumer "suppress" 1 consumeAndSuppress  -- | The 'erroneous' transducer reports an error if any input reaches it.-erroneous :: (Monad m, Typeable x) => Transducer m x x-erroneous = liftStatelessTransducer (\x-> error "Erroneous.")+erroneous :: forall m x. (Monad m, Typeable x) => String -> Consumer m x ()+erroneous message = liftAtomicConsumer "erroneous" 0 $ \source-> get source >>= maybe (return ()) (const (error message))  -- | The 'lowercase' transforms all uppercase letters in the input to lowercase, leaving the rest unchanged.-lowercase :: Monad m => Transducer m Char Char-lowercase = lift121Transducer toLower+lowercase :: forall m. Monad m => Transducer m Char Char+lowercase = lift121Transducer "lowercase" toLower  -- | The 'uppercase' transforms all lowercase letters in the input to uppercase, leaving the rest unchanged.-uppercase :: Monad m => Transducer m Char Char-uppercase = lift121Transducer toUpper---- | Transducer 'prepend' passes its input unmodified, except for prepending contents of the given list parameter before--- it.-prepend :: (Monad m, Typeable x) => [x] -> Transducer m x x-prepend prefix = Transducer (\source sink-> putList prefix sink >>= whenNull (pour source sink >> return []))---- | Transducer 'append' passes its input unmodified, except for appending contents of the given list parameter to--- its end.-append :: (Monad m, Typeable x) => [x] -> Transducer m x x-append suffix = Transducer (\source sink-> do pour source sink-                                              putList suffix sink-                                              return [])---- | The 'substitute' transducer replaces its whole input by its parameter.-substitute :: (Monad m, Typeable x, Typeable y) => [y] -> Transducer m x y-substitute list = Transducer (\source sink-> consumeAndSuppress source >> putList list sink >> return [])+uppercase :: forall m. Monad m => Transducer m Char Char+uppercase = lift121Transducer "uppercase" toUpper  -- | The 'count' transducer counts all its input values and outputs the final tally.-count :: (Monad m, Typeable x) => Transducer m x Integer-count = Transducer (\source sink-> let t count = get source-                                                 >>= maybe-                                                        (put sink count >> return [])-                                                        (\_-> t (succ count))-                                   in canPut sink >>= cond (t 0) (return []))+count :: forall m x. (Monad m, Typeable x) => Transducer m x Integer+count = liftFoldTransducer "count" (\count _-> succ count) 0 id -toString :: (Monad m, Show x, Typeable x) => Transducer m x String-toString = lift121Transducer show+toString :: forall m x. (Monad m, Show x, Typeable x) => Transducer m x String+toString = lift121Transducer "toString" show  -- | Transducer 'group' collects all its input values into a single list.-group :: (Monad m, Typeable x) => Transducer m x [x]-group = Transducer (\source sink-> let group q = get source-                                                 >>= maybe-                                                        (let list = Foldable.toList (Seq.viewl q)-                                                         in put sink list-                                                               >>= cond-                                                                      (return [])-                                                                      (return list))-                                                        (\x-> group (q |> x))-                                   in group Seq.empty)+group :: forall m x. (Monad m, Typeable x) => Transducer m x [x]+group = liftFoldTransducer "group" (|>) Seq.empty Foldable.toList  -- | Transducer 'concatenate' flattens the input stream of lists of values into the output stream of values.-concatenate :: (Monad m, Typeable x) => Transducer m [x] x-concatenate = liftStatelessTransducer id+concatenate :: forall m x. (Monad m, Typeable x) => Transducer m [x] x+concatenate = liftStatelessTransducer "concatenate" id -concatSeparate :: (Monad m, Typeable x) => [x] -> Transducer m [x] x-concatSeparate separator = Transducer (\source sink-> let t = canPut sink-                                                              >>= cond-                                                                     (get source-                                                                      >>= maybe-                                                                             (return [])-                                                                             (\xs-> do putList separator sink-                                                                                       putList xs sink-                                                                                       t))-                                                                     (return [])-                                                      in get source-                                                            >>= maybe-                                                                   (return [])-                                                                   (\xs-> putList xs sink >> t))+concatSeparate :: forall m x. (Monad m, Typeable x) => [x] -> Transducer m [x] x+concatSeparate separator = liftStatefulTransducer "concatSeparate"+                                                  (\seen list-> (True, if seen then separator ++ list else list))+                                                  False   -- | Splitter 'whitespace' feeds all white-space characters into its /true/ sink, all others into /false/.-whitespace :: Monad m => Splitter m Char-whitespace = liftStatelessSplitter isSpace+whitespace :: forall m. ParallelizableMonad m => Splitter m Char+whitespace = liftStatelessSplitter "whitespace" isSpace  -- | Splitter 'letters' feeds all alphabetical characters into its /true/ sink, all other characters into /false/.-letters :: Monad m => Splitter m Char-letters = liftStatelessSplitter isAlpha+letters :: forall m. ParallelizableMonad m => Splitter m Char+letters = liftStatelessSplitter "letters" isAlpha  -- | Splitter 'digits' feeds all digits into its /true/ sink, all other characters into /false/.-digits :: Monad m => Splitter m Char-digits = liftStatelessSplitter isDigit+digits :: forall m. ParallelizableMonad m => Splitter m Char+digits = liftStatelessSplitter "digits" isDigit  -- | Splitter 'nonEmptyLine' feeds line-ends into its /false/ sink, and all other characters into /true/.-nonEmptyLine :: Monad m => Splitter m Char-nonEmptyLine = liftStatelessSplitter (\ch-> ch /= '\n' && ch /= '\r')+nonEmptyLine :: forall m. ParallelizableMonad m => Splitter m Char+nonEmptyLine = liftStatelessSplitter "nonEmptyLine" (\ch-> ch /= '\n' && ch /= '\r')  -- | The sectioning splitter 'line' feeds line-ends into its /false/ sink, and line contents into /true/. A single -- line-end can be formed by any of the character sequences \"\\n\", \"\\r\", \"\\r\\n\", or \"\\n\\r\".-line :: Monad m => Splitter m Char-line = liftSectionSplitter (\source true false->-                            let split0 = get source >>= maybe (return []) split1+line :: forall m. ParallelizableMonad m => Splitter m Char+line = liftAtomicSectionSplitter "line" 1 $+       \source true false-> let split0 = get source >>= maybe (return []) split1                                 split1 x = if x == '\n' || x == '\r'                                            then split2 x                                            else lineChar x@@ -220,43 +215,44 @@                                                   (return [x])                                 emptyLine x = put true Nothing >>= cond (split2 x) (return [])                                 lineChar x = put true (Just x) >>= cond split0 (return [x])-                            in split0)+                            in split0 --- | Splitter 'allTrue' feeds its entire input into its /true/ sink.-allTrue :: (Monad m, Typeable x) => Splitter m x-allTrue = liftStatelessSplitter (const True)+-- | Splitter 'everything' feeds its entire input into its /true/ sink.+everything :: forall m x. (ParallelizableMonad m, Typeable x) => Splitter m x+everything = liftStatelessSplitter "everything" (const True) --- | Splitter 'allFalse' feeds its entire input into its /false/ sink.-allFalse :: (Monad m, Typeable x) => Splitter m x-allFalse = liftStatelessSplitter (const False)+-- | Splitter 'nothing' feeds its entire input into its /false/ sink.+nothing :: forall m x. (ParallelizableMonad m, Typeable x) => Splitter m x+nothing = liftStatelessSplitter "nothing" (const False)  -- | Splitter 'one' feeds all input values to its /true/ sink, treating every value as a separate section.-one :: (Monad m, Typeable x) => Splitter m x-one = liftSectionSplitter (\source true false-> let split x = put true (Just x)-                                                              >>= cond (get source-                                                                        >>= maybe-                                                                               (return [])-                                                                               (\x-> put false Nothing >> split x))-                                                                       (return [x])-                                                in get source >>= maybe (return []) split)+one :: forall m x. (ParallelizableMonad m, Typeable x) => Splitter m x+one = liftAtomicSectionSplitter "one" 1 $+      \source true false-> let split x = put true (Just x)+                                         >>= cond (get source+                                                   >>= maybe+                                                          (return [])+                                                          (\x-> put false Nothing >> split x))+                                                  (return [x])+                           in get source >>= maybe (return []) split  -- | Splitter 'substring' feeds to its /true/ sink all input parts that match the contents of the given list -- argument. If two overlapping parts of the input both match the argument, only the first one wins.-substring :: (Monad m, Eq x, Typeable x) => [x] -> Splitter m x-substring = substringPrim False+substring :: forall m x. (ParallelizableMonad m, Eq x, Typeable x) => [x] -> Splitter m x+substring = substringPrim "substring" False  -- | Splitter 'substringMatch' feeds to its /true/ sink all input parts that match the contents of the given list -- argument. If two overlapping parts of the input match the argument, both are considered /true/.-substringMatch :: (Monad m, Eq x, Typeable x) => [x] -> Splitter m x-substringMatch = substringPrim True+substringMatch :: forall m x. (ParallelizableMonad m, Eq x, Typeable x) => [x] -> Splitter m x+substringMatch = substringPrim "substringMatch" True -substringPrim _ [] = liftSectionSplitter (\ source true false ->-                                             do put true Nothing-                                                rest <- splitSections one source false true-                                                put true Nothing-                                                return rest)-substringPrim overlap list-   = liftSectionSplitter $+substringPrim name _ [] = liftAtomicSectionSplitter name 1 $+                          \ source true false -> do put true Nothing+                                                    rest <- splitSections one source false true+                                                    put true Nothing+                                                    return rest+substringPrim name overlap list+   = liftAtomicSectionSplitter name 1 $      \ source true false ->         let getNext rest q separate = get source                                       >>= maybe
Control/Concurrent/SCC/Foundation.hs view
@@ -19,29 +19,62 @@ {-# LANGUAGE ScopedTypeVariables, Rank2Types, PatternGuards, ExistentialQuantification #-}  module Control.Concurrent.SCC.Foundation-   (-- * Types-    Pipe, Source, Sink, Consumer, Producer,+   (-- * Classes+    ParallelizableMonad (parallelize),+    -- * Types+    Pipe, Source, Sink,     -- * Flow-control functions-    pipe, pipeD, get, getSuccess, canPut, put,+    pipe, pipeD, pipeP, get, getSuccess, canPut, put,     liftPipe, runPipes,     -- * Utility functions     cond, whenNull, pour, tee, getList, putList, consumeAndSuppress) where +import Control.Concurrent (forkIO)+import Control.Concurrent.MVar (newEmptyMVar, putMVar, takeMVar) import Control.Exception (assert)-import Control.Monad (liftM, when)+import Control.Monad (liftM, liftM2, when)+import Control.Monad.Identity+import Control.Parallel (par, pseq) import Data.Maybe (maybe) import Data.Typeable (Typeable, cast)  import Debug.Trace (trace) +class Monad m => ParallelizableMonad m where+   parallelize :: m a -> m b -> m (a, b)+   parallelize = liftM2 (,)++instance ParallelizableMonad Identity where+   parallelize ma mb = let a = runIdentity ma+                           b = runIdentity mb+                       in  a `par` (b `pseq` Identity (a, b))++instance ParallelizableMonad Maybe where+   parallelize ma mb = case ma `par` (mb `pseq` (ma, mb))+                       of (Just a, Just b) -> Just (a, b)+                          _ -> Nothing+++instance ParallelizableMonad IO where+   parallelize ma mb = do va <- newEmptyMVar+                          vb <- newEmptyMVar+                          forkIO (ma >>= putMVar va)+                          forkIO (mb >>= putMVar vb)+                          a <- takeMVar va+                          b <- takeMVar vb+                          return (a, b)+                          + -- | 'Pipe' represents the type of monadic computations that can be split into co-routining computations using function -- 'pipe'. The /context/ type parameter delimits the scope of the computation.-newtype Monad m => Pipe context m r = Pipe {proceed :: context -> Int -> Integer -> m (PipeState context m r)}-data PipeState context m r = Suspend context [Suspension context m r]-                           | Done Integer r-data Suspension context m r = Suspension {pid :: Int,-                                          clock :: Integer,+newtype Pipe context m r = Pipe {proceed :: PipeState context -> m (PipeRendezvous context m r)}+data PipeState context = PipeState {level :: Int,+                                    clock :: Integer}+data PipeRendezvous context m r = Suspend [Suspension context m r]+                                | Done Integer r+data Suspension context m r = Suspension {targetLevel :: Int,+                                          state :: PipeState context,                                           description :: String,                                           continuation :: SuspendedContinuation context m r} data SuspendedContinuation context m r = forall x. Typeable x => Get (Maybe x -> Pipe context m r)@@ -49,119 +82,148 @@                                        | CanPut (Bool -> Pipe context m r)  -- | A 'Source' is the read-only end of a 'Pipe' communication channel.-data Source context x = Source context Int String+data Source context x = Source Int String -- | A 'Sink' is the write-only end of a 'Pipe' communication channel.-data Sink   context x = Sink   context Int String+data Sink   context x = Sink   Int String  -- | A computation that consumes values from a 'Source' is called 'Consumer'.-type Consumer m x r = forall c. Source c x -> Pipe c m r+type Consumer c m x r = Source c x -> Pipe c m r -- | A computation that produces values and puts them into a 'Sink' is called 'Producer'.-type Producer m x r = forall c. Sink c x -> Pipe c m r+type Producer c m x r = Sink c x -> Pipe c m r  -- | Function 'liftPipe' lifts a value of the underlying monad type into a 'Pipe' computation. liftPipe :: forall context m r. Monad m => m r -> Pipe context m r-liftPipe mr = Pipe (\context pid clock-> liftM (Done clock) mr)+liftPipe mr = Pipe (\state-> liftM (Done (clock state)) mr)  -- | Function 'runPipes' runs the given computation involving pipes and returns the final result. -- The /context/ argument ensures that no suspended computation can escape its scope. runPipes :: forall m r. Monad m => (forall context. Pipe context m r) -> m r-runPipes c = proceed c undefined 1 0 >>= \s-> case s of Done _ r -> return r+runPipes c = proceed c (PipeState 1 0) >>= \s-> case s of Done _ r -> return r  instance Monad m => Monad (Pipe context m) where-   return r = Pipe (\context pid clock-> return (Done clock r))-   Pipe p >>= f = Pipe (\context pid clock-> p context pid clock >>= apply f context pid)-      where apply :: forall r1 r2. (r1 -> Pipe context m r2) -> context -> Int -> PipeState context m r1 -> m (PipeState context m r2)-            apply f context pid (Done clock r) = proceed (f r) context pid (succ clock)-            apply f _ pid (Suspend context suspensions) = return $ Suspend context (map suspendApplied suspensions)-               where suspendApplied s@Suspension{description= desc, clock= clock', continuation= Get cont}-                        = s{description= "applied " ++ desc, continuation= Get ((f =<<) . cont)}-                     suspendApplied s@Suspension{description= desc, clock= clock', continuation= Put x cont}-                        = s{description= "applied " ++ desc, continuation= Put x ((f =<<) . cont)}-                     suspendApplied s@Suspension{description= desc, clock= clock', continuation= CanPut cont}-                        = s{description= "applied " ++ desc, continuation= CanPut ((f =<<) . cont)}+   return r = Pipe (\state-> return (Done (clock state) r))+   Pipe p >>= f = Pipe (\state-> p state >>= apply f state)+      where apply :: forall r1 r2. (r1 -> Pipe context m r2) -> PipeState context -> PipeRendezvous context m r1+                  -> m (PipeRendezvous context m r2)+            apply f state (Done t r) = proceed (f r) state{clock= succ t}+            apply f state (Suspend suspensions) = return $ Suspend (map suspendApplied suspensions)+               where suspendApplied s = postApply (>>= f) s{description= "applied " ++ description s} +postApply :: (Pipe context m r1 -> Pipe context m r2) -> Suspension context m r1 -> Suspension context m r2+postApply f s = s{continuation= case continuation s of Get cont -> Get (f . cont)+                                                       Put x cont -> Put x (f . cont)+                                                       CanPut cont -> CanPut (f . cont)}++instance ParallelizableMonad m => ParallelizableMonad (Pipe context m) where+   parallelize p1 p2 = Pipe (\state-> liftM combine $ parallelize (proceed p1 state) (proceed p2 state))+      where combine :: forall r1 r2. (PipeRendezvous context m r1, PipeRendezvous context m r2) -> PipeRendezvous context m (r1, r2)+            combine (Done c1 r1, Done c2 r2) = Done (max c1 c2) (r1, r2)+            combine (Suspend s1, Done c2 r2) = Suspend (map (adjustSuspension c2 (liftM $ flip (,) r2)) s1)+            combine (Done c1 r1, Suspend s2) = Suspend (map (adjustSuspension c1 (liftM $ (,) r1)) s2)+            combine (r1@(Suspend s1), r2@(Suspend s2)) = Suspend (merge (map (postApply (flip parallelize (rewrap r2))) s1)+                                                                        (map (postApply (parallelize (rewrap r1))) s2))+            rewrap :: PipeRendezvous context m r -> Pipe context m r+            rewrap r = Pipe $ const $ return $ r+            adjustSuspension :: Integer -> (Pipe context m r1 -> Pipe context m r2)+                             -> Suspension context m r1 -> Suspension context m r2+            adjustSuspension c f s = postApply f s{state= (state s) {clock= clock (state s) `max` c}}+ instance Show (Suspension context m r) where-   show Suspension{pid= pid, description = desc, continuation= c} = (case c of Put{} -> "(Put)"-                                                                               CanPut{} -> "(CanPut)"-                                                                               Get{} -> "(Get)")-                                                                    ++ desc ++ " -> " ++ show pid+   show Suspension{targetLevel= lvl, description = desc, continuation= c} = (case c of Put{} -> "(Put)"+                                                                                       CanPut{} -> "(CanPut)"+                                                                                       Get{} -> "(Get)")+                                                                            ++ desc ++ " -> " ++ show lvl  -- | The 'pipe' function splits the computation into two concurrent parts, /producer/ and /consumer/. The /producer/ is -- given a 'Sink' to put values into, and /consumer/ a 'Source' to get those values from.  Once producer and consumer -- both complete, 'pipe' returns their paired results.-pipe :: forall context x m r1 r2. Monad m => Producer m x r1 -> Consumer m x r2 -> Pipe context m (r1, r2)+pipe :: forall context x m r1 r2. Monad m => Producer context m x r1 -> Consumer context m x r2 -> Pipe context m (r1, r2) pipe = pipeD ""  -- | The 'pipeD' function is same as 'pipe', with an additional description argument.-pipeD :: forall context x m r1 r2. Monad m => String -> Producer m x r1 -> Consumer m x r2 -> Pipe context m (r1, r2)-pipeD description producer consumer-   = Pipe (\context pid clock-> let producerPid = 2*pid-                                    consumerPid = 2*pid+1-                                    context' = undefined-                                    description' = description ++ ':' : show pid-                                in assert (track (indent pid ++ "pipe " ++ description')) $-                                   do ps <- proceed (producer (Sink context' producerPid description')) context' producerPid clock-                                      cs <- proceed (consumer (Source context' consumerPid description')) context' consumerPid clock-                                      reduce context' producerPid ps consumerPid cs)+pipeD :: forall c x m r1 r2. Monad m => String -> Producer c m x r1 -> Consumer c m x r2 -> Pipe c m (r1, r2)+pipeD description producer consumer = pipePrim description (liftM2 (,)) producer consumer -reduce :: forall c m r1 r2. Monad m => c -> Int -> PipeState c m r1 -> Int -> PipeState c m r2 -> m (PipeState c m (r1, r2))-reduce context pid1 (Done t1 r1) pid2 (Done t2 r2)-   = assert (track (indent pid1 ++ "Done " ++ show pid1 ++ " -> " ++ show pid2)) $+-- | The 'pipeP' function is equivalent to 'pipe', except the /producer/ and /consumer/ are run in parallel if resources+-- allow.+pipeP :: forall c x m r1 r2. ParallelizableMonad m => Producer c m x r1 -> Consumer c m x r2 -> Pipe c m (r1, r2)+pipeP producer consumer = pipePrim "" parallelize producer consumer++-- | The 'pipePrim' function is the actual worker function of the 'pipe' family.+pipePrim :: forall c m x r1 r2. Monad m =>+            String -> (forall a b. m a -> m b -> m (a, b)) -> Producer c m x r1 -> Consumer c m x r2 -> Pipe c m (r1, r2)+pipePrim description pairMonads producer consumer+   = Pipe (\(PipeState level clock)-> let level' = succ level+                                          description' = description ++ ':' : show level+                                      in assert (track (indent level ++ "pipe " ++ description')) $+                                         do (ps, cs) <- pairMonads (proceed (producer (Sink level description'))+                                                                            (PipeState level' clock))+                                                                   (proceed (consumer (Source level description'))+                                                                            (PipeState level' clock))+                                            reduce pairMonads level ps cs)++reduce :: forall c m r1 r2. Monad m =>+          (m (PipeRendezvous c m r1) -> m (PipeRendezvous c m r2) -> m (PipeRendezvous c m r1, PipeRendezvous c m r2))+             -> Int -> PipeRendezvous c m r1 -> PipeRendezvous c m r2 -> m (PipeRendezvous c m (r1, r2))+reduce pairMonads level (Done t1 r1) (Done t2 r2)+   = assert (track (indent level ++ "Done " ++ show level ++ " -> " ++ show level)) $      return (Done (max t1 t2) (r1, r2))-reduce context pid1 (Suspend c1 ps@(Suspension{pid= pid1', clock= t, continuation= pCont} : _)) pid2 consumer@Done{}-   | pid1' == pid1, Put _ cont <- pCont-   = assert (track (indent pid1 ++ "Failed producer put " ++ show ps ++ " from " ++ show pid1)) $-     proceed (cont False) context pid1 t >>= \p'-> reduce context pid1 p' pid2 consumer-   | pid1' == pid1, CanPut cont <- pCont-   = assert (track (indent pid1 ++ "Finish producer " ++ show ps ++ " from " ++ show pid1)) $-     proceed (cont False) context pid1 t >>= \p'-> reduce context pid1 p' pid2 consumer-   | pid1' < pid1 = assert (track (indent pid1 ++ "Suspend producer " ++ show ps ++ " from " ++ show pid1)) $-                    return $ Suspend context $ map (delay (\ps'-> reduce context pid1 ps' pid2 consumer)) ps-   | otherwise = error (show pid1' ++ ">" ++ show pid1 ++ " | producer : " ++ show ps)-reduce context pid1 producer@Done{} pid2 (Suspend c2 cs@(Suspension{pid= pid2', clock= t, continuation= cCont} : _))-   | pid2' == pid2, Get cont <- cCont-   = assert (track (indent pid1 ++ "Finish consumer " ++ show cs ++ " from " ++ show pid2)) $-     proceed (cont Nothing) context pid2 t >>= reduce context pid1 producer pid2-   | pid2' < pid2 = assert (track (indent pid1 ++ "Suspend consumer " ++ show cs ++ " from " ++ show pid2)) $-                    return $ Suspend context $ map (delay (reduce context pid1 producer pid2)) cs-   | otherwise = error (show pid2' ++ ">" ++ show pid2 ++ " | consumer : " ++ show cs)-reduce context pid1 producer@(Suspend _ ps@(Suspension{pid= pid1', clock=t1, continuation= pc} : _))-               pid2 consumer@(Suspend _ cs@(Suspension{pid= pid2', clock=t2, continuation= Get cCont} : _))-   | pid1' == pid1 && pid2' == pid2, CanPut pCont <- pc-   = assert (track (indent pid1 ++ "CanPut Match at " ++ show pid1 ++ "/" ++ show pid2 ++ " : " ++ show ps ++ " -> " ++ show cs)) $-     proceed (pCont True) context pid1 t1 >>= \p'-> reduce context pid1 p' pid2 consumer-   | pid1' == pid1 && pid2' == pid2, Put x pCont <- pc-   = assert (track (indent pid1 ++ "Match at " ++ show pid1 ++ "/" ++ show pid2 ++ " : " ++ show ps ++ " -> " ++ show cs)) $-     let t' = max t1 t2-     in do p' <- assert (track "producer (") $ proceed (pCont True) context pid1 t'-           c' <- assert (track ") consumer (") $ proceed (cCont (cast x)) context pid2 t'-           assert (track ") combined ->") reduce context pid1 p' pid2 c'-reduce context pid1 producer@(Suspend c1 ps) pid2 consumer@(Suspend c2 cs) = assert (track (indent pid1 ++ "Suspend producer & consumer, "-                                                                                            ++ show ps ++ " from " ++ show pid1 ++ " & "-                                                                                            ++ show cs ++ " from " ++ show pid2)) $-                                                                             keepSuspending ps cs-     where keepSuspending (Suspension{pid=pid1'} : pTail) cs | pid1' == pid1 = keepSuspending pTail cs-           keepSuspending ps (Suspension{pid= pid2'} : cTail) | pid2' == pid2 = keepSuspending ps cTail-           keepSuspending ps cs = assert (track (indent pid1 ++ "Suspend' producer & consumer, "-                                                 ++ show ps ++ " from " ++ show pid1 ++ " & "-                                                 ++ show cs ++ " from " ++ show pid2)) $-                                  return $ Suspend context $-                                         merge (map (\p-> delay (\p'-> reduce context pid1 p' pid2 consumer) p) ps)-                                               (map (delay (reduce context pid1 producer pid2)) cs)+reduce pairMonads level (Suspend ps@(Suspension{targetLevel= l1, state= s1, continuation= pCont} : _)) consumer@Done{}+   | l1 == level, Put _ cont <- pCont+   = assert (track (indent level ++ "Failed producer put " ++ show ps ++ " from " ++ show level)) $+     proceed (cont False) s1 >>= \p'-> reduce pairMonads level p' consumer+   | l1 == level, CanPut cont <- pCont+   = assert (track (indent level ++ "Finish producer " ++ show ps ++ " from " ++ show level)) $+     proceed (cont False) s1 >>= \p'-> reduce pairMonads level p' consumer+   | l1 < level = assert (track (indent level ++ "Suspend producer " ++ show ps ++ " from " ++ show level)) $+                  return $ Suspend $ map (delay (\ps'-> reduce pairMonads level ps' consumer)) ps+   | otherwise = error (show l1 ++ ">" ++ show level ++ " | producer : " ++ show ps)+reduce pairMonads level producer@Done{} (Suspend cs@(Suspension{targetLevel= l2, state= s2, continuation= cCont} : _))+   | l2 == level, Get cont <- cCont+   = assert (track (indent level ++ "Finish consumer " ++ show cs ++ " from " ++ show level)) $+     proceed (cont Nothing) s2 >>= reduce pairMonads level producer+   | l2 < level+   = assert (track (indent level ++ "Suspend consumer " ++ show cs ++ " from " ++ show level)) $+     return $ Suspend $ map (delay (reduce pairMonads level producer)) cs+   | otherwise = error (show l2 ++ ">" ++ show level ++ " | consumer : " ++ show cs)+reduce pairMonads level producer@(Suspend ps@(Suspension{targetLevel= l1, state= s1, continuation= pc} : _))+                        consumer@(Suspend cs@(Suspension{targetLevel= l2, state= s2, continuation= Get cCont} : _))+   | l1 == level && l2 == level, CanPut pCont <- pc+   = assert (track (indent level ++ "CanPut Match at " ++ show level ++ " : " ++ show ps ++ " -> " ++ show cs)) $+     proceed (pCont True) s1 >>= \p'-> reduce pairMonads level p' consumer+   | l1 == level, Put x pCont <- pc+   = assert (track (indent level ++ "Match at " ++ show level ++ " : " ++ show ps ++ " -> " ++ show cs)) $+     do (p', c') <- pairMonads (assert (track "producer (") $ proceed (pCont True) (synchronizeState s1 s2))+                               (assert (track ") consumer (") $ proceed (cCont (cast x)) (synchronizeState s2 s1))+        assert (track ") combined ->") reduce pairMonads level p' c'+reduce pairMonads level producer@(Suspend ps) consumer@(Suspend cs) = assert (track (indent level ++ "Suspend producer & consumer, "+                                                                                     ++ show ps ++ " from " ++ show level ++ " & "+                                                                                     ++ show cs ++ " from " ++ show level)) $+                                                                                        keepSuspending ps cs+     where keepSuspending (Suspension{targetLevel=level'} : pTail) cs | level' == level = keepSuspending pTail cs+           keepSuspending ps (Suspension{targetLevel= level'} : cTail) | level' == level = keepSuspending ps cTail+           keepSuspending ps cs = assert (track (indent level ++ "Suspend' producer & consumer, "+                                                 ++ show ps ++ " from " ++ show level ++ " & "+                                                 ++ show cs ++ " from " ++ show level)) $+                                  return $ Suspend $+                                         merge (map (\p-> delay (\p'-> reduce pairMonads level p' consumer) p) ps)+                                               (map (delay (reduce pairMonads level producer)) cs) -merge :: Monad m => [Suspension context m r] -> [Suspension context m r] -> [Suspension context m r]+merge :: [Suspension context m r] -> [Suspension context m r] -> [Suspension context m r] merge [] l = l merge l [] = l-merge l1@(h1@Suspension{pid= pid1, clock= c1} : tail1) l2@(h2@Suspension{pid= pid2, clock= c2} : tail2)-   | pid1 > pid2 = h1 : merge tail1 l2-   | pid1 < pid2 = h2 : merge l1 tail2+merge l1@(h1@Suspension{targetLevel= level1, state= PipeState _ c1} : tail1)+      l2@(h2@Suspension{targetLevel= level2, state= PipeState _ c2} : tail2)+   | level1 > level2 = h1 : merge tail1 l2+   | level1 < level2 = h2 : merge l1 tail2    | c1 < c2 = h1 : merge tail1 l2    | otherwise = h2 : merge l1 tail2 -delay :: Monad m => (PipeState context m r1 -> m (PipeState context m r2)) -> Suspension context m r1 -> Suspension context m r2-delay f = delay' (\p-> Pipe $ \context pid clock-> proceed p context pid clock >>= f)+delay :: Monad m =>+         (PipeRendezvous context m r1 -> m (PipeRendezvous context m r2)) -> Suspension context m r1 -> Suspension context m r2+delay f = delay' (\p-> Pipe $ \state-> proceed p state >>= f) -delay' :: Monad m => (Pipe context m r1 -> Pipe context m r2) -> Suspension context m r1 -> Suspension context m r2+delay' :: (Pipe context m r1 -> Pipe context m r2) -> Suspension context m r1 -> Suspension context m r2 delay' f s@Suspension{description= desc, continuation= Get cont}    = s{description= "delayed " ++ desc, continuation= Get (f . cont)} delay' f s@Suspension{description= desc, continuation= Put x cont}@@ -169,72 +231,76 @@ delay' f s@Suspension{description= desc, continuation= CanPut cont}    = s{description= "delayed " ++ desc, continuation= CanPut (f . cont)} +synchronizeState :: PipeState context -> PipeState context -> PipeState context+synchronizeState (PipeState pid1 clock1) (PipeState pid2 clock2) = (PipeState pid1 (max clock1 clock2))+ indent 0 = "" indent n = ' ' : indent (n `div` 2)  -- | Function 'get' tries to get a value from the given 'Source' argument. The intervening 'Pipe' computations suspend -- all the way to the 'pipe' function invocation that created the source. The result of 'get' is 'Nothing' iff the -- argument source is empty.-get :: forall context context' x m r. (Monad m, Typeable x)-       => Source context' x -> Pipe context m (Maybe x)-get (Source _ pid desc) = assert (track (indent pid ++ "Get from " ++ desc ++ "@" ++ show pid)) $-                          Pipe (\context pid' clock->-                                assert (track (indent pid ++ "Get<- " ++ desc ++ "@" ++ show pid ++ ":" ++ show clock)) $-                                return $ Suspend context $-                                [Suspension pid clock ("get from " ++ desc ++ "@" ++ show pid ++ ":" ++ show clock) $ Get return])+get :: forall context x m r. (Monad m, Typeable x) => Source context x -> Pipe context m (Maybe x)+get (Source pid desc) = assert (track (indent pid ++ "Get from " ++ desc ++ "@" ++ show pid)) $+                        Pipe (\state@(PipeState pid' clock)->+                              assert (track (indent pid ++ "Get<- " ++ desc ++ "@" ++ show pid ++ ":" ++ show clock)) $+                              return $ Suspend $+                              [Suspension pid state ("get from " ++ desc ++ "@" ++ show pid ++ ":" ++ show clock) $ Get return]) -getSuccess :: forall context context' x m. (Monad m, Typeable x)-              => Source context' x+getSuccess :: forall context x m. (Monad m, Typeable x)+              => Source context x                  -> (x -> Pipe context m ()) -- ^ Success continuation                  -> Pipe context m () getSuccess source succeed = get source >>= maybe (return ()) succeed  -- | Function 'put' tries to put a value into the given sink. The intervening 'Pipe' computations suspend up to the -- 'pipe' invocation that has created the argument sink. The result of 'put' indicates whether the operation succeded.-put :: forall context context' x m r. (Monad m, Typeable x) => Sink context' x -> x -> Pipe context m Bool-put (Sink _ pid desc) x = assert (track (indent pid ++ "Put into " ++ desc ++ "@" ++ show pid)) $-                          Pipe (\context pid' clock->-                                assert (track (indent pid ++ "Put-> " ++ desc ++ "@" ++ show pid ++ ":" ++ show clock)) $-                                return $ Suspend context $-                                [Suspension pid clock ("put into " ++ desc ++ "@" ++ show pid ++ ":" ++ show clock)-                                 (Put x return)])+put :: forall context x m r. (Monad m, Typeable x) => Sink context x -> x -> Pipe context m Bool+put (Sink pid desc) x = assert (track (indent pid ++ "Put into " ++ desc ++ "@" ++ show pid)) $+                        Pipe (\state@(PipeState pid' clock)->+                              assert (track (indent pid ++ "Put-> " ++ desc ++ "@" ++ show pid ++ ":" ++ show clock)) $+                              return $ Suspend $+                              [Suspension pid state ("put into " ++ desc ++ "@" ++ show pid ++ ":" ++ show clock)+                               (Put x return)])  -- | Function 'canPut' checks if the argument sink accepts values, i.e., whether a 'put' operation would succeed on the -- sink.-canPut :: forall context context' x m r. (Monad m, Typeable x) => Sink context' x -> Pipe context m Bool-canPut (Sink _ pid desc) = assert (track (indent pid ++ "CanPut into " ++ desc ++ "@" ++ show pid)) $-                           Pipe (\context pid' clock->-                                 assert (track (indent pid ++ "CanPut-> " ++ desc ++ "@" ++ show pid ++ ":" ++ show clock)) $-                                 return $ Suspend context $-                                 [Suspension pid clock ("canPut into " ++ desc ++ "@" ++ show pid ++ ":" ++ show clock)-                                  (CanPut return)])+canPut :: forall context x m r. (Monad m, Typeable x) => Sink context x -> Pipe context m Bool+canPut (Sink pid desc) = assert (track (indent pid ++ "CanPut into " ++ desc ++ "@" ++ show pid)) $+                         Pipe (\state@(PipeState pid' clock)->+                               assert (track (indent pid ++ "CanPut-> " ++ desc ++ "@" ++ show pid ++ ":" ++ show clock)) $+                               return $ Suspend $+                               [Suspension pid state ("canPut into " ++ desc ++ "@" ++ show pid ++ ":" ++ show clock)+                                (CanPut return)])  -- | 'pour' copies all data from the /source/ argument into the /sink/ argument, as long as there is anything to copy -- and the sink accepts it.-pour :: forall c c1 c2 x m. (Monad m, Typeable x) => Source c1 x -> Sink c2 x -> Pipe c m ()+pour :: forall c x m. (Monad m, Typeable x) => Source c x -> Sink c x -> Pipe c m () pour source sink = fill'    where fill' = canPut sink >>= flip when (getSuccess source (\x-> put sink x >> fill'))  -- | 'tee' is similar to 'pour' except it distributes every input value from the /source/ arguments into both /sink1/ -- and /sink2/.-tee :: (Monad m, Typeable x) => Source c1 x -> Sink c2 x -> Sink c3 x -> Pipe c m ()+tee :: (Monad m, Typeable x) => Source c x -> Sink c x -> Sink c x -> Pipe c m [x] tee source sink1 sink2 = distribute    where distribute = do c1 <- canPut sink1                          c2 <- canPut sink2-                         when (c1 && c2) (getSuccess source $ \x-> put sink1 x >> put sink2 x >> distribute)+                         if c1 && c2+                            then get source >>= maybe (return []) (\x-> put sink1 x >> put sink2 x >> distribute)+                            else getList source  -- | 'putList' puts entire list into its /sink/ argument, as long as the sink accepts it. The remainder that wasn't -- accepted by the sink is the result value.-putList :: forall x c c1 m. (Monad m, Typeable x) => [x] -> Sink c1 x -> Pipe c m [x]+putList :: forall x c m. (Monad m, Typeable x) => [x] -> Sink c x -> Pipe c m [x] putList [] sink = return [] putList l@(x:rest) sink = put sink x >>= cond (putList rest sink) (return l)  -- | 'getList' returns the list of all values generated by the source.-getList :: forall x c c1 m. (Monad m, Typeable x) => Source c1 x -> Pipe c m [x]+getList :: forall x c m. (Monad m, Typeable x) => Source c x -> Pipe c m [x] getList source = get source >>= maybe (return []) (\x-> liftM (x:) (getList source))  -- | 'consumeAndSuppress' consumes the entire source ignoring the values it generates.-consumeAndSuppress :: forall x c c1 m. (Monad m, Typeable x) => Source c1 x -> Pipe c m ()+consumeAndSuppress :: forall x c m. (Monad m, Typeable x) => Source c x -> Pipe c m () consumeAndSuppress source = getSuccess source (\x-> consumeAndSuppress source)  -- | A utility function wrapping if-then-else, useful for handling monadic truth values
Makefile view
@@ -1,23 +1,27 @@-SourceFiles=$(addprefix Control/Concurrent/SCC/, Foundation.hs ComponentTypes.hs Components.hs Combinators.hs) Test.hs Shell.hs-DocumentationFiles=$(shell echo $(SourceFiles) | ./shsh -c 'stdin | select (>! (substring  " Shell.hs" >| substring " Test.hs"))')+LibraryFiles=$(addprefix Control/Concurrent/SCC/, Foundation.hs ComponentTypes.hs Components.hs Combinators.hs)+DocumentationFiles=$(LibraryFiles)+CommonOptions=-hidir obj -odir obj  all: test test-prof shsh shsh-prof docs docs: doc/index.html -test: $(SourceFiles)-	ghc --make Test.hs -main-is Test -O -o test -hidir obj -odir obj+test: $(LibraryFiles) Test.hs | obj+	ghc --make Test.hs -O2 -threaded -o test $(CommonOptions) -test-prof: $(SourceFiles)-	ghc --make Test.hs -main-is Test -o test-prof -prof -auto-all -hidir prof -odir prof+test-prof: $(LibraryFiles) Test.hs | obj+	ghc --make Test.hs -o test-prof -prof -auto-all $(CommonOptions) -shsh: $(SourceFiles)-	ghc --make Shell.hs -O -o shsh -hidir obj -odir obj+shsh: $(LibraryFiles) Shell.hs | obj+	ghc --make Shell.hs -O2 -threaded -o shsh $(CommonOptions) -shsh-prof: $(SourceFiles)-	ghc --make Shell.hs -main-is Shell -o shsh-prof -prof -auto-all -hidir prof -odir prof+shsh-prof: $(LibraryFiles) Shell.hs | obj+	ghc --make Shell.hs -o shsh-prof -prof -auto-all $(CommonOptions)  doc/index.html: $(DocumentationFiles) 	haddock -h -o doc $^ +obj:+	mkdir -p $@+ clean:-	rm obj/* prof/* doc/*+	rm -r obj/* prof/* doc/*
Shell.hs view
@@ -14,718 +14,771 @@     <http://www.gnu.org/licenses/>. -} -{-# LANGUAGE ScopedTypeVariables, Rank2Types, GADTs, PatternSignatures #-}--module Main where--import Prelude hiding ((&&), (||), appendFile, interact, last)-import Data.List (intersperse)-import Data.Maybe (fromJust)-import Data.Typeable (Typeable)-import Control.Monad (liftM, when)-import Text.ParserCombinators.Parsec hiding (between, count)-import Text.ParserCombinators.Parsec.Language (emptyDef)-import Text.ParserCombinators.Parsec.Token-import System.Console.GetOpt-import System.Console.Readline-import System.Environment (getArgs)-import System.IO (hFlush, hPutStrLn, stderr, stdout)-import System.Process (runCommand, runInteractiveCommand)--import Control.Concurrent.SCC.Foundation-import Control.Concurrent.SCC.ComponentTypes-import Control.Concurrent.SCC.Components-import Control.Concurrent.SCC.Combinators--data VoidExpression where-   NativeVoidCommand :: String -> VoidExpression-   VoidPipe :: Typeable x => ProducerExpression x -> ConsumerExpression x -> VoidExpression-   Exit :: VoidExpression--data ProducerExpression x where-   PrimitiveProducer :: Typeable x => String -> Producer IO x () -> ProducerExpression x-   NativeProducerCommand :: String -> ProducerExpression Char-   VoidSource :: Typeable x => VoidExpression -> ProducerExpression x-   ProducerPipe :: (Typeable x, Typeable y) => ProducerExpression x -> TransducerExpression x y -> ProducerExpression y-   FileSource :: String -> ProducerExpression Char-   StdInSource :: ProducerExpression Char-   Sequence :: Typeable x => ProducerExpression x -> ProducerExpression x -> ProducerExpression x--data ConsumerExpression x where-   NativeConsumerCommand :: String -> ConsumerExpression Char-   VoidSink :: Typeable x => VoidExpression -> ConsumerExpression x-   ConsumerPipe :: (Typeable x, Typeable y) => TransducerExpression x y -> ConsumerExpression y -> ConsumerExpression x-   FileSink :: String -> ConsumerExpression Char-   FileAppendSink :: String -> ConsumerExpression Char-   SuppressingConsumer :: Typeable x => ConsumerExpression x-   ErrorSink :: Typeable x => String -> ConsumerExpression x-   Tee :: Typeable x => ConsumerExpression x -> ConsumerExpression x -> ConsumerExpression x--data TransducerExpression x y where-   PrimitiveTransducer :: (Typeable x, Typeable y) => String -> Transducer IO x y -> TransducerExpression x y-   NativeTransducerCommand :: String -> TransducerExpression Char Char-   TransducerPipe :: (Typeable x, Typeable y, Typeable z)-                     => TransducerExpression x y -> TransducerExpression y z -> TransducerExpression x z-   TransducerJoin :: (Typeable x, Typeable y) => TransducerExpression x y -> TransducerExpression x y -> TransducerExpression x y-   Select :: Typeable x => SplitterExpression x -> TransducerExpression x x-   If :: (Typeable x, Typeable y)-         => SplitterExpression x -> TransducerExpression x y -> TransducerExpression x y -> TransducerExpression x y-   While :: Typeable x => SplitterExpression x -> TransducerExpression x x -> TransducerExpression x x-   ForEach :: (Typeable x, Typeable y)-              => SplitterExpression x -> TransducerExpression x y -> TransducerExpression x y -> TransducerExpression x y--data SplitterExpression x where-   PrimitiveSplitter :: Typeable x => String -> Splitter IO x -> SplitterExpression x-   And :: Typeable x => SplitterExpression x -> SplitterExpression x -> SplitterExpression x-   Or :: Typeable x => SplitterExpression x -> SplitterExpression x -> SplitterExpression x-   ZipWithAnd :: Typeable x => SplitterExpression x -> SplitterExpression x -> SplitterExpression x-   ZipWithOr :: Typeable x => SplitterExpression x -> SplitterExpression x -> SplitterExpression x-   Not :: Typeable x => SplitterExpression x -> SplitterExpression x-   FollowedBy :: Typeable x => SplitterExpression x -> SplitterExpression x -> SplitterExpression x-   Nested :: Typeable x => SplitterExpression x -> SplitterExpression x -> SplitterExpression x-   Having :: Typeable x => SplitterExpression x -> SplitterExpression x -> SplitterExpression x-   HavingOnly :: Typeable x => SplitterExpression x -> SplitterExpression x -> SplitterExpression x-   Between :: Typeable x => SplitterExpression x -> SplitterExpression x -> SplitterExpression x-   BetweenInclusive :: Typeable x => SplitterExpression x -> SplitterExpression x -> SplitterExpression x-   First :: Typeable x => SplitterExpression x -> SplitterExpression x-   Last :: Typeable x => SplitterExpression x -> SplitterExpression x-   Prefix :: Typeable x => SplitterExpression x -> SplitterExpression x-   Suffix :: Typeable x => SplitterExpression x -> SplitterExpression x--instance Show VoidExpression where-   show (NativeVoidCommand cmd) = "NativeVoidCommand \"" ++ cmd ++ "\""-   show (VoidPipe p c) = "(VoidPipe " ++ shows p (" " ++ shows c ")")-   show (Exit) = "Exit"--instance Show (ProducerExpression x) where-   show (PrimitiveProducer name p) = name-   show (NativeProducerCommand cmd) = cmd-   show (VoidSource v) = "(VoidSource " ++ shows v ")"-   show (ProducerPipe p t) = "(" ++ shows p (" | " ++ shows t ")")-   show (FileSource f) = "FileSource \"" ++ f ++ "\""-   show (Sequence p1 p2) = "(Sequence " ++ shows p1 (" "++ shows p2 ")")--instance Show (ConsumerExpression x) where-   show (NativeConsumerCommand cmd) = cmd-   show (VoidSink v) = "(VoidSink " ++ shows v ")"-   show (ConsumerPipe t c) = "(ConsumerPipe " ++ shows t (" " ++ shows c ")")-   show (FileSink f) = "> \"" ++ f ++ "\""-   show (FileAppendSink f) = ">> \"" ++ f ++ "\""-   show (SuppressingConsumer) = "SuppressingConsumer"-   show (ErrorSink e) = "ErrorSink \"" ++ e ++ "\""-   show (Tee c1 c2) = "(" ++ shows c1 (" tee " ++ shows c2 ")")--instance Show (TransducerExpression x y) where-   show (PrimitiveTransducer name t) = name-   show (NativeTransducerCommand cmd) = cmd-   show (TransducerPipe t1 t2) = "(" ++ shows t1 (" | " ++ shows t2 ")")-   show (TransducerJoin t1 t2) = "(" ++ shows t1 (" >< " ++ shows t2 ")")-   show (Select s) = "(select " ++ shows s ")"-   show (If s t1 t2) = "if " ++ shows s (" then " ++ shows t1 (" else " ++ shows t2 " end if"))-   show (While s t) = "while " ++ shows s (" do " ++ shows t " end while")-   show (ForEach s t1 t2) = "foreach " ++ shows s (" then " ++ shows t1 (" else " ++ shows t2 " end foreach"))--instance Show (SplitterExpression x) where-   show (PrimitiveSplitter name s) = name-   show (And s1 s2) = shows s1 (" >& " ++ show s2)-   show (Or s1 s2) = shows s1 (" >| " ++ show s2)-   show (ZipWithAnd s1 s2) = shows s1 (" >& " ++ show s2)-   show (ZipWithOr s1 s2) = shows s1 (" >| " ++ show s2)-   show (FollowedBy s1 s2) = shows s1 (", " ++ show s2)-   show (Not s) = "Not " ++ show s-   show (Nested s1 s2) = shows s1 (" nested in " ++ show s2)-   show (Having s1 s2) = shows s1 (" having " ++ show s2)-   show (HavingOnly s1 s2) = shows s1 (" having-only " ++ show s2)-   show (Between s1 s2) = shows s1 (" having " ++ show s2)-   show (BetweenInclusive s1 s2) = shows s1 (" having " ++ show s2)-   show (First s) = "first " ++ show s-   show (Last s) = "last " ++ show s-   show (Prefix s) = "prefix " ++ show s-   show (Suffix s) = "suffix " ++ show s--data TaggedExpression where-  TaggedCommand    :: VoidExpression -> TaggedExpression-  TaggedConsumer   :: Typeable x => TypeTag x -> ConsumerExpression x -> TaggedExpression-  TaggedProducer   :: Typeable x => TypeTag x -> ProducerExpression x -> TaggedExpression-  TaggedSplitter   :: Typeable x => TypeTag x -> SplitterExpression x -> TaggedExpression-  TaggedTransducer :: (Typeable x, Typeable y) => TypeTag x -> TypeTag y -> TransducerExpression x y -> TaggedExpression-  GenericInputExpression :: (forall x. Typeable x => TypeTag x -> TaggedExpression) -> TaggedExpression-  TypeErrorExpression :: TypeTag x -> TypeTag y -> String -> TaggedExpression--instance Show TaggedExpression where-  show (TaggedCommand e) = "TaggedCommand " ++ show e-  show (TaggedConsumer tag e) = "TaggedConsumer " ++ shows e (" :: " ++ show tag)-  show (TaggedProducer tag e) =  "TaggedProducer " ++ shows e (" :: " ++ show tag)-  show (TaggedSplitter tag e) = "TaggedSplitter " ++ shows e (" :: " ++ show tag)-  show (TaggedTransducer tag1 tag2 e) = "TaggedSplitter " ++ shows e (" :: " ++ show tag1 ++ " -> " ++ show tag2)-  show (TypeErrorExpression tag1 tag2 s) = "TypeError " ++ shows s (" :: " ++ show tag1 ++ " -> " ++ show tag2)-  show GenericInputExpression{} = "Cannot show a generic expression!"--data TypeTag x where-   AnyTag  :: TypeTag ()-   ShowableTag :: (Typeable x, Show x) => TypeTag x-   CharTag :: TypeTag Char-   IntTag  :: TypeTag Integer-   ListTag  :: Typeable x => TypeTag x -> TypeTag [x]-   PairTag :: TypeTag x -> TypeTag y -> TypeTag (x, y)--instance Show (TypeTag x) where-   show AnyTag = "Any"-   show CharTag = "Char"-   show IntTag = "Int"-   show (ListTag x) = '[' : shows x "]"-   show (PairTag x y) = "(" ++ shows x (", " ++ shows y ")")---- we use Weirich's higher-order type-safe cast to avoid deep traversals--- one can replace the type_cast with a more simple traversal-based--- version.--data CList c a = CList (c [a])-data CFlip c b a = CFlip (c a b)-data CL c a d = CL (c (d,a))-data CR c a d = CR (c (a,d))--typecast :: forall a b c. TypeTag a -> TypeTag b -> c a -> Maybe (c b)-typecast CharTag CharTag x = Just x-typecast IntTag IntTag x = Just x-typecast (ListTag a) (ListTag b) x = fmap (\(CList y)-> y) (typecast a b (CList x))-typecast (PairTag (ra::TypeTag a0) (rb::TypeTag b0)) (PairTag (ra'::TypeTag a0') (rb'::TypeTag b0')) x =-    let g = (typecast ra ra' :: (CL c b0)  a0 -> Maybe ((CL c b0) a0'))-        h = (typecast rb rb' :: (CR c a0') b0 -> Maybe ((CR c a0') b0'))-    in case g (CL x)-       of Just (CL x') -> case h (CR x')-                          of Just (CR y') -> Just y'-typecast _ _ _ = Nothing--typecast2 :: forall a a' b b' c. TypeTag a -> TypeTag b -> TypeTag a' -> TypeTag b' -> c a b -> Maybe (c a' b')-typecast2 a b a' b' x = typecast a a' (CFlip x) >>= \(CFlip x')-> typecast b b' x'--trycast :: forall a b c. Show (c a) => TypeTag a -> TypeTag b -> (c b -> TaggedExpression) -> c a -> TaggedExpression-trycast a b wrap x = case typecast a b x of Just y -> wrap y-                                            Nothing -> TypeErrorExpression a b (show x)--data Flag = Command | Help | Interactive | ScriptFile String | StandardInput-            deriving Eq--options = [Option "c" ["command"] (NoArg Command) "Execute a single command",-           Option "h" ["help"] (NoArg Help) "Show help",-           Option "f" ["file"] (ReqArg ScriptFile "file") "Execute commands from a script file",-           Option "i" ["interactive"] (NoArg Interactive) "Execute commands interactively",-           Option "s" ["stdin"] (NoArg StandardInput) "Execute commands from the standard input"]--usageSyntax = "Usage: shsh (-c <command> | -f <file> | -i | -s) "--main = do args <- getArgs-          case getOpt Permute options args-            of (_, _, errors) | not (null errors) -> putStr (concat errors)-               (option, _, []) | Help `elem` option -> showHelp-               (options, arguments, []) | Command `elem` options -> interpret (concat (intersperse " " arguments)) >> return ()-               ([ScriptFile name], [], []) -> readFile name >>= interpret >> return ()-               ([Interactive], [], []) -> interact-               ([StandardInput], [], []) -> getContents >>= interpret >> return ()-               _ -> showHelp--showHelp = putStrLn (usageInfo usageSyntax options)--interact = do Just command <- readline "> "-              addHistory command-              finish <- interpret command-              when (not finish) interact--interpret command = case parseExpression command-                    of Left position -> putStrLn ("Error at " ++ show position) >> return False-                       Right (TaggedCommand Exit, "", _) -> return True-                       Right (expression, "", _) -> execute expression >> return False-                       Right (expression, rest, _) -> putStrLn ("Cannot parse " ++ show rest) >> return False---execute :: TaggedExpression -> IO ()-execute (TaggedCommand command) = runPipes (evaluateVoidExpression command)-execute (TaggedProducer CharTag producer) = liftM fst (runPipes (pipe (evaluateProducerExpression producer) toStdOut))-                                            >> hFlush stdout-execute (TaggedProducer tag _) = hPutStrLn stderr ("Expecting a Char Producer, received a " ++ shows tag " producer.")-execute (TypeErrorExpression tag1 tag2 e) = hPutStrLn stderr ("Expecting " ++ show tag2 ++ ", received " ++ show tag1-                                                              ++ " in expression " ++ e)--evaluateConsumerExpression :: Typeable x => ConsumerExpression x -> Consumer IO x ()-evaluateConsumerExpression (NativeConsumerCommand command) =-   \source-> do (stdin, _, _, pid) <- liftPipe (runInteractiveCommand command)-                toHandle stdin source-evaluateConsumerExpression (ConsumerPipe filter sink) = evaluateTransducerExpression filter ->> evaluateConsumerExpression sink-evaluateConsumerExpression (FileSink path) = toFile path-evaluateConsumerExpression (FileAppendSink path) = appendFile path-evaluateConsumerExpression SuppressingConsumer = consumeAndSuppress-evaluateConsumerExpression (ErrorSink message) = undefined-evaluateConsumerExpression (Tee ce1 ce2) = \source-> pipe (\sink1-> pipe (\sink2-> tee source sink1 sink2)-                                                                    (evaluateConsumerExpression ce2)-                                                          )-                                           (evaluateConsumerExpression ce1)-                                           >> return ()--evaluateProducerExpression :: Typeable x => ProducerExpression x -> Producer IO x ()-evaluateProducerExpression (PrimitiveProducer _ producer) = producer-evaluateProducerExpression (NativeProducerCommand command) =-   \sink-> do (_, stdout, _, pid) <- liftPipe (runInteractiveCommand command)-              fromHandle stdout sink-evaluateProducerExpression (ProducerPipe source filter) = evaluateTransducerExpression filter <<- evaluateProducerExpression source-evaluateProducerExpression (FileSource path) = fromFile path-evaluateProducerExpression StdInSource = fromStdIn--evaluateVoidExpression :: VoidExpression -> Pipe c IO ()-evaluateVoidExpression (NativeVoidCommand command) = liftPipe (runCommand command >> return ())-evaluateVoidExpression (VoidPipe source sink) =-   do pipe (evaluateProducerExpression source) (evaluateConsumerExpression sink)-      return ()--evaluateTransducerExpression :: (Typeable x, Typeable y) => TransducerExpression x y -> Transducer IO x y-evaluateTransducerExpression (PrimitiveTransducer _ filter) = filter-evaluateTransducerExpression (NativeTransducerCommand command) = Transducer f-   where f source sink = do (stdin, stdout, _, pid) <- liftPipe (runInteractiveCommand command)-                            interleavedPour source (toHandle stdin) (fromHandle stdout) sink-                            return []--         interleavedPour :: forall c c1 c2 m x y. (Monad m, Typeable x, Typeable y)-                            => Source c1 x -> Consumer m x () -> Producer m y () -> Sink c2 y -> Pipe c m ()-         interleavedPour source consumer producer sink = pipe (\sink-> pipe producer (interleave sink)) consumer-                                                >> return ()-            where interleave consumerSink producerSource = interleave1-                     where interleave1 = canPut consumerSink-                                         >>= flip when (get source >>= maybe interleaveEnd (\x-> put consumerSink x >> interleave2))-                           interleave2 = canPut sink-                                         >>= flip when (getSuccess producerSource (\y-> put sink y >> interleave1))-                           interleaveEnd = canPut sink-                                           >>= flip when (getSuccess producerSource (\y-> put sink y >> interleaveEnd))--evaluateTransducerExpression (TransducerPipe f1 f2) = evaluateTransducerExpression f1 >-> evaluateTransducerExpression f2-evaluateTransducerExpression (TransducerJoin f1 f2) = evaluateTransducerExpression f1 `join` evaluateTransducerExpression f2-evaluateTransducerExpression (Select splitter) = select (evaluateSplitterExpression splitter)-evaluateTransducerExpression (If splitter f1 f2) =-   ifs (evaluateSplitterExpression splitter) (evaluateTransducerExpression f1) (evaluateTransducerExpression f2)-evaluateTransducerExpression (While splitter filter) =-   evaluateTransducerExpression filter `while` evaluateSplitterExpression splitter-evaluateTransducerExpression (ForEach splitter f1 f2) =-   foreach (evaluateSplitterExpression splitter) (evaluateTransducerExpression f1) (evaluateTransducerExpression f2)--evaluateSplitterExpression :: Typeable x => SplitterExpression x -> Splitter IO x-evaluateSplitterExpression (PrimitiveSplitter _ splitter) = splitter-evaluateSplitterExpression (FollowedBy s1 s2) = evaluateSplitterExpression s1 `followedBy` evaluateSplitterExpression s2-evaluateSplitterExpression (And s1 s2) = evaluateSplitterExpression s1 >& evaluateSplitterExpression s2-evaluateSplitterExpression (Or s1 s2) = evaluateSplitterExpression s1 >| evaluateSplitterExpression s2-evaluateSplitterExpression (ZipWithAnd s1 s2) = evaluateSplitterExpression s1 && evaluateSplitterExpression s2-evaluateSplitterExpression (ZipWithOr s1 s2) = evaluateSplitterExpression s1 || evaluateSplitterExpression s2-evaluateSplitterExpression (Not splitter) = snot (evaluateSplitterExpression splitter)-evaluateSplitterExpression (Nested s1 s2) = evaluateSplitterExpression s1 `nestedIn` evaluateSplitterExpression s2-evaluateSplitterExpression (Having s1 s2) = evaluateSplitterExpression s1 `having` evaluateSplitterExpression s2-evaluateSplitterExpression (HavingOnly s1 s2) = evaluateSplitterExpression s1 `havingOnly` evaluateSplitterExpression s2-evaluateSplitterExpression (Between s1 s2) = evaluateSplitterExpression s1 `between` evaluateSplitterExpression s2-evaluateSplitterExpression (BetweenInclusive s1 s2) = evaluateSplitterExpression s1 ... evaluateSplitterExpression s2-evaluateSplitterExpression (First splitter) = first (evaluateSplitterExpression splitter)-evaluateSplitterExpression (Last splitter) = last (evaluateSplitterExpression splitter)-evaluateSplitterExpression (Prefix splitter) = prefix (evaluateSplitterExpression splitter)-evaluateSplitterExpression (Suffix splitter) = suffix (evaluateSplitterExpression splitter)--specialize :: Typeable x => TypeTag x -> Parser TaggedExpression -> Parser TaggedExpression-specialize tag parser = do e <- parser-                           return (case e-                                   of GenericInputExpression g -> g tag-                                      _ -> e)--combineTransducers :: (forall x y. (Typeable x, Typeable y)-                       => TypeTag x -> TypeTag y -> TransducerExpression x y -> TransducerExpression x y-                                    -> TransducerExpression x y)-                   -> TaggedExpression -> TaggedExpression -> TaggedExpression-combineTransducers combinator e1 e2 = -   case (e1, e2)-   of (TaggedTransducer tag1 tag2 t1, TaggedTransducer tag1' tag2' t2) ->-         case typecast2 tag1' tag2' tag1 tag2 t2-         of Just t2' -> TaggedTransducer tag1 tag2 (combinator tag1 tag2 t1 t2')-            Nothing -> TypeErrorExpression tag2' tag2 (show t1)-      (GenericInputExpression ge, TaggedTransducer tag1 _ _) -> combineTransducers combinator (ge tag1) e2-      (TaggedTransducer tag1 _ _, GenericInputExpression ge) -> combineTransducers combinator e1 (ge tag1)-      (GenericInputExpression g1, GenericInputExpression g2)-         -> GenericInputExpression (\tag-> combineTransducers combinator (g1 tag) (g2 tag))-      (TypeErrorExpression{}, _) -> e1-      (_, TypeErrorExpression{}) -> e2--foldPipeline :: [TaggedExpression] -> TaggedExpression-foldPipeline list = foldl1 combine list-   where combine :: TaggedExpression -> TaggedExpression -> TaggedExpression-         combine (TaggedProducer tag p) (TaggedTransducer tag1 tag2 t)-            = trycast tag tag1 (\p'-> TaggedProducer tag2 (ProducerPipe p' t)) p-         combine (TaggedTransducer tag1 tag2 t1) (TaggedTransducer tag1' tag2' t2)-            = trycast tag2 tag1' (\t1'-> TaggedTransducer tag1 tag2' (TransducerPipe t1' t2)) t1-         combine p@(TaggedProducer tag _) (GenericInputExpression ge) = combine p (ge tag)-         combine (GenericInputExpression ge) other = GenericInputExpression (\tag-> combine (ge tag) other)-         combine t@(TaggedTransducer tag1 tag2 _) (GenericInputExpression ge) = combine t (ge tag2)-         combine e@TypeErrorExpression{} _ = e-         combine _ e@TypeErrorExpression{} = e--parseExpression :: String -> Either Int (TaggedExpression, [Char], Int)-parseExpression s = case parse partialExpressionParser "" s of-   Left error -> Left (sourceLine (errorPos error))-   Right result -> Right result--lexer = (makeTokenParser emptyDef{commentLine= "#"}){stringLiteral= stringLexemeParser}--partialExpressionParser :: Parser (TaggedExpression, [Char], Int)-partialExpressionParser = do whiteSpace lexer-                             t <- expressionParser-                             rest <- getInput-                             pos <- getPosition-                             return (t, rest, sourceLine pos - 1)--expressionParser :: Parser TaggedExpression-expressionParser = do tp@(TaggedProducer tag producer) <- producerPrimaryParser-                      whiteSpace lexer-                      transducers <- many (try (char '|' >> whiteSpace lexer >> transducerPrimaryParser))-                      let tpt = foldPipeline (tp:transducers)-                      whiteSpace lexer-                      option tpt (liftM ((,) tpt) (try (char '|' >> whiteSpace lexer >> specialize tag consumerPrimaryParser))-                                  >>= \(TaggedTransducer tag1 tag2 transducer, TaggedConsumer tag' consumer)-                                     -> return (trycast tag tag' (\p'-> TaggedCommand (VoidPipe p' consumer)) producer))--producerExpressionParser :: Parser TaggedExpression-producerExpressionParser = do tp@(TaggedProducer tag producer) <- producerPrimaryParser-                              whiteSpace lexer-                              (try (do char '|'-                                       whiteSpace lexer-                                       TaggedTransducer tag1 tag2 transducer <- transducerExpressionParser-                                       return (trycast tag tag1 (\p'-> TaggedProducer tag2 (ProducerPipe p' transducer)) producer))-                               <|>-                               return tp)--consumerExpressionParser :: Parser TaggedExpression-consumerExpressionParser = try consumerForkParser-                           <|>-                           do TaggedTransducer tag1 tag2 transducer <- transducerExpressionParser-                              whiteSpace lexer-                              char '|'-                              TaggedConsumer tag' consumer <- consumerForkParser-                              return (trycast tag' tag2 (TaggedConsumer tag1 . ConsumerPipe transducer) consumer)--consumerForkParser :: Parser TaggedExpression-consumerForkParser = do tc@(TaggedConsumer tag first) <- consumerPrimaryParser-                        whiteSpace lexer-                        (try (do symbol lexer "tee"-                                 TaggedConsumer tag' rest <- consumerForkParser-                                 return (trycast tag tag' (\first'-> TaggedConsumer tag' (Tee first' rest)) first))-                         <|>-                         return tc)--voidPrimaryParser = try (symbol lexer "exit" >> return Exit)-                    <|> liftM NativeVoidCommand nativeCommand--producerPrimaryParser :: Parser TaggedExpression-producerPrimaryParser = try (do char '('-                                whiteSpace lexer-                                (try (do command <- nativeCommand-                                         whiteSpace lexer-                                         char ')'-                                         whiteSpace lexer-                                         char '>'-                                         return (TaggedProducer CharTag (NativeProducerCommand command)))-                                 <|>-                                 do source <- producerExpressionParser-                                    whiteSpace lexer-                                    char ')'-                                    return source))-                        <|> try (nativeSourceParser "cat")---                        <|> try (nativeSourceParser "echo")-                        <|> try (do symbol lexer "echo"-                                    string <- parameterParser True-                                    return (TaggedProducer CharTag $-                                            PrimitiveProducer ("echo " ++ string) (\sink-> putList string sink >> return ())))-                        <|> try (symbol lexer "stdin" >> return (TaggedProducer CharTag StdInSource))-                        <|> nativeSourceParser "ls"--nativeSourceParser :: String -> Parser TaggedExpression-nativeSourceParser command = do symbol lexer command-                                params <- nativeCommand-                                return (TaggedProducer CharTag (NativeProducerCommand (command ++ " " ++ params)))--consumerPrimaryParser :: Parser TaggedExpression-consumerPrimaryParser = try (do symbol lexer ">>"-                                file <- parameterParser True-                                return (TaggedConsumer CharTag (FileAppendSink file)))-                        <|>-                        try (do symbol lexer ">"-                                symbol lexer "("-                                command <- nativeCommand-                                whiteSpace lexer-                                symbol lexer ")"-                                return (TaggedConsumer CharTag (NativeConsumerCommand command)))-                        <|>-                        try (do symbol lexer ">"-                                file <- parameterParser True-                                return (TaggedConsumer CharTag (FileSink file)))-                        <|>-                        try (do symbol lexer "null"-                                return (GenericInputExpression ((\tag-> TaggedConsumer tag SuppressingConsumer))))-                        <|>-                        do symbol lexer "error"-                           message <- (try (parameterParser True) <|> return "Error sink reached!")-                           return (GenericInputExpression (\tag-> TaggedConsumer tag (ErrorSink message)))--transducerExpressionParser :: Parser TaggedExpression-transducerExpressionParser = do first <- transducerPrimaryParser-                                (try (do rest <- many1 (try (whiteSpace lexer >> symbol lexer "|" >> transducerPrimaryParser))-                                         return (foldPipeline (first:rest)))-                                 <|>-                                 try (do rest <- many1 (try (whiteSpace lexer >> symbol lexer "><" >> transducerPrimaryParser))-                                         return (foldr1 (combineTransducers (const $ const TransducerJoin)) (first:rest)))-                                 <|>-                                 return first)-   where tagged :: (forall x y. (Typeable x, Typeable y)-                    => TransducerExpression x y -> TransducerExpression x y -> TransducerExpression x y)-                -> TaggedExpression -> TaggedExpression -> TaggedExpression-         tagged combinator (TaggedTransducer tag1 tag2 t1) (TaggedTransducer tag1' tag2' t2)-            = case typecast2 tag1 tag2 tag1' tag2' t1 of Just t1' -> TaggedTransducer tag1' tag2' (combinator t1' t2)-                                                         Nothing -> TypeErrorExpression tag1 tag1' (show t1)--splitterExpressionParser :: Parser TaggedExpression-splitterExpressionParser = do first@(TaggedSplitter tag one)  <- splitterPrimaryParser-                              whiteSpace lexer-                              (try (do rest <- many1 (try (symbol lexer ">," >> splitterPrimaryParser))-                                       return (foldr1 (tagged FollowedBy) (first:rest)))-                               <|>-                               try (do rest <- many1 (try (symbol lexer ">|" >> splitterPrimaryParser))-                                       return (foldr1 (tagged Or) (first:rest)))-                               <|>-                               try (do rest <- many1 (try (symbol lexer ">&" >> splitterPrimaryParser))-                                       return (foldr1 (tagged And) (first:rest)))-                               <|>-                               try (do rest <- many1 (try (symbol lexer "||" >> splitterPrimaryParser))-                                       return (foldr1 (tagged ZipWithOr) (first:rest)))-                               <|>-                               try (do rest <- many1 (try (symbol lexer "&&" >> splitterPrimaryParser))-                                       return (foldr1 (tagged ZipWithAnd) (first:rest)))-                               <|>-                               try (do rest <- many1 (try (symbol lexer "..." >> splitterPrimaryParser))-                                       return (foldr1 (tagged BetweenInclusive) (first:rest)))-                               <|>-                               try (do symbol lexer "having"-                                       TaggedSplitter tag' other <- splitterPrimaryParser-                                       return (trycast tag tag' (\one'-> TaggedSplitter tag' (Having one' other)) one))-                               <|>-                               try (do symbol lexer "having-only"-                                       TaggedSplitter tag' other <- splitterPrimaryParser-                                       return (trycast tag tag' (\one'-> TaggedSplitter tag' (HavingOnly one' other)) one))-                               <|>-                               return first)-   where tagged :: (forall x. Typeable x => SplitterExpression x -> SplitterExpression x -> SplitterExpression x)-                -> TaggedExpression -> TaggedExpression -> TaggedExpression-         tagged combinator (TaggedSplitter tag1 s1) (TaggedSplitter tag2 s2)-            = trycast tag1 tag2 (\s1'-> TaggedSplitter tag2 (combinator s1' s2)) s1--transducerPrimaryParser :: Parser TaggedExpression-transducerPrimaryParser = try (do symbol lexer "("-                                  filter <- transducerExpressionParser-                                  symbol lexer ")"-                                  return filter)-                          <|>-                          try (do symbol lexer "id"-                                  return (GenericInputExpression (\tag-> TaggedTransducer tag tag (PrimitiveTransducer "id" asis))))-                          <|>-                          try (do symbol lexer "suppress"-                                  return (GenericInputExpression (\tag-> TaggedTransducer tag tag (PrimitiveTransducer "suppress" suppress))))-                          <|>-                          try (do symbol lexer "uppercase"-                                  return (TaggedTransducer CharTag CharTag (PrimitiveTransducer "uppercase" uppercase)))-                          <|>-                          try (do symbol lexer "count"-                                  return (GenericInputExpression (\tag-> TaggedTransducer tag IntTag (PrimitiveTransducer "count" count))))-                          <|>-                          try (do symbol lexer "show"-                                  return (TaggedTransducer IntTag (ListTag CharTag) (PrimitiveTransducer "show" toString)))-                          <|>-                          try (do symbol lexer "concatenate"-                                  return (GenericInputExpression $-                                          \tag-> case tag-                                                 of ListTag tag'-                                                       -> TaggedTransducer tag tag' (PrimitiveTransducer "concatenate" concatenate)-                                                    _ -> TypeErrorExpression tag (ListTag AnyTag) "concatenate"))-                          <|>-                          try (do symbol lexer "group"-                                  return (GenericInputExpression $-                                          \tag-> TaggedTransducer tag (ListTag tag) (PrimitiveTransducer "group" group)))-                          <|>-                          try (do symbol lexer "prepend"-                                  prefix <- parameterParser True-                                  return (TaggedTransducer CharTag CharTag (PrimitiveTransducer ("prepend " ++ prefix) (prepend prefix))))-                          <|>-                          try (do symbol lexer "append"-                                  suffix <- parameterParser True-                                  return (TaggedTransducer CharTag CharTag (PrimitiveTransducer ("append " ++ suffix) (append suffix))))-                          <|>-                          try (do symbol lexer "substitute"-                                  replacement <- parameterParser True-                                  return (TaggedTransducer CharTag CharTag-                                          (PrimitiveTransducer ("substitute " ++ replacement) (substitute replacement))))-                          <|>-                          try (do symbol lexer "select"-                                  TaggedSplitter tag splitter <- splitterPrimaryParser-                                  return (TaggedTransducer tag tag (Select splitter)))-                          <|>-                          try (do symbol lexer "if"-                                  TaggedSplitter tag splitter <- splitterExpressionParser-                                  whiteSpace lexer-                                  symbol lexer "then"-                                  true <- transducerExpressionParser-                                  false <- (try (symbol lexer "else" >> transducerExpressionParser)-                                            <|> return (TaggedTransducer tag tag (PrimitiveTransducer "id" asis)))-                                  symbol lexer "end"-                                  option "" (symbol lexer "if")-                                  return (combineBranches (If splitter) tag true false))-                          <|>-                          try (do symbol lexer "while"-                                  TaggedSplitter tag splitter <- splitterExpressionParser-                                  whiteSpace lexer-                                  symbol lexer "do"-                                  TaggedTransducer tag' tag'' body <- transducerExpressionParser-                                  whiteSpace lexer-                                  symbol lexer "end"-                                  option "" (symbol lexer "while")-                                  return (case (typecast tag tag' splitter, typecast tag'' tag' body)-                                          of (Just test, Just body) -> TaggedTransducer tag' tag' (While test body)))-                          <|>-                          try (do symbol lexer "foreach"-                                  TaggedSplitter tag splitter <- splitterExpressionParser-                                  whiteSpace lexer-                                  symbol lexer "then"-                                  trueBranch <- transducerExpressionParser-                                  whiteSpace lexer-                                  falseBranch <- (try (symbol lexer "else" >> transducerExpressionParser)-                                                  <|> return (TaggedTransducer tag tag (PrimitiveTransducer "id" asis)))-                                  whiteSpace lexer-                                  symbol lexer "end"-                                  option "" (symbol lexer "foreach")-                                  return (combineBranches (ForEach splitter) tag trueBranch falseBranch))-                          <|>-                          liftM (TaggedTransducer CharTag CharTag . NativeTransducerCommand) nativeCommand--combineBranches :: forall x. Typeable x-                   => (forall y. Typeable y => TransducerExpression x y -> TransducerExpression x y -> TransducerExpression x y)-                      -> TypeTag x -> TaggedExpression -> TaggedExpression -> TaggedExpression-combineBranches combinator tag b1 b2 = -   case (b1, b2)-   of (TaggedTransducer tag1 tag2 true, TaggedTransducer tag1' tag2' false) ->-         case (typecast2 tag1 tag2 tag tag2 true, typecast2 tag1' tag2' tag tag2 false)-         of (Just true', Just false') -> TaggedTransducer tag tag2 (combinator true' false')-            (Nothing, _) -> TypeErrorExpression tag1 tag (show true)-            (_, Nothing) -> TypeErrorExpression tag2' tag2 (show true)-      (GenericInputExpression ge, _) -> combineBranches combinator tag (ge tag) b2-      (_, GenericInputExpression ge) -> combineBranches combinator tag b1 (ge tag)-      (TypeErrorExpression{}, _) -> b1-      (_, TypeErrorExpression{}) -> b2--splitterPrimaryParser :: Parser TaggedExpression-splitterPrimaryParser = try (do symbol lexer "("-                                splitter <- splitterExpressionParser-                                symbol lexer ")"-                                return splitter)-                        <|>-                        try (do symbol lexer ">!"-                                TaggedSplitter tag splitter <- splitterPrimaryParser-                                return (TaggedSplitter tag (Not splitter)))-                        <|>-                        try (do symbol lexer "prefix"-                                TaggedSplitter tag splitter <- splitterPrimaryParser-                                return (TaggedSplitter tag (Prefix splitter)))-                        <|>-                        try (do symbol lexer "suffix"-                                TaggedSplitter tag splitter <- splitterPrimaryParser-                                return (TaggedSplitter tag (Suffix splitter)))-                        <|>-                        try (do symbol lexer "first"-                                TaggedSplitter tag splitter <- splitterPrimaryParser-                                return (TaggedSplitter tag (First splitter)))-                        <|>-                        try (do symbol lexer "last"-                                TaggedSplitter tag splitter <- splitterPrimaryParser-                                return (TaggedSplitter tag (Last splitter)))-                        <|>-                        try (do symbol lexer "whitespace"-                                return (TaggedSplitter CharTag (PrimitiveSplitter "whitespace" whitespace)))-                        <|>-                        try (do symbol lexer "line"-                                return (TaggedSplitter CharTag (PrimitiveSplitter "line" line)))-                        <|>-                        try (do symbol lexer "letters"-                                return (TaggedSplitter CharTag (PrimitiveSplitter "letters" letters)))-                        <|>-                        try (do symbol lexer "digits"-                                return (TaggedSplitter CharTag (PrimitiveSplitter "digits" digits)))-                        <|>-                        try (do symbol lexer "substring"-                                part <- parameterParser True-                                return (TaggedSplitter CharTag (PrimitiveSplitter ("substring " ++ part) (substring part))))-                        <|>-                        do symbol lexer "nested"-                           TaggedSplitter tag1 core <- splitterExpressionParser-                           whiteSpace lexer-                           symbol lexer "in"-                           TaggedSplitter tag2 shell <- splitterExpressionParser-                           whiteSpace lexer-                           symbol lexer "end"-                           option "" (symbol lexer "nested")-                           return (trycast tag1 tag2 (\core'-> TaggedSplitter tag2 (Nested core' shell)) core)-                        <|>-                        do symbol lexer "between"-                           TaggedSplitter tag1 left <- splitterExpressionParser-                           whiteSpace lexer-                           symbol lexer "and"-                           TaggedSplitter tag2 right <- splitterExpressionParser-                           whiteSpace lexer-                           symbol lexer "end"-                           option "" (symbol lexer "between")-                           return (trycast tag1 tag2 (\left'-> TaggedSplitter tag2 (Between left' right)) left)--nativeCommand  :: Parser String-nativeCommand = do parts <- many (try (lexeme lexer (parameterParser False)))-                   return (concat (intersperse " " parts))--parameterParser :: Bool -> Parser String-parameterParser normalize = do chars <- many (noneOf " \t\n'\"`\\()[]{}<>|&")-                               (do try (string "\\n")-                                   rest <- (parameterParser normalize <|> return "")-                                   return (chars ++ '\n' : rest)-                                <|>-                                do try (string "\\t")-                                   rest <- (parameterParser normalize <|> return "")-                                   return (chars ++ '\t' : rest)-                                <|>-                                do next <- escape-                                   rest <- (parameterParser normalize <|> return "")-                                   return (chars ++ next : rest)-                                <|>-                                do quote <- oneOf "'\"`"-                                   string <- many (noneOf (quote : "\\") <|> escape)-                                   char quote-                                   rest <- (parameterParser normalize <|> return "")-                                   return (chars ++ (if normalize then string else quote : (string ++ [quote])) ++ rest)-                                <|>-                                do try (char '(')-                                   whiteSpace lexer-                                   inside <- nativeCommand-                                   char ')'-                                   rest <- (parameterParser normalize <|> return "")-                                   return (chars ++ '(' : inside ++ ')' : rest)-                                <|>-                                do try (char '[')-                                   whiteSpace lexer-                                   inside <- nativeCommand-                                   char ']'-                                   rest <- parameterParser normalize-                                   return (chars ++ '[' : inside ++ ']' : rest)-                                <|>-                                do try (char '{')-                                   whiteSpace lexer-                                   inside <- nativeCommand-                                   char '}'-                                   rest <- (parameterParser normalize <|> return "")-                                   return (chars ++ '{' : inside ++ '}' : rest)-                                <|>-                                do when (null chars) pzero+{-# LANGUAGE ScopedTypeVariables, Rank2Types, GADTs, FlexibleContexts, PatternSignatures #-}++module Main where++import Prelude hiding ((&&), (||), appendFile, interact, last, sequence)+import qualified Prelude+import Data.List (intersperse, partition)+import Data.Maybe (fromJust)+import Data.Typeable (Typeable)+import Control.Concurrent (forkIO)+import Control.Exception (evaluate)+import Control.Monad (liftM, when)+import Text.Parsec hiding (count)+import Text.Parsec.String+import Text.Parsec.Language (emptyDef)+import Text.Parsec.Token+import System.Console.GetOpt+import System.Console.Readline+import System.Environment (getArgs)+import System.IO (BufferMode (NoBuffering), hFlush, hIsWritable, hPutStrLn, hReady, hSetBuffering, stderr, stdout)+import qualified System.Process as Process++import Control.Concurrent.MVar+import Debug.Trace (trace)+import System.IO (Handle, IOMode (ReadMode, WriteMode, AppendMode), openFile, hClose,+                  hGetChar, hGetContents, hPutChar, hFlush, hIsEOF, hClose, putChar, isEOF, stdout)++import Control.Concurrent.SCC.Foundation+import Control.Concurrent.SCC.ComponentTypes+import Control.Concurrent.SCC.Components+import Control.Concurrent.SCC.Combinators++data Expression where+   -- Compiled expressions+   Compiled         :: Component x => TypeTag x -> x -> Expression+   -- Generic expressions+   NativeCommand    :: String -> Expression+   TypeError        :: TypeTag x -> TypeTag y -> Expression -> Expression+   Join             :: Expression -> Expression -> Expression+   Sequence         :: Expression -> Expression -> Expression+   Pipe             :: Expression -> Expression -> Expression+   If               :: Expression -> Expression -> Expression -> Expression+   ForEach          :: Expression -> Expression -> Expression -> Expression+   -- Void expressions, i.e. commands+   Exit             :: Expression+   -- Producer constructs+   FromList         :: String -> Expression+   FileProducer     :: String -> Expression+   StdInProducer    :: Expression+   -- Consumer constructs+   FileConsumer     :: String -> Expression+   FileAppend       :: String -> Expression+   Suppress         :: Expression+   ErrorConsumer    :: String -> Expression+   -- Transducer constructs+   Select           :: Expression -> Expression+   While            :: Expression -> Expression -> Expression+   IdentityTransducer :: Expression+   Count            :: Expression+   Concatenate      :: Expression+   Group            :: Expression+   Uppercase        :: Expression+   ShowTransducer   :: Expression+   -- Splitter constructs+   EverythingSplitter :: Expression+   NothingSplitter  :: Expression+   WhitespaceSplitter :: Expression+   LineSplitter     :: Expression+   LetterSplitter   :: Expression+   DigitSplitter    :: Expression+   OneSplitter      :: Expression+   SubstringSplitter :: String -> Expression+   And              :: Expression -> Expression -> Expression+   Or               :: Expression -> Expression -> Expression+   ZipWithAnd       :: Expression -> Expression -> Expression+   ZipWithOr        :: Expression -> Expression -> Expression+   Not              :: Expression -> Expression+   FollowedBy       :: Expression -> Expression -> Expression+   Nested           :: Expression -> Expression -> Expression+   Having           :: Expression -> Expression -> Expression+   HavingOnly       :: Expression -> Expression -> Expression+   Between          :: Expression -> Expression -> Expression+   First            :: Expression -> Expression+   Last             :: Expression -> Expression+   Prefix           :: Expression -> Expression+   Suffix           :: Expression -> Expression+   Prepend          :: Expression -> Expression+   Append           :: Expression -> Expression+   Substitute       :: Expression -> Expression+   StartOf          :: Expression -> Expression+   EndOf            :: Expression -> Expression++instance Show Expression where+   showsPrec _ (Compiled tag c) rest = "compiled " ++ shows tag rest+   showsPrec _ (NativeCommand cmd) rest = "native \"" ++ cmd ++ "\"" ++ rest+   showsPrec p (Pipe left right) rest | p < 3 = showsPrec 3 left (" | " ++ showsPrec 2 right rest)+   showsPrec _ (If s t f) rest = "if " ++ showsPrec 0 s (" then " ++ showsPrec 0 t (" else " ++ showsPrec 0 f (" end if" ++ rest)))+   showsPrec _ (ForEach s t f) rest = "foreach " ++ showsPrec 0 s (" then " ++ showsPrec 0 t+                                                                   (" else " ++ showsPrec 0 f (" end foreach" ++ rest)))+   showsPrec _ Exit rest = "Exit" ++ rest+   showsPrec _ (FileProducer f) rest = "FileProducer \"" ++ f ++ "\"" ++ rest+   showsPrec _ (FromList str) rest = "echo \"" ++ str ++ "\"" ++ rest+   showsPrec 0 (Sequence p1 p2) rest = showsPrec 2 p1 (";\n" ++ showsPrec 0 p2 rest)+   showsPrec 1 (Sequence p1 p2) rest = showsPrec 2 p1 ("; " ++ showsPrec 1 p2 rest)+   showsPrec p e@Sequence{} rest = "(" ++ showsPrec 1 e (')' : rest)+   showsPrec 0 (Join p1 p2) rest = showsPrec 2 p1 (" &\n" ++ showsPrec 0 p2 rest)+   showsPrec 1 (Join p1 p2) rest = showsPrec 2 p1 (" & " ++ showsPrec 1 p2 rest)+   showsPrec p e@Join{} rest = "(" ++ showsPrec 1 e (')' : rest)+   showsPrec _ (FileConsumer f) rest = "> \"" ++ f ++ "\"" ++ rest+   showsPrec _ (FileAppend f) rest = ">> \"" ++ f ++ "\"" ++ rest+   showsPrec _ (Suppress) rest = "suppress" ++ rest+   showsPrec _ (ErrorConsumer e) rest = "error \"" ++ e ++ "\"" ++ rest+   showsPrec p (Select s) rest | p < 4 = "select " ++ showsPrec 4 s rest+   showsPrec _ (While s t) rest = "while " ++ showsPrec 0 s (" do " ++ showsPrec 0 t (" end while" ++ rest))+   showsPrec p (And s1 s2) rest | p < 4 = showsPrec 4 s1 (" >& " ++ showsPrec 4 s2 rest)+   showsPrec p (Or s1 s2) rest | p < 4 = showsPrec 4 s1 (" >| " ++ showsPrec 4 s2 rest)+   showsPrec p (ZipWithAnd s1 s2) rest | p < 4 = showsPrec 4 s1 (" && " ++ showsPrec 4 s2 rest)+   showsPrec p (ZipWithOr s1 s2) rest | p < 4 = showsPrec 4 s1 (" || " ++ showsPrec 4 s2 rest)+   showsPrec p (FollowedBy s1 s2) rest | p < 4 = showsPrec 4 s1 (", " ++ showsPrec 4 s2 rest)+   showsPrec p (Not s) rest | p < 4 = ">! " ++ showsPrec 4 s rest+   showsPrec p (Nested s1 s2) rest | p < 4 = showsPrec 4 s1 (" nested in " ++ showsPrec 4 s2 rest)+   showsPrec p (Having s1 s2) rest | p < 4 = showsPrec 4 s1 (" having " ++ showsPrec 4 s2 rest)+   showsPrec p (HavingOnly s1 s2) rest | p < 4 = showsPrec 4 s1 (" having-only " ++ showsPrec 4 s2 rest)+   showsPrec p (Between s1 s2) rest | p < 4 = showsPrec 4 s1 (" ... " ++ showsPrec 4 s2 rest)+   showsPrec p (First s) rest | p < 4 = "first " ++ showsPrec 4 s rest+   showsPrec p (Last s) rest | p < 4 = "last " ++ showsPrec 4 s rest+   showsPrec p (Prefix s) rest | p < 4 = "prefix " ++ showsPrec 4 s rest+   showsPrec p (Suffix s) rest | p < 4 = "suffix " ++ showsPrec 4 s rest+   showsPrec p (Prepend s) rest | p < 4 = "prepend " ++ showsPrec 4 s rest+   showsPrec p (Append s) rest | p < 4 = "append " ++ showsPrec 4 s rest+   showsPrec p (Substitute s) rest | p < 4 = "substitute " ++ showsPrec 4 s rest+   showsPrec p (StartOf s) rest | p < 4 = "start-of " ++ showsPrec 4 s rest+   showsPrec p (EndOf s) rest | p < 4 = "end-of " ++ showsPrec 4 s rest+   showsPrec _ IdentityTransducer rest = "id" ++ rest+   showsPrec _ Count rest = "count" ++ rest+   showsPrec _ Concatenate rest = "concatenate" ++ rest+   showsPrec _ Group rest = "group" ++ rest+   showsPrec _ Uppercase rest = "uppercase" ++ rest+   showsPrec _ ShowTransducer rest = "show" ++ rest+   showsPrec _ EverythingSplitter rest = "everything" ++ rest+   showsPrec _ NothingSplitter rest = "nothing" ++ rest+   showsPrec _ WhitespaceSplitter rest = "whitespace" ++ rest+   showsPrec _ LineSplitter rest = "line" ++ rest+   showsPrec _ LetterSplitter rest = "letters" ++ rest+   showsPrec _ DigitSplitter rest = "digits" ++ rest+   showsPrec _ OneSplitter rest = "one" ++ rest+   showsPrec _ (SubstringSplitter s) rest = "substring " ++ shows s rest+   showsPrec _ (TypeError tag1 tag2 e) rest = ("Type error: expecting " ++ show tag2 ++ ", received " ++ show tag1+                                               ++ "\nin expression " ++ showsPrec 9 e rest)+   showsPrec p e rest | p > 0 = "(" ++ showsPrec 0 e (')' : rest)++data TypeTag x where+   -- Data type tags+   AnyTag  :: TypeTag ()+   UnitTag  :: TypeTag ()+   ShowableTag :: (Typeable x, Show x) => TypeTag x+   CharTag :: TypeTag Char+   IntTag  :: TypeTag Integer+   ListTag  :: Typeable x => TypeTag x -> TypeTag [x]+   PairTag :: TypeTag x -> TypeTag y -> TypeTag (x, y)+   -- Streaming component type tags+   CommandTag    :: TypeTag (Performer IO ())+   ConsumerTag   :: Typeable x => TypeTag x -> TypeTag (Consumer IO x ())+   ProducerTag   :: Typeable x => TypeTag x -> TypeTag (Producer IO x ())+   SplitterTag   :: Typeable x => TypeTag x -> TypeTag (Splitter IO x)+   TransducerTag :: (Typeable x, Typeable y) => TypeTag x -> TypeTag y -> TypeTag (Transducer IO x y)+   GenericInputTag :: forall x y. (Typeable x, Typeable y) => (TypeTag x -> TypeTag y) -> TypeTag y++instance Show (TypeTag x) where+   show AnyTag = "Any"+   show UnitTag = "()"+   show CharTag = "Char"+   show IntTag = "Int"+   show (ListTag x) = '[' : shows x "]"+   show (PairTag x y) = "(" ++ shows x (", " ++ shows y ")")+   show CommandTag  = "Command"+   show (ConsumerTag x) = "Consumer " ++ show x+   show (ProducerTag x) = "Producer " ++ show x+   show (SplitterTag x) = "Splitter " ++ show x+   show (TransducerTag x y) = "Transducer " ++ shows x (" -> " ++ show y)+   show GenericInputTag{} = "Generic"++-- Weirich's higher-order type-safe cast++data CConsumer c x = CConsumer (c (Consumer IO x ()))+data CProducer c x = CProducer (c (Producer IO x ()))+data CSplitter c x = CSplitter (c (Splitter IO x))++data CList c a = CList (c [a])+data CFlip c b a = CFlip (c a b)+data CL c a d = CL (c (d,a))+data CR c a d = CR (c (a,d))+data CTL c a d = CTL (c (Transducer IO d a))+data CTR c a d = CTR (c (Transducer IO a d))++typecast :: forall a b c. TypeTag a -> TypeTag b -> c a -> Maybe (c b)+typecast UnitTag UnitTag x = Just x+typecast CharTag CharTag x = Just x+typecast IntTag IntTag x = Just x+typecast (ListTag a) (ListTag b) x = fmap (\(CList y)-> y) (typecast a b (CList x))+typecast (PairTag (ra::TypeTag a0) (rb::TypeTag b0)) (PairTag (ra'::TypeTag a0') (rb'::TypeTag b0')) x =+    let g = (typecast ra ra' :: (CL c b0)  a0 -> Maybe ((CL c b0) a0'))+        h = (typecast rb rb' :: (CR c a0') b0 -> Maybe ((CR c a0') b0'))+    in case g (CL x)+       of Just (CL x') -> case h (CR x')+                          of Just (CR y') -> Just y'+typecast CommandTag CommandTag x = Just x+typecast (ConsumerTag a) (ConsumerTag b) x = fmap (\(CConsumer y)-> y) (typecast a b (CConsumer x))+typecast (ProducerTag a) (ProducerTag b) x = fmap (\(CProducer y)-> y) (typecast a b (CProducer x))+typecast (SplitterTag a) (SplitterTag b) x = fmap (\(CSplitter y)-> y) (typecast a b (CSplitter x))+typecast (TransducerTag (ra::TypeTag a0) (rb::TypeTag b0)) (TransducerTag (ra'::TypeTag a0') (rb'::TypeTag b0')) x+   = let g = (typecast ra ra' :: (CTL c b0)  a0 -> Maybe ((CTL c b0) a0'))+         h = (typecast rb rb' :: (CTR c a0') b0 -> Maybe ((CTR c a0') b0'))+     in case g (CTL x)+        of Just (CTL x') -> case h (CTR x')+                            of Just (CTR y') -> Just y'+typecast _ _ _ = Nothing++trycast :: forall a b. TypeTag a -> TypeTag b -> a -> Expression -> (b -> Expression) -> Expression+trycast tag1 tag2 x e constructor = case typecast tag1 tag2 (Just x)+                                    of Just (Just y) -> constructor y+                                       Nothing -> TypeError tag1 tag2 e++data Flag = Command | Help | Interactive | PrettyPrint | ScriptFile String | StandardInput | Threads String+            deriving Eq++data InputSource = UnspecifiedSource | CommandLineSource | InteractiveSource | ScriptFileSource String | StandardInputSource++data Flags = Flags {helpFlag :: Bool,+                    inputSourceFlag :: InputSource,+                    prettyPrintFlag :: Bool,+                    threadCount :: Maybe Int}++flagList = [Option "c" ["command"] (NoArg Command) "Execute a single command",+            Option "p" ["prettyprint"] (NoArg PrettyPrint) "Pretty print the input expression instead of executing it",+            Option "h" ["help"] (NoArg Help) "Show help",+            Option "f" ["file"] (ReqArg ScriptFile "file") "Execute commands from a script file",+            Option "i" ["interactive"] (NoArg Interactive) "Execute commands interactively",+            Option "s" ["stdin"] (NoArg StandardInput) "Execute commands from the standard input",+            Option "t" ["threads"] (ReqArg Threads "threads") "Specify number of threads to use"]++usageSyntax = "Usage: shsh (-c <command> | -f <file> | -i | -s) "++main = do args <- getArgs+          let (specifiedOptions, arguments, errors) = getOpt Permute flagList args+              emptyOptions = Flags {helpFlag = False,+                                    inputSourceFlag = UnspecifiedSource,+                                    prettyPrintFlag = False,+                                    threadCount = Nothing}+              options = foldr extractOption emptyOptions specifiedOptions+              extractOption Command options@Flags{inputSourceFlag= UnspecifiedSource} = options{inputSourceFlag= CommandLineSource}+              extractOption Help options = options{helpFlag= True}+              extractOption Interactive options@Flags{inputSourceFlag= UnspecifiedSource}+                 = options{inputSourceFlag= InteractiveSource}+              extractOption StandardInput options@Flags{inputSourceFlag= UnspecifiedSource}+                 = options{inputSourceFlag= StandardInputSource}+              extractOption PrettyPrint options = options{prettyPrintFlag= True}+              extractOption (ScriptFile name) options@Flags{inputSourceFlag= UnspecifiedSource}+                 = options{inputSourceFlag= ScriptFileSource name}+              extractOption (Threads count) options@Flags{threadCount= Nothing} = options{threadCount= Just (read count)}+          if not (null errors) Prelude.|| helpFlag options+             then showHelp+             else case inputSourceFlag options+                  of CommandLineSource -> interpret options (concat (intersperse " " arguments)) >> return ()+                     InteractiveSource -> interact options+                     ScriptFileSource name -> readFile name >>= interpret options >> return ()+                     StandardInputSource -> getContents >>= interpret options >> return ()+                     UnspecifiedSource -> interact options++prettyprint options expression = print expression+                                 >> case compile UnitTag expression+                                    of Compiled tag component -> putStrLn "::" >> print tag+                                                                 >> putStrLn (showComponentTree $ adjust options component)+                                       e@TypeError{} -> print e++showHelp = putStrLn (usageInfo usageSyntax flagList)++interact options = do Just command <- readline "> "+                      addHistory command+                      finish <- interpret options command+                      when (not finish) (interact options)++interpret options command = case parseExpression command+                            of Left position -> hPutStrLn stderr ("Error at " ++ show position) >> return False+                               Right (Exit, "", _) -> return True+                               Right (expression, "", _) -> (if (prettyPrintFlag options)+                                                             then prettyprint options expression+                                                             else case compile UnitTag expression+                                                                  of e@Compiled{} -> execute options e+                                                                     e@TypeError{} -> print e)+                                                            >> return False+                               Right (expression, rest, _) -> hPutStrLn stderr ("Cannot parse \"" ++ rest+                                                                           ++ "\"\nafter " ++ show expression)+                                                              >> return False++execute :: Flags -> Expression -> IO ()+execute options (Compiled CommandTag command) = runPipes (perform $ adjust options command)+execute options (Compiled (ProducerTag CharTag) producer) = liftM fst (runPipes (pipe (produce $ adjust options producer)+                                                                                      (consume toStdOut)))+                                                            >> hFlush stdout+execute options (Compiled tag _) = hPutStrLn stderr ("Expecting a command or a Producer Char, received a " ++ show tag)++adjust Flags{threadCount= Just threads} component = usingThreads threads component+adjust _ component = component++compile :: Typeable x => TypeTag x -> Expression -> Expression+compile inputTag e@Compiled{} = e+compile inputTag e@TypeError{} = e+compile inputTag (Pipe left right)+   = case compile inputTag left+     of Compiled tag@(ProducerTag tag1) p+           -> case compile tag1 right+              of Compiled (ConsumerTag tag2) c -> trycast tag (ProducerTag tag2) p left $ \p'-> Compiled CommandTag (p' >-> c)+                 Compiled (TransducerTag tag2 tag3) t -> trycast tag (ProducerTag tag2) p left $+                                                         \p'-> Compiled (ProducerTag tag3) (p' >-> t)+                 e@TypeError{} -> e+        Compiled (TransducerTag tag1 tag2) t+           -> case compile tag2 right+              of Compiled tag3@ConsumerTag{} c -> trycast tag3 (ConsumerTag tag2) c right $+                                                  \c'-> Compiled (ConsumerTag tag1) (t >-> c')+                 Compiled tag@(TransducerTag tag3 tag4) t2 -> trycast tag (TransducerTag tag2 tag4) t2 right $+                                                              \t2'-> Compiled (TransducerTag tag1 tag4) (t >-> t2')+                 e@TypeError{} -> e+        e@TypeError{} -> e+compile UnitTag (NativeCommand command)+   = Compiled (ProducerTag CharTag) (liftAtomicProducer command ioCost $+                                     \sink-> do (_, stdout, _, pid) <- liftPipe (Process.runInteractiveCommand command)+                                                produce (fromHandle stdout True) sink)+compile UnitTag (FileProducer path) = Compiled (ProducerTag CharTag) (fromFile path)+compile UnitTag StdInProducer = Compiled (ProducerTag CharTag) fromStdIn+compile inputTag (FromList string) = Compiled (ProducerTag CharTag) (liftAtomicProducer "putList" 1 $+                                                                     \sink-> putList string sink >> return ())+compile inputTag (FileConsumer path) = Compiled (ConsumerTag CharTag) (toFile path)+compile inputTag (FileAppend path) = Compiled (ConsumerTag CharTag) (appendFile path)+compile inputTag Suppress = Compiled (ConsumerTag inputTag) suppress+compile inputTag (ErrorConsumer message) = Compiled (ConsumerTag inputTag) (erroneous message)+compile inputTag (Sequence e1 e2) = compileJoin sequence inputTag e1 e2+compile inputTag (Join e1 e2) = compileJoin join inputTag e1 e2+compile inputTag (ForEach splitter true false) = combineSplitterAndBranches foreach inputTag splitter true false+compile inputTag (If splitter true false) = combineSplitterAndBranches ifs inputTag splitter true false+compile inputTag (NativeCommand command) = Compiled (TransducerTag CharTag CharTag) (liftAtomicTransducer command ioCost f)+   where f source sink = do (stdin, stdout, stderr, pid) <- liftPipe (Process.runInteractiveCommand command)+                            liftPipe (do hSetBuffering stdin NoBuffering+                                         hSetBuffering stdout NoBuffering+                                         err <- hGetContents stderr+                                         forkIO (evaluate (length err) >> return ()))+                            interleave source stdin pid stdout sink+                            return []+         interleave :: forall c. Source c Char -> Handle -> Process.ProcessHandle -> Handle -> Sink c Char -> Pipe c IO ()+         interleave source stdin pid stdout sink = interleave1+            where interleave1 = get source+                                >>= maybe+                                       (liftPipe (hClose stdin) >> interleaveEnd)+                                       (\x-> liftPipe (Process.getProcessExitCode pid)+                                                >>= maybe+                                                       (liftPipe (hPutChar stdin x) >> interleave2)+                                                       (const interleave2))+                  interleave2 = canPut sink+                                >>= flip when (liftPipe (hReady stdout)+                                               >>= flip when (liftPipe (hGetChar stdout)+                                                              >>= put sink+                                                              >> return ())+                                               >> interleave1)+                  interleaveEnd = canPut sink+                                  >>= flip when (liftPipe (hIsEOF stdout)+                                                 >>= cond+                                                        (liftPipe $ hClose stdout)+                                                        (liftPipe (hGetChar stdout)+                                                         >>= put sink+                                                         >> interleaveEnd))+compile inputTag (Select e) = case compile inputTag e+                              of Compiled (SplitterTag tag) s -> Compiled (TransducerTag tag tag) (select s)+                                 Compiled tag _  -> TypeError tag (SplitterTag inputTag) e+                                 e'@TypeError{} -> e'+compile inputTag (While condition body)+   = case (compile inputTag condition, compile inputTag body)+     of (Compiled (SplitterTag tag1) s, Compiled tag2@TransducerTag{} t)+           -> let tag2' = TransducerTag tag1 tag1+              in trycast tag2 tag2' t body (\t'-> Compiled tag2' (while t' s))+compile inputTag (FollowedBy left right) = combineSplittersOfSameType followedBy inputTag left right+compile inputTag (And left right) = combineSplittersOfSameType (>&) inputTag left right+compile inputTag (Or left right) = combineSplittersOfSameType (>|) inputTag left right+compile inputTag (ZipWithAnd left right) = combineSplittersOfSameType (&&) inputTag left right+compile inputTag (ZipWithOr left right) = combineSplittersOfSameType (||) inputTag left right+compile inputTag (Nested left right) = combineSplittersOfSameType nestedIn inputTag left right+compile inputTag (Having left right) = combineSplittersOfSameType having inputTag left right+compile inputTag (HavingOnly left right) = combineSplittersOfSameType havingOnly inputTag left right+compile inputTag (Between left right) = combineSplittersOfSameType (...) inputTag left right+compile inputTag (Not splitter) = wrapSplitter snot inputTag splitter+compile inputTag (First splitter) = wrapSplitter first inputTag splitter+compile inputTag (Last splitter) = wrapSplitter last inputTag splitter+compile inputTag (Prefix splitter) = wrapSplitter prefix inputTag splitter+compile inputTag (Suffix splitter) = wrapSplitter suffix inputTag splitter+compile inputTag (StartOf splitter) = wrapSplitter startOf inputTag splitter+compile inputTag (EndOf splitter) = wrapSplitter endOf inputTag splitter+compile inputTag (Prepend prefix) = wrapProducerIntoTransducer prepend inputTag prefix+compile inputTag (Append suffix) = wrapProducerIntoTransducer append inputTag suffix+compile inputTag (Substitute replacement) = wrapGenericProducerIntoTransducer substitute inputTag replacement+compile inputTag IdentityTransducer = Compiled (TransducerTag inputTag inputTag) asis+compile inputTag Count = Compiled (TransducerTag inputTag IntTag) count+compile inputTag@(ListTag itemTag) Concatenate = Compiled (TransducerTag inputTag itemTag) concatenate+compile inputTag Concatenate = TypeError inputTag (ListTag AnyTag) Concatenate+compile inputTag Group = Compiled (TransducerTag inputTag (ListTag inputTag)) group+compile CharTag Uppercase = Compiled (TransducerTag CharTag CharTag) uppercase+compile inputTag Uppercase = TypeError (TransducerTag CharTag CharTag) (TransducerTag inputTag inputTag) Uppercase+compile inputTag@CharTag ShowTransducer = Compiled (TransducerTag inputTag (ListTag CharTag)) toString+compile inputTag@IntTag ShowTransducer = Compiled (TransducerTag inputTag (ListTag CharTag)) toString+compile inputTag ShowTransducer+   = TypeError (TransducerTag IntTag (ListTag CharTag)) (TransducerTag inputTag AnyTag) ShowTransducer+{-+compile inputTag ShowTransducer = let targetType = TransducerTag ShowableTag (ListTag CharTag)+                                      actualType = TransducerTag inputTag (ListTag CharTag)+                                  in trycast targetType actualType toString ShowTransducer (Compiled actualType)+-}+compile inputTag EverythingSplitter = Compiled (SplitterTag inputTag) everything+compile inputTag NothingSplitter = Compiled (SplitterTag inputTag) nothing+compile inputTag WhitespaceSplitter = Compiled (SplitterTag CharTag) whitespace+compile inputTag LineSplitter = Compiled (SplitterTag CharTag) line+compile inputTag LetterSplitter = Compiled (SplitterTag CharTag) letters+compile inputTag DigitSplitter = Compiled (SplitterTag CharTag) digits+compile inputTag OneSplitter = Compiled (SplitterTag inputTag) one+compile CharTag (SubstringSplitter part) = Compiled (SplitterTag CharTag) (substring part)+compile inputTag e@SubstringSplitter{} = TypeError (SplitterTag CharTag) (SplitterTag inputTag) e++compile inputTag expression = error ("Cannot compile " ++ show expression ++ " with input " ++ show inputTag)++compileJoin :: forall t. Typeable t =>+               (forall t1 t2 t3 m x y c1 c2 c3. JoinableComponentPair t1 t2 t3 m x y c1 c2 c3 => c1 -> c2 -> c3)+                  -> TypeTag t -> Expression -> Expression -> Expression+compileJoin combinator inputTag e1 e2 = case (compile inputTag e1, compile inputTag e2)+                                        of (Compiled CommandTag c1, Compiled CommandTag c2)+                                              -> Compiled CommandTag (combinator c1 c2)+                                           (Compiled tag1@ProducerTag{} p1, Compiled tag2@ProducerTag{} p2)+                                              -> trycast tag2 tag1 p2 e2 (\p2'-> Compiled tag1 (combinator p1 p2'))+                                           (Compiled tag1@ConsumerTag{} c1, Compiled tag2@ConsumerTag{} c2)+                                              -> trycast tag2 tag1 c2 e2 (\c2'-> Compiled tag1 (combinator c1 c2'))+                                           (Compiled tag1@TransducerTag{} t1, Compiled tag2@TransducerTag{} t2)+                                              -> trycast tag2 tag1 t2 e2 (\t2'-> Compiled tag1 (combinator t1 t2'))+                                           (Compiled CommandTag c, Compiled tag@ProducerTag{} p) -> Compiled tag (combinator c p)+                                           (Compiled tag@ProducerTag{} p, Compiled CommandTag c) -> Compiled tag (combinator p c)+                                           (Compiled CommandTag c1, Compiled tag@ConsumerTag{} c2)+                                              -> Compiled tag (combinator c1 c2)+                                           (Compiled tag@ConsumerTag{} c1, Compiled CommandTag c2)+                                              -> Compiled tag (combinator c1 c2)+                                           (Compiled CommandTag c, Compiled tag@TransducerTag{} t) -> Compiled tag (combinator c t)+                                           (Compiled tag@TransducerTag{} t, Compiled CommandTag c) -> Compiled tag (combinator t c)+                                           (Compiled (ProducerTag tag1) p, Compiled (ConsumerTag tag2) c)+                                              -> Compiled (TransducerTag tag2 tag1) (combinator p c)+                                           (Compiled (ConsumerTag tag1) p, Compiled (ProducerTag tag2) c)+                                              -> Compiled (TransducerTag tag1 tag2) (combinator p c)+                                           (Compiled (ProducerTag tag1) p, Compiled tag@(TransducerTag tag2 tag3) t)+                                              -> let tag' = TransducerTag tag2 tag1+                                                 in trycast tag tag' t e2 (\t'-> Compiled tag' (combinator p t'))+                                           (Compiled tag@(TransducerTag tag1 tag2) t, Compiled tag3@ProducerTag{} p)+                                              -> let tag' = TransducerTag tag2 tag1+                                                 in trycast tag3 (ProducerTag tag2) p e2 (\p'-> Compiled tag (combinator t p'))+                                           (Compiled (ConsumerTag tag1) c, Compiled tag@(TransducerTag tag2 tag3) t)+                                              -> let tag' = TransducerTag tag1 tag3+                                                 in trycast tag tag' t e2 (\t'-> Compiled tag' (combinator c t'))+                                           (Compiled tag@(TransducerTag tag1 tag2) t, Compiled tag3@ConsumerTag{} c)+                                              -> let tag' = TransducerTag tag2 tag1+                                                 in trycast tag3 (ConsumerTag tag1) c e2 (\c'-> Compiled tag (combinator t c'))+                                           (e@TypeError{}, _) -> e+                                           (_, e@TypeError{}) -> e+                                           (Compiled tag@SplitterTag{} _, _) -> TypeError tag (ProducerTag AnyTag) e1+                                           (_, Compiled tag@SplitterTag{} _) -> TypeError tag (ProducerTag AnyTag) e2++wrapSplitter :: forall x. Typeable x =>+                (forall x. Typeable x => Splitter IO x -> Splitter IO x) -> TypeTag x -> Expression -> Expression+wrapSplitter combinator inputTag expression = case compile inputTag expression+                                              of Compiled tag@SplitterTag{} splitter -> Compiled tag (combinator splitter)+                                                 Compiled tag _ -> TypeError tag (SplitterTag inputTag) expression+                                                 e@TypeError{} -> e++wrapProducerIntoTransducer :: forall x. Typeable x =>+                              (Producer IO x () -> Transducer IO x x) -> TypeTag x -> Expression -> Expression+wrapProducerIntoTransducer combinator inputTag expression+   = case compile inputTag expression+     of Compiled tag@ProducerTag{} p+           -> trycast tag (ProducerTag inputTag) p expression (\p'-> Compiled (TransducerTag inputTag inputTag) (combinator p'))+        Compiled tag _ -> TypeError tag (ProducerTag inputTag) expression+        e@TypeError{} -> e++wrapGenericProducerIntoTransducer :: forall x. Typeable x =>+                                     (forall y r. Typeable y => Producer IO y r -> Transducer IO x y)+                                        -> TypeTag x -> Expression -> Expression+wrapGenericProducerIntoTransducer combinator inputTag expression+   = case compile inputTag expression of Compiled (ProducerTag outTag) p -> Compiled (TransducerTag inputTag outTag) (combinator p)+                                         Compiled tag _ -> TypeError tag (ProducerTag inputTag) expression+                                         e@TypeError{} -> e++combineSplittersOfSameType :: forall x. Typeable x =>+                              (forall x. Typeable x => Splitter IO x -> Splitter IO x -> Splitter IO x)+                                 -> TypeTag x -> Expression -> Expression -> Expression+combineSplittersOfSameType combinator inputTag left right+   = case (compile inputTag left, compile inputTag right)+     of (Compiled tag1@SplitterTag{} s1, Compiled tag2@SplitterTag{} s2)+           -> trycast tag2 tag1 s2 right (\s2'-> Compiled tag1 (combinator s1 s2'))++combineTransducersOfSameType :: forall x. Typeable x =>+                                (forall x y. (Typeable x, Typeable y)=> Transducer IO x y -> Transducer IO x y -> Transducer IO x y)+                                 -> TypeTag x -> Expression -> Expression -> Expression+combineTransducersOfSameType combinator inputTag left right+   = case (compile inputTag left, compile inputTag right)+     of (Compiled tag1@TransducerTag{} t1, Compiled tag2@TransducerTag{} t2)+           -> trycast tag2 tag1 t2 right (\t2'-> Compiled tag1 (combinator t1 t2'))++combineSplitterAndBranches :: forall x. Typeable x =>+                              (forall x cc. (Typeable x, BranchComponent cc IO x [x])=> Splitter IO x -> cc -> cc -> cc)+                                 -> TypeTag x -> Expression -> Expression -> Expression -> Expression+combineSplitterAndBranches combinator inputTag splitter true false+   = case (compile inputTag splitter, compile inputTag true, compile inputTag false)+     of (Compiled (SplitterTag tag1) s, Compiled tag2@ConsumerTag{} t, Compiled tag3@ConsumerTag{} f)+           -> trycast tag2 (ConsumerTag tag1) t true $+              \t'-> trycast tag3 (ConsumerTag tag1) f false $+                       \f'-> Compiled (ConsumerTag tag1) (combinator s t' f')+        (Compiled tag1@SplitterTag{} s, Compiled tag2@SplitterTag{} t, Compiled tag3@SplitterTag{} f)+           -> trycast tag2 tag1 t true $+              \t'-> trycast tag3 tag1 f false $+                       \f'-> Compiled tag1 (combinator s t' f')+        (Compiled (SplitterTag tag1) s, Compiled tag2@(TransducerTag tag2a tag2b) t, Compiled tag3@TransducerTag{} f)+           -> let tag2' = TransducerTag tag1 tag2b+              in trycast tag2 tag2' t true $+                    \t'-> trycast tag3 tag2' f false $+                             \f'-> Compiled tag2' (combinator s t' f')+        (Compiled (SplitterTag tag1) s, Compiled tag2@(TransducerTag tag2a tag2b) t, Compiled tag3@ConsumerTag{} f)+           -> let tag2' = TransducerTag tag1 tag2b+              in trycast tag2 tag2' t true $+                    \t'-> trycast tag3 (ConsumerTag tag1) f false $+                             \f'-> Compiled tag2' (combinator s t' (consumeBy f'))+        (Compiled (SplitterTag tag1) s, Compiled tag2@ConsumerTag{} t, Compiled tag3@(TransducerTag tag3a tag3b) f)+           -> let tag3' = TransducerTag tag1 tag3b+              in trycast tag2 (ConsumerTag tag1) t true $+                    \t'-> trycast tag3 tag3' f false $+                             \f'-> Compiled tag3' (combinator s (consumeBy t') f')+        (e@TypeError{}, _, _) -> e+        (_, e@TypeError{}, _) -> e+        (_, _, e@TypeError{}) -> e+        (Compiled SplitterTag{} _, Compiled tag _, _) -> TypeError tag (TransducerTag inputTag AnyTag) true+        (Compiled SplitterTag{} _, _, Compiled tag _) -> TypeError tag (TransducerTag inputTag AnyTag) false+        (Compiled tag _, _, _) -> TypeError tag (SplitterTag inputTag) splitter++parseExpression :: String -> Either Int (Expression, [Char], Int)+parseExpression s = case parse partialExpressionParser "" s of+   Left error -> Left (sourceLine (errorPos error))+   Right result -> Right result++lexer = (makeTokenParser language) {stringLiteral= stringLexemeParser}++language = emptyDef{commentLine= "#",+                    reservedOpNames= ["...", ">!", ">", ">&", ">,", ">>", ">|", "|", "||", ";", "&"],+                    reservedNames= ["append", "concatenate", "count", "digits", "do",+                                    "else", "end", "error", "exit", "everything", "first", "foreach",+                                    "group", "having", "having-only", "id", "if", "in",+                                    "last", "letters", "line", "nested", "nothing", "prefix", "prepend",+                                    "select", "show", "stdin", "substitute", "substring", "suffix", "suppress",+                                    "then", "uppercase", "while", "whitespace"]}++reservedTokens = reservedOpNames language ++ reservedNames language++partialExpressionParser :: Parser (Expression, [Char], Int)+partialExpressionParser = do whiteSpace lexer+                             t <- expressionParser+                             whiteSpace lexer+                             rest <- getInput+                             pos <- getPosition+                             return (t, rest, sourceLine pos - 1)++expressionParser :: Parser Expression+expressionParser = do head <- stepParser+                      whiteSpace lexer+                      (do tail <- many1 (try (symbol lexer ";" >> stepParser))+                          return (foldr1 Sequence (head:tail))+                       <|>+                       do tail <- many1 (try (symbol lexer "&" >> stepParser))+                          return (foldr1 Join (head:tail))+                       <|>+                       return head+                       )++stepParser :: Parser Expression+stepParser = do head <- termParser+                whiteSpace lexer+                tail <- many (try (char '|' >> whiteSpace lexer >> termParser))+                return (foldr1 Pipe (head:tail))++termParser :: Parser Expression+termParser =+   do first <- prefixTermParser+      whiteSpace lexer+      option first (liftM (foldr1 FollowedBy . (first :)) (many1 $ try (symbol lexer ">," >> prefixTermParser))+                    <|>+                    liftM (foldr1 Or . (first :)) (many1 $ try (symbol lexer ">|" >> prefixTermParser))+                    <|>+                    liftM (foldr1 And . (first :)) (many1 $ try (symbol lexer ">&" >> prefixTermParser))+                    <|>+                    liftM (foldr1 ZipWithOr . (first :)) (many1 $ try (symbol lexer "||" >> prefixTermParser))+                    <|>+                    liftM (foldr1 ZipWithAnd . (first :)) (many1 $ try (symbol lexer "&&" >> prefixTermParser))+                    <|>+                    liftM (Between first) (try (symbol lexer "..." >> prefixTermParser))+                    <|>+                    liftM (Having first) (try (symbol lexer "having" >> prefixTermParser))+                    <|>+                    liftM (HavingOnly first) (try (symbol lexer "having-only" >> prefixTermParser))+                   )++prefixTermParser :: Parser Expression+prefixTermParser =+   try (symbol lexer ">!" >> liftM Not prefixTermParser)+   <|> try (symbol lexer "prefix" >> liftM Prefix prefixTermParser)+   <|> try (symbol lexer "suffix" >> liftM Suffix prefixTermParser)+   <|> try (symbol lexer "prepend" >> liftM Prepend prefixTermParser)+   <|> try (symbol lexer "append" >> liftM Append prefixTermParser)+   <|> try (symbol lexer "substitute" >> liftM Substitute prefixTermParser)+   <|> try (symbol lexer "first" >> liftM First prefixTermParser)+   <|> try (symbol lexer "last" >> liftM Last prefixTermParser)+   <|> try (symbol lexer "start-of" >> liftM StartOf prefixTermParser)+   <|> try (symbol lexer "end-of" >> liftM EndOf prefixTermParser)+   <|> try (symbol lexer "select" >> liftM Select prefixTermParser)+   <|> primaryParser++primaryParser :: Parser Expression+primaryParser =+   try (do char '('+           whiteSpace lexer+           expression <- expressionParser+           whiteSpace lexer+           char ')'+           return expression)+   <|> try (symbol lexer "exit" >> return Exit)+   <|> try (nativeSourceParser "cat")+   <|> try (nativeSourceParser "ls")+   <|> try (do symbol lexer "echo"+               string <- nativeCommand True+               return (FromList string))+   <|> try (symbol lexer "stdin" >> return StdInProducer)+   <|> try (do symbol lexer ">>"+               file <- parameterParser True+               return (FileAppend file))+   <|> try (do symbol lexer ">"+               file <- parameterParser True+               return (FileConsumer file))+   <|> try (symbol lexer "suppress" >> return Suppress)+   <|> try (do symbol lexer "error"+               message <- (try (parameterParser True) <|> return "Error sink reached!")+               return (ErrorConsumer message))+   <|> try (symbol lexer "id" >> return IdentityTransducer)+   <|> try (symbol lexer "uppercase" >> return Uppercase)+   <|> try (symbol lexer "count" >> return Count)+   <|> try (symbol lexer "show" >> return ShowTransducer)+   <|> try (symbol lexer "concatenate" >> return Concatenate)+   <|> try (symbol lexer "group" >> return Group)+   <|> try (symbol lexer "everything" >> return EverythingSplitter)+   <|> try (symbol lexer "nothing" >> return NothingSplitter)+   <|> try (symbol lexer "whitespace" >> return WhitespaceSplitter)+   <|> try (symbol lexer "line" >> return LineSplitter)+   <|> try (symbol lexer "letters" >> return LetterSplitter)+   <|> try (symbol lexer "digits" >> return DigitSplitter)+   <|> try (symbol lexer "one" >> return OneSplitter)+   <|> try (do symbol lexer "substring"+               part <- parameterParser True+               return (SubstringSplitter part))+   <|> try (do symbol lexer "if"+               splitter <- expressionParser+               whiteSpace lexer+               symbol lexer "then"+               true <- expressionParser+               false <- (try (symbol lexer "else" >> expressionParser)+                         <|> return Suppress)+               symbol lexer "end"+               option "" (symbol lexer "if")+               return (If splitter true false))+   <|> try (do symbol lexer "nested"+               core <- expressionParser+               whiteSpace lexer+               symbol lexer "in"+               shell <- expressionParser+               whiteSpace lexer+               symbol lexer "end"+               option "" (symbol lexer "nested")+               return (Nested core shell))+   <|> try (do symbol lexer "while"+               test <- expressionParser+               whiteSpace lexer+               symbol lexer "do"+               body <- expressionParser+               whiteSpace lexer+               symbol lexer "end"+               option "" (symbol lexer "while")+               return (While test body))+   <|> try (do symbol lexer "foreach"+               splitter <- expressionParser+               whiteSpace lexer+               symbol lexer "then"+               trueBranch <- expressionParser+               whiteSpace lexer+               falseBranch <- (try (symbol lexer "else" >> expressionParser)+                               <|> return IdentityTransducer)+               whiteSpace lexer+               symbol lexer "end"+               option "" (symbol lexer "foreach")+               return (ForEach splitter trueBranch falseBranch))+   <|> liftM NativeCommand (nativeCommand False)++nativeSourceParser :: String -> Parser Expression+nativeSourceParser command = do symbol lexer command+                                params <- nativeCommand False+                                return (NativeCommand (command ++ " " ++ params))++nativeCommand :: Bool -> Parser String+nativeCommand normalize = do parts <- try (lexeme lexer (parameterParser normalize)+                                           `manyTill`+                                           ((eof >> return "") <|> lookAhead (choice (map (try . symbol lexer) reservedTokens))))+                             return (concat (intersperse " " parts))+   where manyTill :: GenParser tok st a -> GenParser tok st end -> GenParser tok st [a]+         manyTill p end      = scan+            where scan  = do{ end; return [] }+                            <|>+                          do{ x <- p; xs <- scan; return (x:xs) }+                            <|>+                          return []++parameterParser :: Bool -> Parser String+parameterParser normalize = do chars <- many (noneOf " \t\n'\"`\\()[]{}<>|&;")+                               (do try (string "\\n")+                                   rest <- option "" (parameterParser normalize)+                                   return (chars ++ '\n' : rest)+                                <|>+                                do try (string "\\t")+                                   rest <- option "" (parameterParser normalize)+                                   return (chars ++ '\t' : rest)+                                <|>+                                do next <- escape+                                   rest <- option "" (parameterParser normalize)+                                   return (chars ++ next : rest)+                                <|>+                                do quote <- oneOf "'\"`"+                                   string <- many (try (noneOf (quote : "\\")) <|> escape)+                                   char quote+                                   rest <- option "" (parameterParser normalize)+                                   return (chars ++ (if normalize then string else quote : (string ++ [quote])) ++ rest)+                                <|>+                                do try (char '(')+                                   whiteSpace lexer+                                   inside <- nativeCommand normalize+                                   char ')'+                                   rest <- option "" (parameterParser normalize)+                                   return (chars ++ '(' : inside ++ ')' : rest)+                                <|>+                                do try (char '[')+                                   whiteSpace lexer+                                   inside <- nativeCommand normalize+                                   char ']'+                                   rest <- parameterParser normalize+                                   return (chars ++ '[' : inside ++ ']' : rest)+                                <|>+                                do try (char '{')+                                   whiteSpace lexer+                                   inside <- nativeCommand normalize+                                   char '}'+                                   rest <- option "" (parameterParser normalize)+                                   return (chars ++ '{' : inside ++ '}' : rest)+                                <|>+                                do when (null chars) parserZero                                    return chars)  escape :: Parser Char
Test.hs view
@@ -16,7 +16,7 @@  {-# LANGUAGE DeriveDataTypeable, FlexibleInstances, ScopedTypeVariables, PatternSignatures #-} -module Test where+module Main where  import Control.Concurrent.SCC.Foundation import Control.Concurrent.SCC.ComponentTypes@@ -25,6 +25,7 @@ import qualified Control.Concurrent.SCC.Combinators as Combinators  import Control.Monad (liftM)+import Control.Monad.Identity (Identity (Identity)) import Data.Char (ord, isLetter, isSpace, toUpper) import Data.Dynamic (Typeable) import Data.List (find, stripPrefix, groupBy, intersect, union, intercalate, isInfixOf, isPrefixOf, isSuffixOf, sort)@@ -32,7 +33,7 @@ import qualified Data.List as List import qualified Data.Foldable as Foldable import qualified Data.Sequence as Seq-import Data.Sequence (Seq, (|>), ViewL (EmptyL, (:<)))+import Data.Sequence (Seq, (|>), (><), ViewL (EmptyL, (:<))) import Debug.Trace (trace) import Prelude hiding (even, last) import qualified Prelude@@ -55,18 +56,28 @@          label "substitute" prop_substitute,          label "prepend" prop_prepend,          label "append" prop_append,-         label "allTrue" prop_allTrue,-         label "allFalse" prop_allFalse,+         label "everything" prop_allTrue,+         label "nothing" prop_allFalse,          label "substring" prop_substring,          label "group" prop_group,          label "concatenate" prop_concatenate,          label "concatSeparate" prop_concatSeparate,-         label "uppercase ->>" $ \s-> runPipes (pipe (putList s) (uppercase ->> getList)) == Just ([], map toUpper s),-         label "uppercase <<-" $ \s-> runPipes (pipe (uppercase <<- putList s) getList) == Just ([], map toUpper s),+         label "uppercase ->>" $ \s-> runPipes (pipe (putList s) (consume $ uppercase >-> liftAtomicConsumer "getList" 1 getList))+                  == Just ([], map toUpper s),+         label "uppercase <<-" $ \s-> runPipes (pipe (produce $ liftAtomicProducer "putList" 1 (putList s) >-> uppercase) getList)+                  == Just ([], map toUpper s),          label "uppercase `join` asis" $ \s-> transducerOutput (uppercase `join` asis) s == map toUpper s ++ s,-         label "prepend >-> append" $ \s-> transducerOutput (prepend "Hello, " >-> append "!") s == "Hello, "++ s ++ "!",+         label "prepend >-> append" (\(s :: String) prefix suffix->+                                     transducerOutput (prepend (fromList prefix) >-> append (fromList suffix)) s+                                     == prefix ++ s ++ suffix),+         label "prepend == (`join` asis) . substitute" $+               \(s :: String) prefix-> transducerOutput (prepend (fromList prefix)) s+                                       == transducerOutput (substitute (fromList prefix) `join` asis) s,+         label "append == (asis `join`) . substitute" $+               \(s :: String) suffix-> transducerOutput (append (fromList suffix)) s+                                       == transducerOutput (asis `join` substitute (fromList suffix)) s,          label "whitespace" $ \s-> splitterOutputs whitespace s == (filter isSpace s, filter (not . isSpace) s),-         label "ifs allTrue asis asis" $ \(s :: [TestEnum])-> transducerOutput (ifs allTrue asis asis) s == s,+         label "ifs everything asis asis" $ \(s :: [TestEnum])-> transducerOutput (ifs everything asis asis) s == s,          label "substring" $ \s (c :: TestEnum)-> splitterOutputs (substring [c]) s == (filter (==c) s, filter (/=c) s),          label "ifs (substring X) uppercase asis" $                \s (LowercaseLetter c)-> transducerOutput (ifs (substring [c]) uppercase asis) s@@ -74,7 +85,8 @@          label "count >-> toString >-> concatenate" $                \(s :: [TestEnum])-> transducerOutput (count >-> toString >-> concatenate) s == show (length s),          label "foreach whitespace asis (prepend \"[\" >-> append \"]\")" $-               \s-> transducerOutput (foreach whitespace asis (prepend "[" >-> append "]")) s == mapWords (("[" ++) . (++ "]")) s,+               \s-> transducerOutput (foreach whitespace asis (prepend (fromList "[") >-> append (fromList "]"))) s+                       == mapWords (("[" ++) . (++ "]")) s,          label "foreach whitespace asis (count >-> toString >-> concatenate)" $                \s-> transducerOutput (foreach whitespace asis (count >-> toString >-> concatenate)) s == mapWords (show . length) s,          label "uppercase `wherever` (snot whitespace `having` substring X)" $@@ -136,9 +148,12 @@          label "first followedBy" $ \trace1 trace2 n-> prop_followedBy2 (splitterFromTrace trace1) (splitterFromTrace trace2) n,          label "substring followedBy substring 1" prop_followedBy3,          label "substring followedBy substring 2" prop_followedBy4,-         label "between..."  $ \trace1 trace2 n-> prop_between_first_last (simpleSplitterFromTrace trace1)-                                                                          (simpleSplitterFromTrace trace2) n]+         label "substring followedBy substring 3" prop_followedBy5,+         label "... followedBy ..." prop_followedByBetween,+         label "start ... end"  $ \trace n-> prop_between1 (simpleSplitterFromTrace trace) n,+         label "start everything ... end"  $ \trace n-> prop_between2 (simpleSplitterFromTrace trace) n] + prop_pour :: [Int] -> Bool prop_pour input = runPipes (pipeD "input" (putList input) (\source-> pipeD "output" (\sink-> pour source sink) getList))                   == Just ([], ((), input))@@ -147,22 +162,24 @@ prop_asis input = transducerOutput asis input == input  prop_suppress :: [Int] -> Bool-prop_suppress input = null (transducerOutput (suppress :: Transducer Maybe Int ()) input)+prop_suppress input = null (transducerOutput (consumeBy suppress :: Transducer Identity Int ()) input)  prop_substitute :: [Int] -> [Maybe Int] -> Bool-prop_substitute input replacement = transducerOutput (substitute replacement) input == replacement+prop_substitute input replacement = transducerOutput (substitute $ fromList replacement) input == replacement -prop_prepend :: [Int] -> [Int] -> Bool-prop_prepend input prefix = transducerOutput (prepend prefix) input == prefix ++ input+prop_prepend :: [Int] -> [Int] -> Int -> Property+prop_prepend input prefix threads = threads > 0 ==>+                                    transducerOutput (usingThreads threads $ prepend $ fromList prefix) input == prefix ++ input -prop_append :: [Int] -> [Int] -> Bool-prop_append input suffix = transducerOutput (append suffix) input == input ++ suffix+prop_append :: [Int] -> [Int] -> Int -> Property+prop_append input suffix threads = threads > 0 ==>+                                   transducerOutput (usingThreads threads $ append $ fromList suffix) input == input ++ suffix  prop_allTrue :: [Int] -> Bool-prop_allTrue input = splitterOutputs allTrue input == (input, [])+prop_allTrue input = splitterOutputs everything input == (input, [])  prop_allFalse :: [Int] -> Bool-prop_allFalse input = splitterOutputs allFalse input == ([], input)+prop_allFalse input = splitterOutputs nothing input == ([], input)  prop_substring :: [TestEnum] -> [TestEnum] -> Property prop_substring input sublist = trivial (not (isInfixOf sublist input)) (fst (splitterOutputs (substring sublist) input)@@ -177,69 +194,76 @@ prop_concatSeparate :: [[TestEnum]] -> [TestEnum] -> Bool prop_concatSeparate input separator = transducerOutput (concatSeparate separator) input == intercalate separator input -prop_snot :: Splitter Maybe Int -> [Int] -> Bool+prop_snot :: Splitter Identity Int -> [Int] -> Bool prop_snot splitter input = splitterOutputs (snot splitter) input == swap (splitterOutputs splitter input) -prop_andAssoc :: SplitterTrace -> SplitterTrace -> SplitterTrace -> [Int] -> Bool-prop_andAssoc st1 st2 st3 input = --trace (show $ (splitterOutputs (s1 >& (s2 >& s3)) input, splitterOutputs ((s1 >& s2) >& s3) input)) $-                                  splitterOutputs (s1 >& (s2 >& s3)) input == splitterOutputs ((s1 >& s2) >& s3) input+prop_andAssoc :: SplitterTrace -> SplitterTrace -> SplitterTrace -> [Int] -> Int -> Int -> Property+prop_andAssoc st1 st2 st3 input t1 t2+   = t1 > 0 && t2 > 0+     ==> splitterOutputs (usingThreads t1 $ s1 >& (s2 >& s3)) input == splitterOutputs (usingThreads t2 $ (s1 >& s2) >& s3) input    where s1 = splitterFromTrace st1          s2 = splitterFromTrace st2          s3 = splitterFromTrace st3 -prop_orAssoc :: SplitterTrace -> SplitterTrace -> SplitterTrace -> [Int] -> Bool-prop_orAssoc st1 st2 st3 input = splitterOutputs (s1 >| (s2 >| s3)) input == splitterOutputs ((s1 >| s2) >| s3) input+prop_orAssoc :: SplitterTrace -> SplitterTrace -> SplitterTrace -> [Int] -> Int -> Int -> Property+prop_orAssoc st1 st2 st3 input t1 t2+   = t1 > 0 && t2 > 0+     ==> splitterOutputs (usingThreads t1 $ s1 >| (s2 >| s3)) input == splitterOutputs (usingThreads t2 $ (s1 >| s2) >| s3) input    where s1 = splitterFromTrace st1          s2 = splitterFromTrace st2          s3 = splitterFromTrace st3 -prop_DeMorgan1 :: Splitter Maybe Int -> Splitter Maybe Int -> [Int] -> Bool-prop_DeMorgan1 s1 s2 input = splitterOutputs (snot (s1 >& s2)) input == splitterOutputs (snot s1 >| snot s2) input+prop_DeMorgan1 :: Splitter Identity Int -> Splitter Identity Int -> [Int] -> Int -> Int -> Property+prop_DeMorgan1 s1 s2 input t1 t2+   = t1 > 0 && t2 > 0+     ==> splitterOutputs (usingThreads t1 $ snot (s1 >& s2)) input == splitterOutputs (usingThreads t2 $ snot s1 >| snot s2) input -prop_DeMorgan2 :: Splitter Maybe Int -> Splitter Maybe Int -> [Int] -> Bool-prop_DeMorgan2 s1 s2 input = splitterOutputs (snot (s1 >| s2)) input == splitterOutputs (snot s1 >& snot s2) input+prop_DeMorgan2 :: Splitter Identity Int -> Splitter Identity Int -> [Int] -> Int -> Int -> Property+prop_DeMorgan2 s1 s2 input t1 t2+   = t1 > 0 && t2 > 0+     ==> splitterOutputs (usingThreads t1 $ snot (s1 >| s2)) input == splitterOutputs (usingThreads t2 $ snot s1 >& snot s2) input -prop_and :: Splitter Maybe Int -> Splitter Maybe Int -> Int -> Bool+prop_and :: Splitter Identity Int -> Splitter Identity Int -> Int -> Bool prop_and s1 s2 n = fst (splitterOutputs (s1 Combinators.&& s2) l)                    == fst (splitterOutputs s1 l) `intersect` fst (splitterOutputs s2 l)    where l = [1 .. abs n] -prop_or :: Splitter Maybe Int -> Splitter Maybe Int -> Int -> Bool+prop_or :: Splitter Identity Int -> Splitter Identity Int -> Int -> Bool prop_or s1 s2 n = fst (splitterOutputs (s1 Combinators.|| s2) l)                   == sort (fst (splitterOutputs s1 l) `union` fst (splitterOutputs s2 l))    where l = [1 .. abs n] -prop_even :: Splitter Maybe TestEnum -> [TestEnum] -> Bool+prop_even :: Splitter Identity TestEnum -> [TestEnum] -> Bool prop_even splitter input = let splitOddEven [] = ([], [])                                splitOddEven (head:tail) = let (evens, odds) = splitOddEven tail in (head:odds, evens)                            in fst (splitterOutputs (even splitter) input)-                              == concat (snd $ splitOddEven $ transducerOutput (foreach splitter group suppress) input)+                              == concat (snd $ splitOddEven $ transducerOutput (foreach splitter group (consumeBy suppress)) input) -prop_prefix_1 :: Splitter Maybe TestEnum -> [TestEnum] -> Bool+prop_prefix_1 :: Splitter Identity TestEnum -> [TestEnum] -> Bool prop_prefix_1 splitter input = let (pfx, rest) = splitterOutputs (prefix splitter) input                                    (true, false) = splitterOutputs splitter input                                in pfx ++ rest == input && pfx `isPrefixOf` true -prop_prefix_2 :: Splitter Maybe TestEnum -> [TestEnum] -> Bool+prop_prefix_2 :: Splitter Identity TestEnum -> [TestEnum] -> Bool prop_prefix_2 splitter input = let (prefix1, rest1) = splitterOutputs (prefix splitter) input                                in case splitterOutputChunks splitter input                                   of (prefix2, True):rest2 -> prefix1 == prefix2 && rest1 == concat (map fst rest2)                                      (prefix2, False):rest2 -> prefix1 == [] && rest1 == prefix2 ++ concat (map fst rest2)                                      [] -> prefix1 ++ rest1 == [] -prop_suffix_1 :: Splitter Maybe TestEnum -> [TestEnum] -> Bool+prop_suffix_1 :: Splitter Identity TestEnum -> [TestEnum] -> Bool prop_suffix_1 splitter input = let (sfx, rest) = splitterOutputs (suffix splitter) input                                    (true, false) = splitterOutputs splitter input                                in rest ++ sfx == input && sfx `isSuffixOf` true -prop_suffix_2 :: Splitter Maybe TestEnum -> [TestEnum] -> Bool+prop_suffix_2 :: Splitter Identity TestEnum -> [TestEnum] -> Bool prop_suffix_2 splitter input = let (suffix1, rest1) = splitterOutputs (suffix splitter) input                                in case reverse (splitterOutputChunks splitter input)                                   of (suffix2, True):rest2 -> suffix1 == suffix2 && rest1 == concat (map fst (reverse rest2))                                      (suffix2, False):rest2 -> suffix1 == [] && rest1 == concat (map fst (reverse rest2)) ++ suffix2                                      [] -> rest1 ++ suffix1 == [] -prop_first :: Splitter Maybe TestEnum -> [TestEnum] -> Bool+prop_first :: Splitter Identity TestEnum -> [TestEnum] -> Bool prop_first splitter input = let (first1, rest1) = splitterOutputs (first splitter) input                             in case splitterOutputChunks splitter input                                of (first2, True):rest2 -> first1 == first2 && rest1 == concat (map fst rest2)@@ -248,7 +272,7 @@                                   (prefix, False):[] -> first1 == [] && rest1 == prefix                                   [] -> first1 ++ rest1 == [] -prop_last :: Splitter Maybe TestEnum -> [TestEnum] -> Bool+prop_last :: Splitter Identity TestEnum -> [TestEnum] -> Bool prop_last splitter input = let (last1, rest1) = splitterOutputs (last splitter) input                            in -- trace (show (last1, rest1)) $ trace (show (splitterOutputChunks splitter input)) $                               case reverse (splitterOutputChunks splitter input)@@ -258,7 +282,7 @@                                  (suffix, False):[] -> last1 == [] && rest1 == suffix                                  [] -> last1 ++ rest1 == [] -prop_uptoFirst :: Splitter Maybe TestEnum -> [TestEnum] -> Bool+prop_uptoFirst :: Splitter Identity TestEnum -> [TestEnum] -> Bool prop_uptoFirst splitter input = let (first1, rest1) = splitterOutputs (uptoFirst splitter) input                                 in case splitterOutputChunks splitter input                                    of (first2, True):rest2 -> first1 == first2 && rest1 == concat (map fst rest2)@@ -267,7 +291,7 @@                                       (prefix, False):[] -> first1 == [] && rest1 == prefix                                       [] -> first1 ++ rest1 == [] -prop_lastAndAfter :: Splitter Maybe TestEnum -> [TestEnum] -> Bool+prop_lastAndAfter :: Splitter Identity TestEnum -> [TestEnum] -> Bool prop_lastAndAfter splitter input = let (last1, rest1) = splitterOutputs (lastAndAfter splitter) input                                    in case reverse (splitterOutputChunks splitter input)                                       of (last2, True):rest2 -> last1 == last2 && rest1 == concat (map fst (reverse rest2))@@ -276,11 +300,11 @@                                          (suffix, False):[] -> last1 == [] && rest1 == suffix                                          [] -> last1 ++ rest1 == [] -prop_followedBy1 :: Splitter Maybe Int -> Splitter Maybe Int -> Int -> Bool+prop_followedBy1 :: Splitter Identity Int -> Splitter Identity Int -> Int -> Bool prop_followedBy1 s1 s2 n = splitterOutputs (s1 `followedBy` s2) l == splitterOutputs (s1 `followedBy` prefix s2) l    where l = [1 .. abs n] -prop_followedBy2 :: Splitter Maybe Int -> Splitter Maybe Int -> Int -> Bool+prop_followedBy2 :: Splitter Identity Int -> Splitter Identity Int -> Int -> Bool prop_followedBy2 s1 s2 n = splitterOutputs (first (s1 `followedBy` s2)) l == splitterOutputs (first s1 `followedBy` s2) l    where l = [1 .. abs n] @@ -294,27 +318,45 @@                                                                                                `followedBy` substring l2) l3)                                                                          == sublists (l1 ++ l2) l3) -prop_between_first_last :: Splitter Maybe Int -> Splitter Maybe Int -> Int -> Bool-prop_between_first_last s1 s2 n = fst (splitterOutputs (first s1 ... last s2) l)-                                  == sort (fst (splitterOutputs (first s1 `between` last s2) l)-                                           `union`-                                           limits (fst $ splitterOutputs (first s1) l) (fst $ splitterOutputs (last s2) l))-   where limits [] _  = []-         limits l1 [] = l1-         limits l1 l2 | head l1 <= head l2 = l1 `union` l2 -                      | head l1 <= Prelude.last l2 = [head l1 .. Prelude.last l2]-                      | otherwise = l1-         l = [1 .. abs n]+prop_followedBy5 :: Int -> Int -> Int -> Int -> Bool+prop_followedBy5 i1 i2 i3 i4 = let n1 = abs i1+                                   n2 = n1 + abs i2+                                   n3 = n2 + abs i3 + 1+                                   n4 = n3 + abs i4+                               in splitterOutputs (substring [n1 .. n2] `followedBy` substring [n2 + 1 .. n3]) [0 .. n4]+                                     == ([n1 .. n3], [0 .. n1 - 1] ++ [n3 + 1 .. n4]) -transducerOutput :: (Typeable x, Typeable y) => Transducer Maybe x y -> [x] -> [y]+prop_followedByBetween :: Int -> Int -> Int -> Int -> Bool+prop_followedByBetween i1 i2 i3 i4 = let n1 = abs i1+                                         n2 = n1 + abs i2+                                         n3 = n2 + abs i3 + 1+                                         n4 = n3 + abs i4+                                     in splitterOutputs+                                           ((substring [n1] ... substring [n2])+                                            `followedBy` (substring [n2 + 1] ... substring [n3]))+                                           [0 .. n4]+                                     +                                           == ([n1 .. n3], [0 .. n1 - 1] ++ [n3 + 1 .. n4])++prop_between1 :: Splitter Identity Int -> Int -> Bool+prop_between1 splitter n = splitterOutputs (startOf splitter ... endOf splitter) input == splitterOutputs splitter input+                           && splitterOutputs (endOf splitter ... startOf splitter) input == ([], input)+   where input = [1 .. abs n]++prop_between2 :: Splitter Identity Int -> Int -> Bool+prop_between2 splitter n = splitterOutputs (startOf everything ... endOf splitter) input == splitterOutputs (uptoFirst splitter) input+                           || null (fst $ splitterOutputs splitter input)+   where input = [1 .. abs n]++transducerOutput :: (Typeable x, Typeable y) => Transducer Identity x y -> [x] -> [y] transducerOutput t input = case runPipes (pipeD "transducerOutput input"                                                 (putList input)                                                 (\source-> pipeD "transducerOutput output"                                                                  (\sink-> transduce t source sink)                                                                  getList))-                           of Just ([], ([], output)) -> output+                           of Identity ([], ([], output)) -> output -splitterOutputs :: Typeable x => Splitter Maybe x -> [x] -> ([x], [x])+splitterOutputs :: Typeable x => Splitter Identity x -> [x] -> ([x], [x]) splitterOutputs s input = case runPipes (pipeD "splitterOutputs input"                                                (putList input)                                                (\source-> pipeD "splitterOutputs true"@@ -322,19 +364,19 @@                                                                                (split s source true)                                                                                getList)                                                                 getList))-                          of Just ([], (([], false), true)) -> (true, false)+                          of Identity ([], (([], false), true)) -> (true, false) -splitterOutputChunks :: Typeable x => Splitter Maybe x -> [x] -> [([x], Bool)]+splitterOutputChunks :: Typeable x => Splitter Identity x -> [x] -> [([x], Bool)] splitterOutputChunks s input = transducerOutput (foreach s-                                                 (group >-> lift121Transducer (\chunk-> (chunk, True)))-                                                 (group >-> lift121Transducer (\chunk-> (chunk, False))))+                                                 (group >-> lift121Transducer "true" (\chunk-> (chunk, True)))+                                                 (group >-> lift121Transducer "false" (\chunk-> (chunk, False))))                                input -simpleSplitterFromTrace :: (Show x, Typeable x) => SimpleSplitterTrace -> Splitter Maybe x+simpleSplitterFromTrace :: (Show x, Typeable x) => SimpleSplitterTrace -> Splitter Identity x simpleSplitterFromTrace (init, last) = splitterFromTrace (map (maybe Nothing (Just . (,) True)) init, last) -splitterFromTrace :: (Show x, Typeable x) => SplitterTrace -> Splitter Maybe x-splitterFromTrace trace1 = liftSectionSplitter $+splitterFromTrace :: (Show x, Typeable x) => SplitterTrace -> Splitter Identity x+splitterFromTrace trace1 = liftAtomicSectionSplitter "splitterFromTrace" 1 $                            \source true false->                            let follow trace2@(head:tail) q = get source >>= maybe fail succeed                                   where succeed x = let q' = q |> Just x@@ -363,7 +405,7 @@  type SplitterTrace = ([Maybe (Bool, Bool)], Bool) -data TestEnum = One | Two | Three | Four | Five deriving (Eq, Show, Typeable)+data TestEnum = One | Two | Three | Four | Five deriving (Enum, Eq, Show, Typeable)  newtype LowercaseLetter = LowercaseLetter Char deriving (Eq, Show, Typeable) @@ -379,9 +421,9 @@     arbitrary     = fmap LowercaseLetter (choose ('a', 'z'))     coarbitrary (LowercaseLetter c) = variant ((ord c - 65) `rem` 26) -instance Arbitrary (Splitter Maybe Int) where+instance Arbitrary (Splitter Identity Int) where    arbitrary = fmap splitterFromTrace arbitrary    coarbitrary s gen = sized (\n-> coarbitrary (transducerOutput (ifs s-                                                                  (lift121Transducer $ const True)-                                                                  (lift121Transducer $ const False))+                                                                  (lift121Transducer "true" $ const True)+                                                                  (lift121Transducer "false" $ const False))                                                 [1..n]) gen)
grammar.bnf view
@@ -1,76 +1,59 @@ Expression ::=-   ProducerPrimary {"|" TransducerPrimary} ["|" ConsumerPrimary].+     Step {";" Step}+   | Step {"&" Step}. -ProducerExpression ::=-     ProducerPrimary-   | ProducerPrimary "|" TransducerExpression.+Step ::=+   Term {"|" Term}. -ProducerPrimary ::=-   "(" NativeCommand ")" ">"+Term ::=+   PrefixTerm+   [  {"&&" PrefixTerm}+    | {"||" PrefixTerm}+    | {">&" PrefixTerm}+    | {">|" PrefixTerm}+    | {">," PrefixTerm}+    | "having" PrefixTerm+    | "having-only" PrefixTerm+    | "..." PrefixTerm].++PrefixTerm ::=+     Primary+   | "first"      PrefixTerm+   | "last"       PrefixTerm+   | "prefix"     PrefixTerm+   | "suffix"     PrefixTerm+   | "start-of"   PrefixTerm+   | "end-of"     PrefixTerm+   | "prepend"    PrefixTerm+   | "append"     PrefixTerm+   | "substitute" PrefixTerm+   | "select"     PrefixTerm+   | ">!"         PrefixTerm.++Primary ::=+     "(" Expression ")"+   | "exit"    | "cat" Parameters    | "echo" Parameters    | "ls" Parameters    | "stdin"    | "{" [String {"," String}] "}"-   | "(" ProducerExpression ")".--ConsumerExpression ::=-     ConsumerFork-   | TransducerExpression "|" ConsumerFork.--ConsumerFork ::=-   ConsumerPrimary {"tee" ConsumerPrimary}.--ConsumerPrimary ::=-     "(" ConsumerExpression ")"-   | ">" "(" NativeCommand ")"    | "error" [String]-   | "null"+   | "suppress"    | ">" File-   | ">>" File.--TransducerExpression ::=-     TransducerPrimary {"|" TransducerPrimary}-   | TransducerPrimary {"><" TransducerPrimary}.--TransducerPrimary ::=-     "(" TransducerExpression ")"+   | ">>" File+   | "if" Expression "then" Expression ["else" Expression] "end" ["if"]+   | "foreach" Expression "then" Expression ["else" Expression] "end" ["foreach"].    | "id"-   | "suppress"    | "count"    | "group"    | "concatenate"    | "uppercase"-   | "prepend" String-   | "append" String-   | "substitute" String-   | "select" SplitterPrimary-   | "if" SplitterExpression "then" TransducerExpression ["else" TransducerExpression] "end" ["if"]-   | "while" SplitterExpression "do" TransducerExpression "end" ["while"]-   | "foreach" SplitterExpression "then" TransducerExpression ["else" TransducerExpression] "end" ["foreach"]-   | NativeCommand.--SplitterExpression ::=-     SplitterPrimary {"&&" SplitterPrimary}-   | SplitterPrimary {"||" SplitterPrimary}-   | SplitterPrimary {">&" SplitterPrimary}-   | SplitterPrimary {">|" SplitterPrimary}-   | SplitterPrimary {">," SplitterPrimary}-   | SplitterPrimary "having" SplitterPrimary-   | SplitterPrimary "having-only" SplitterPrimary-   | SplitterPrimary "..." SplitterPrimary-   | "first" SplitterPrimary-   | "last" SplitterPrimary-   | "prefix" SplitterPrimary-   | "suffix" SplitterPrimary.--SplitterPrimary ::=-     "(" SplitterExpression ")"-   | ">!" SplitterPrimary+   | "while" Expression "do" Expression "end" ["while"]+   | "nested" Expression "in" Expression "end" ["nested"]    | "whitespace"    | "line"    | "letters"    | "digits"    | "substring" String-   | "nested" SplitterExpression "in" SplitterExpression "end" ["nested"]-   | "between" SplitterExpression "and" SplitterExpression "end" ["between"].+   | NativeCommand.
scc.cabal view
@@ -1,5 +1,5 @@ Name:                scc-Version:             0.1+Version:             0.2 Cabal-Version:       >= 1.2 Build-Type:          Simple Synopsis:            Streaming component combinators@@ -10,7 +10,7 @@   types, a number of primitive streaming components and a set of component combinators. Finally,   there is an executable that exposes all functionality in a command-line shell.   .-  The library design is based on paper <http://www.idealliance.org/papers/extreme/Proceedings/html/2006/Blazevic01/EML2006Blazevic01.html>+  The original library design is based on paper <http://www.idealliance.org/papers/extreme/Proceedings/html/2006/Blazevic01/EML2006Blazevic01.html>   .   Mario Bla&#382;evi&#263;, Streaming component combinators, Extreme Markup Languages, 2006.   @@ -25,9 +25,10 @@   Main-is:           Shell.hs   Other-Modules:     Control.Concurrent.SCC.Foundation, Control.Concurrent.SCC.ComponentTypes,                      Control.Concurrent.SCC.Components, Control.Concurrent.SCC.Combinators-  Build-Depends:     base, containers, process, readline, parsec+  Build-Depends:     base, containers, mtl, parallel, process, readline, parsec >= 3+  GHC-options:       "-threaded"  Library   Exposed-Modules:   Control.Concurrent.SCC.Foundation, Control.Concurrent.SCC.ComponentTypes,                      Control.Concurrent.SCC.Components, Control.Concurrent.SCC.Combinators-  Build-Depends:     base, containers+  Build-Depends:     base, containers, mtl, parallel