packages feed

pipes-attoparsec 0.1.0.1 → 0.2.0.0

raw patch · 13 files changed

+336/−723 lines, 13 filesdep +mmorphdep +pipes-parsedep +transformersdep ~attoparsecdep ~basedep ~bytestringPVP ok

version bump matches the API change (PVP)

Dependencies added: mmorph, pipes-parse, transformers

Dependency ranges changed: attoparsec, base, bytestring, pipes

API changes (from Hackage documentation)

- Control.Proxy.Attoparsec: parserD :: (Proxy p, Monad m, AttoparsecInput a) => Parser a b -> () -> p (ParserStatus a) (ParserSupply a) () b m r
- Control.Proxy.Attoparsec: parserInputD :: (Monad m, Proxy p) => ParserStatus a -> p () a (ParserStatus a) (ParserSupply a) m r
- Control.Proxy.Attoparsec.Control: limitInputLength :: (Monad m, Proxy p, AttoparsecInput a) => Int -> ParserStatus a -> EitherP BadInput p (ParserStatus a) (ParserSupply a) (ParserStatus a) (ParserSupply a) m r
- Control.Proxy.Attoparsec.Control: retryLeftovers :: (Monad m, Proxy p, AttoparsecInput a) => ParserStatus a -> p (ParserStatus a) (ParserSupply a) (ParserStatus a) (ParserSupply a) m r
- Control.Proxy.Attoparsec.Control: skipMalformedChunks :: (Monad m, Proxy p, AttoparsecInput a) => ParserStatus a -> p (ParserStatus a) (ParserSupply a) (ParserStatus a) (ParserSupply a) m r
- Control.Proxy.Attoparsec.Control: throwParsingErrors :: (Monad m, Proxy p, AttoparsecInput a) => ParserStatus a -> EitherP BadInput p (ParserStatus a) (ParserSupply a) (ParserStatus a) (ParserSupply a) m r
- Control.Proxy.Attoparsec.Tutorial: Name :: Text -> Name
- Control.Proxy.Attoparsec.Tutorial: data Name
- Control.Proxy.Attoparsec.Tutorial: hello :: Parser Name
- Control.Proxy.Attoparsec.Tutorial: helloPipe1 :: (Proxy p, Monad m) => () -> Pipe p Text Name m r
- Control.Proxy.Attoparsec.Tutorial: helloPipe2 :: (Proxy p, Monad m) => () -> Pipe p Text Name m r
- Control.Proxy.Attoparsec.Tutorial: helloPipe3 :: (Proxy p, Monad m) => () -> Pipe (EitherP BadInput p) Text Name m r
- Control.Proxy.Attoparsec.Tutorial: helloPipe4 :: (Proxy p, Monad m) => () -> Pipe (EitherP BadInput p) Text Name m r
- Control.Proxy.Attoparsec.Tutorial: helloPipe5 :: (Proxy p, Monad m) => () -> Pipe (EitherP BadInput p) Text Name m r
- Control.Proxy.Attoparsec.Tutorial: helloPipe6 :: (Proxy p, Monad m) => () -> Pipe p Text Name m r
- Control.Proxy.Attoparsec.Tutorial: input1 :: [Text]
- Control.Proxy.Attoparsec.Tutorial: input2 :: [Text]
- Control.Proxy.Attoparsec.Tutorial: instance Eq Name
- Control.Proxy.Attoparsec.Tutorial: instance Show Name
- Control.Proxy.Attoparsec.Tutorial: skipPartialResults :: (Monad m, Proxy p, AttoparsecInput a) => ParserStatus a -> p (ParserStatus a) (ParserSupply a) (ParserStatus a) (ParserSupply a) m r
- Control.Proxy.Attoparsec.Types: Failed :: a -> ParserError -> ParserStatus a
- Control.Proxy.Attoparsec.Types: InputTooLong :: Int -> BadInput
- Control.Proxy.Attoparsec.Types: MalformedInput :: ParserError -> BadInput
- Control.Proxy.Attoparsec.Types: ParserError :: [String] -> String -> ParserError
- Control.Proxy.Attoparsec.Types: Parsing :: Int -> ParserStatus a
- Control.Proxy.Attoparsec.Types: Resume :: SupplyUse
- Control.Proxy.Attoparsec.Types: Start :: SupplyUse
- Control.Proxy.Attoparsec.Types: class Eq a => AttoparsecInput a
- Control.Proxy.Attoparsec.Types: data BadInput
- Control.Proxy.Attoparsec.Types: data ParserError
- Control.Proxy.Attoparsec.Types: data ParserStatus a
- Control.Proxy.Attoparsec.Types: data SupplyUse
- Control.Proxy.Attoparsec.Types: drop :: AttoparsecInput a => Int -> a -> a
- Control.Proxy.Attoparsec.Types: errorContexts :: ParserError -> [String]
- Control.Proxy.Attoparsec.Types: errorMessage :: ParserError -> String
- Control.Proxy.Attoparsec.Types: instance AttoparsecInput ByteString
- Control.Proxy.Attoparsec.Types: instance AttoparsecInput Text
- Control.Proxy.Attoparsec.Types: instance Eq BadInput
- Control.Proxy.Attoparsec.Types: instance Eq ParserError
- Control.Proxy.Attoparsec.Types: instance Eq SupplyUse
- Control.Proxy.Attoparsec.Types: instance Eq a => Eq (ParserStatus a)
- Control.Proxy.Attoparsec.Types: instance Show BadInput
- Control.Proxy.Attoparsec.Types: instance Show ParserError
- Control.Proxy.Attoparsec.Types: instance Show SupplyUse
- Control.Proxy.Attoparsec.Types: instance Show a => Show (ParserStatus a)
- Control.Proxy.Attoparsec.Types: itlLength :: BadInput -> Int
- Control.Proxy.Attoparsec.Types: length :: AttoparsecInput a => a -> Int
- Control.Proxy.Attoparsec.Types: miParserErrror :: BadInput -> ParserError
- Control.Proxy.Attoparsec.Types: null :: AttoparsecInput a => a -> Bool
- Control.Proxy.Attoparsec.Types: parse :: AttoparsecInput a => Parser a b -> a -> IResult a b
- Control.Proxy.Attoparsec.Types: psError :: ParserStatus a -> ParserError
- Control.Proxy.Attoparsec.Types: psLeftover :: ParserStatus a -> a
- Control.Proxy.Attoparsec.Types: psLength :: ParserStatus a -> Int
- Control.Proxy.Attoparsec.Types: type ParserSupply a = (SupplyUse, a)
+ Control.Proxy.Attoparsec: ParsingError :: [String] -> String -> ParsingError
+ Control.Proxy.Attoparsec: class Monoid a => ParserInput a
+ Control.Proxy.Attoparsec: data ParsingError
+ Control.Proxy.Attoparsec: isEndOfParserInput :: (ParserInput a, Monad m, Proxy p) => StateP [a] p () (Maybe a) y' y m Bool
+ Control.Proxy.Attoparsec: null :: ParserInput a => a -> Bool
+ Control.Proxy.Attoparsec: parse :: (ParserInput a, Monad m, Proxy p) => Parser a r -> EitherP ParsingError (StateP [a] p) () (Maybe a) y' y m r
+ Control.Proxy.Attoparsec: parseD :: (ParserInput a, Monad m, Proxy p) => Parser a b -> () -> Pipe (EitherP ParsingError (StateP [a] p)) (Maybe a) b m ()
+ Control.Proxy.Attoparsec: peContexts :: ParsingError -> [String]
+ Control.Proxy.Attoparsec: peMessage :: ParsingError -> String

Files

LICENSE view
@@ -1,5 +1,5 @@-Copyright (c) 2012-2012, Paolo Capriotti-Copyright (c) 2012-, Renzo Carbonara+Copyright (c) 2012-2013, Renzo Carbonara+Copyright (c) 2012, Paolo Capriotti  All rights reserved. 
+ NEWS view
@@ -0,0 +1,8 @@+# Version 0.2.0.0++* Droped the previous API in favour of a new and incompatible API+  that supports interleaved parsing by relying on pipes-parse.++# Version 0.1.0.1++* First version mentioned in NEWS file.
+ PEOPLE view
@@ -0,0 +1,6 @@+The following people have participated in creating this library, either+by directly contributing code or by providing thoughtful input in+discussions about the library design.++Renzo Carbonara+Gabriel Gonzalez
README.md view
@@ -1,5 +1,12 @@ # pipes-attoparsec -Utilities to convert an Attoparsec Parser into a Pipe.+Utilities to run **Attoparsec** parser on **Pipes** input streams. -Version 0.1.\* is not backwards compatible with previous versions.+Check the source or rendered Haddocks for documentation.++This code is licensed under the terms of the so called **3-clause BSD+license**. Read the file named ``LICENSE`` found in this same directory+for details.++See the ``PEOPLE`` file to learn about the people involved in this+effort.
pipes-attoparsec.cabal view
@@ -1,29 +1,24 @@-name: pipes-attoparsec-version: 0.1.0.1-license: BSD3-license-file: LICENSE-copyright: Copyright (c) Paolo Capriotti 2012-2012,-                         Renzo Carbonara 2012--author: Paolo Capriotti, Renzo Carbonara-maintainer: renzocarbonaraλgmail.com-stability: Experimental-tested-with: GHC ==7.4.1, GHC ==7.6.1-homepage: https://github.com/k0001/pipes-attoparsec-bug-reports: https://github.com/k0001/pipes-attoparsec-category: Pipes, Parser-build-type: Simple-cabal-version: >=1.8-synopsis: Utilities to convert an Attoparsec parser into a pipe Pipe.+name:               pipes-attoparsec+version:            0.2.0.0+license:            BSD3+license-file:       LICENSE+copyright:          Copyright (c) Renzo Carbonara 2012-2013, Paolo Capriotti 2012+author:             Renzo Carbonara+maintainer:         renzocarbonaraλgmail.com+stability:          Experimental+tested-with:        GHC ==7.4.1+homepage:           https://github.com/k0001/pipes-attoparsec+bug-reports:        https://github.com/k0001/pipes-attoparsec/issues+category:           Pipes, Parser+build-type:         Simple+cabal-version:      >=1.8+synopsis:           Attoparsec and Pipes integration.+extra-source-files: README.md PEOPLE NEWS description:-  Utilities to convert an Attoparsec 'Data.Attoparsec.Types.Parser' into-  a 'Control.Proxy.Synonym.Pipe'.-  .-  See "Control.Proxy.Attoparsec.Tutorial" for an extensive introduction with-  examples.+  Utilities to run @attoparsec@ parsers on @pipes@ input streams.   .-  Version 0.1.* is not backwards compatible with previous versions.-extra-source-files:-  README.md+  See the @NEWS@ file in the source distribution to learn about any+  important changes between version.  source-repository head     type: git@@ -32,36 +27,35 @@ library   hs-source-dirs:  src   exposed-modules: Control.Proxy.Attoparsec-                   Control.Proxy.Attoparsec.Control-                   Control.Proxy.Attoparsec.Tutorial-                   Control.Proxy.Attoparsec.Types+                   Control.Proxy.Attoparsec.Internal   build-depends:-    base (==4.*),-    pipes (>=3.0),-    attoparsec (>=0.10),-    bytestring,-    text--  ghc-options:-    -Wall -fno-warn-missing-signatures+      base         (==4.*)+    , pipes        (>=3.3 && <3.4)+    , pipes-parse  (>=1.0 && <1.1)+    , attoparsec   (>=0.10 && <0.11)+    , bytestring   (>=0.9.2.1)+    , text+    , transformers (>=0.2 && <=0.4)+  ghc-options: -Wall -O2  test-suite tests   type:           exitcode-stdio-1.0   hs-source-dirs: tests   main-is:        Main.hs   other-modules:  Test.Attoparsec-                  Test.Tutorial-  ghc-options:-    -Wall -rtsopts -fno-warn-missing-signatures+  ghc-options: -Wall -rtsopts -fno-warn-missing-signatures    build-depends:-    base (== 4.*),-    pipes (>= 3.0),-    attoparsec (>= 0.10),-    text,-    pipes-attoparsec,-    QuickCheck (== 2.*),-    HUnit (== 1.*),-    test-framework (>= 0.6),-    test-framework-quickcheck2 (>= 0.2),-    test-framework-hunit (>= 0.2)+      base+    , mmorph+    , pipes+    , attoparsec+    , text+    , pipes-attoparsec+    , pipes-parse+    , QuickCheck                 (== 2.*)+    , HUnit                      (== 1.*)+    , test-framework             (>= 0.6)+    , test-framework-quickcheck2 (>= 0.2)+    , test-framework-hunit       (>= 0.2)+
src/Control/Proxy/Attoparsec.hs view
@@ -1,94 +1,117 @@--- | This module provides utilities to turn your Attoparsec @'Parser' a--- b@ into a 'Pipe' that parses @a@ values into @b@ values as they flow--- downstream.+-- | This module allows you to run Attoparsec parsers on input flowing+-- downstream through Pipes streams, possibly interleaving other stream+-- effects. ----- See "Control.Proxy.Attoparsec.Tutorial" for an extensive introduction--- with examples.+-- This module builds on top of the @pipes-parse@ package and assumes+-- you have read "Control.Proxy.Parse.Tutorial".  module Control.Proxy.Attoparsec-  ( -- * Parsing Pipe-    -- $pieces-    parserInputD-  , parserD-    -- * Modules-  , module Control.Proxy.Attoparsec.Types-  , module Control.Proxy.Attoparsec.Control+  ( -- * Parsing+    -- $parsing+    parse+  , parseD+  , isEndOfParserInput+    -- * Types+  , I.ParserInput(I.null)+  , I.ParsingError(..)   ) where -import           Control.Proxy-import           Control.Proxy.Attoparsec.Control-import           Control.Proxy.Attoparsec.Types-import           Data.Attoparsec.Types            (IResult (..), Parser)-import           Prelude                          hiding (length, null)+-------------------------------------------------------------------------------- +import           Control.Monad                     (unless)+import qualified Control.Proxy                     as P+import qualified Control.Proxy.Parse               as Pa+import qualified Control.Proxy.Attoparsec.Internal as I+import qualified Control.Proxy.Trans.Either        as P+import qualified Control.Proxy.Trans.State         as P+import           Data.Attoparsec.Types             (Parser)+import           Data.Foldable                     (mapM_)+import           Data.Function                     (fix)+import           Prelude                           hiding (mapM_) --- $pieces+--------------------------------------------------------------------------------+-- $parsing ----- A 'Pipe' that parses @a@ input flowing downstream into @b@ values is--- made of at least two smaller cooperating 'Proxy's:+-- There are two basic parsing facilities exported by this module, and choosing+-- between them is easy: If you need to interleave Attoparsec parsing with other+-- stream effects you must use 'parse', otherwise you may use the simpler+-- 'parseD'. -----  1. 'parserInputD': Prepares @a@ input received from upstream to be---     consumed by a downstream parsing 'Proxy'.+-- These proxies use the 'P.EitherP' proxy transformer to report parsing errors,+-- you might use any of the facilities exported by "Control.Proxy.Trans.Either"+-- to recover from them.+++-- | Parses one element flowing downstream. -----  2. 'parserD': Repeatedly runs a given @'Parser' a b@ on input 'a'---     from upstream, and sends 'b' values downstream.+-- * In case of parsing errors, a 'I.ParsingError' exception is thrown in+-- the 'Pe.EitherP' proxy transformer. ----- Given a @'Parser' a b@ named @myParser@, the simplest way to use--- these 'Proxy's together is:+-- * Requests more input from upstream using 'Pa.draw' when needed. ----- > myParsingPipe :: (Proxy p, Monad m, AttoparsecInput a) => Pipe a b m r--- > myParsingPipe = parserInputD >-> parserD myParser+-- * /Do not/ use this proxy if 'isEndOfParserInput' returns 'True',+-- otherwise you may get unexpected parsing errors. ----- In between these two 'Proxy's, you can place other 'Proxy's to handle--- extraordinary situations like parsing failures or limiting input--- length. "Control.Proxy.Attoparsec.Control" exports some useful--- 'Proxy's that fit this place. If you skip using any of those, the--- default behavior is that of--- 'Control.Proxy.Attoparsec.Control.skipMalformedChunks': to simply--- ignore all parsing errors and malformed input, and start parsing new--- input as soon as it's available.------ | 'Proxy' turning 'a' values flowing downstream into--- @'ParserSupply' a@ values to be consumed by a downstream parsing--- 'Proxy'.+-- Here is an example parsing loop that allows interleaving stream effects+-- together with 'parse': ----- This 'Proxy' responds with @'Resume' a@ to any 'ParserStatus' value--- received from downstream.-parserInputD :: (Monad m, Proxy p)-             => ParserStatus a -> p () a (ParserStatus a) (ParserSupply a) m r-parserInputD _ = runIdentityP . forever $ request () >>= respond . (,) Resume+-- > loop = do+-- >     eof <- liftP isEndOfParserInput+-- >     unless eof $ do+-- >         -- 1. Possibly perform some stream effects here.+-- >         -- 2. Parse one element from the stream.+-- >         exampleElement <- parse myExampleParser+-- >         -- 3. Do something with exampleElement and possibly perform+-- >         --    some more stream effects.+-- >         -- 4. Start all over again.+-- >         loop+parse+  :: (I.ParserInput a, Monad m, P.Proxy p)+  => Parser a r -- ^Attoparsec parser to run on the input stream.+  -> P.EitherP I.ParsingError (P.StateP [a] p) () (Maybe a) y' y m r+    -- ^Proxy compatible with the facilities provided by "Control.Proxy.Parse".+parse parser = do+    (er, mlo) <- P.liftP (I.parseWithMay Pa.draw parser)+    P.liftP (mapM_ Pa.unDraw mlo)+    either P.throw return er+{-# INLINABLE parse #-}  --- | 'Proxy' using the given @'Parser' a b@ to repeatedly parse pieces--- of @'ParserSupply' a@ values flowing downstream into @b@ values.+-- | Parses consecutive elements flowing downstream until end of input. ----- When more input is needed, a @'ParserStatus' a@ value reporting the--- current parsing status is sent upstream, and in exchange a--- @'ParserSupply' a@ value is expected, containing more input to be--- parsed and directives on how to use it (see 'ParserSupply'--- documentation).-parserD :: (Proxy p, Monad m, AttoparsecInput a)-        => Parser a b-        -> () -> p (ParserStatus a) (ParserSupply a) () b m r-parserD parser () = runIdentityP . forever $ ask k0 0 (Parsing 0)+-- * In case of parsing errors, a 'I.ParsingError' exception is thrown in the+-- 'Pe.EitherP' proxy transformer.+--+-- * Requests more input from upstream using 'Pa.draw' when needed.+--+-- * Empty input chunks flowing downstream will be discarded.+parseD+  :: (I.ParserInput a, Monad m, P.Proxy p)+  => Parser a b -- ^Attoparsec parser to run on the input stream.+  -> () -> P.Pipe (P.EitherP I.ParsingError (P.StateP [a] p)) (Maybe a) b m ()+    -- ^Proxy compatible with the facilities provided by "Control.Proxy.Parse".+parseD parser = \() -> loop   where-    k0 = parse parser--    requestNonEmpty status = go where-      go = do-        ps@(_, chunk) <- request status-        if null chunk then go else return ps--    ask k len status = do-      (su, chunk) <- requestNonEmpty status-      case su of-        Start  -> use (k0 chunk) 0-        Resume -> use (k  chunk) (len + length chunk)+    loop = do+        eof <- P.liftP isEndOfParserInput+        unless eof $ do+          () <- P.respond =<< parse parser+          loop+{-# INLINABLE parseD #-} -    use (Partial k)         len = ask k  len $ Parsing len-    use (Fail rest ctx msg) _   = ask k0 0   $ Failed rest (ParserError ctx msg)-    use (Done rest result)  _   = respond result >> use (k0 rest) 0+-------------------------------------------------------------------------------- +-- | Like 'Pa.isEndOfInput', except it also consumes and discards leading+-- empty 'I.ParserInput' chunks.+isEndOfParserInput+  :: (I.ParserInput a, Monad m, P.Proxy p)+  => P.StateP [a] p () (Maybe a) y' y m Bool+isEndOfParserInput = fix $ \loop -> do+    ma <- Pa.draw+    case ma of+      Just a+       | I.null a  -> loop+       | otherwise -> Pa.unDraw a >> return False+      Nothing      -> return True+{-# INLINABLE isEndOfParserInput #-} 
− src/Control/Proxy/Attoparsec/Control.hs
@@ -1,80 +0,0 @@--- | This module exports some useful 'Proxy's that act upon--- 'ParserStatus' values received from downstream.--module Control.Proxy.Attoparsec.Control-  ( skipMalformedChunks-  , retryLeftovers-  , throwParsingErrors-  , limitInputLength-  ) where--import           Control.Proxy-import           Control.Proxy.Attoparsec.Types-import qualified Control.Proxy.Trans.Either as PE-import           Prelude                    hiding (drop, null)---- | If a downstream parsing 'Proxy' reports a parser failure, skip the--- whole input /chunk/ being processed, including any left-overs, and--- start processing new input as soon as it's available.------ Useful when the input found in a single @'ParserSupply' a@ is--- supposed to be well-formed, so it can be safely skipped if it is not.-skipMalformedChunks-  :: (Monad m, Proxy p, AttoparsecInput a)-  => ParserStatus a-  -> p (ParserStatus a) (ParserSupply a) (ParserStatus a) (ParserSupply a) m r-skipMalformedChunks = runIdentityK . foreverK $ go-  where go x@(Failed _ _) = request x >>= \(_,a) -> respond (Start, a)-        go x              = request x >>= respond----- | If a downstream parsing 'Proxy' reports a parsing failure, then--- keep retrying with any left-over input, skipping individual bits each--- time. If there are no left-overs, then more input is requestsd form--- upstream.-retryLeftovers-  :: (Monad m, Proxy p, AttoparsecInput a)-  => ParserStatus a-  -> p (ParserStatus a) (ParserSupply a) (ParserStatus a) (ParserSupply a) m r-retryLeftovers = runIdentityK . foreverK $ go-  where-    go s@(Failed _ _) = retry s >>= moreSupply >>= respond-    go s              = request s >>= respond--    retry s@(Failed rest _) = do-      s' <- respond (Start, rest)-      if s' == s then return s-                 else retry s'-    retry s = return s--    moreSupply s@(Failed rest _) = do-      let rest' = drop 1 rest-      if null rest'-        then request s-        else return (Start, rest')-    moreSupply s = request s----- | If a downstream parsing 'Proxy' reports a parser failure, then--- throw a 'MalformedInput' error in the 'EitherP' proxy transformer.-throwParsingErrors-  :: (Monad m, Proxy p, AttoparsecInput a)-  => ParserStatus a-  -> PE.EitherP BadInput p (ParserStatus a) (ParserSupply a) (ParserStatus a) (ParserSupply a) m r-throwParsingErrors = foreverK go-  where go (Failed _ e) = PE.throw $ MalformedInput e-        go x            = request x >>= respond----- | If a downstream parsing 'Proxy' doesn't produce a value after--- having consumed input of at least the given length, then throw an--- 'InputTooLong' error in the 'EitherP' proxy transformer.-limitInputLength-  :: (Monad m, Proxy p, AttoparsecInput a)-  => Int-  -> ParserStatus a-  -> PE.EitherP BadInput p (ParserStatus a) (ParserSupply a) (ParserStatus a) (ParserSupply a) m r-limitInputLength n = foreverK go-  where go (Parsing m) | m >= n = PE.throw $ InputTooLong m-        go x                    = request x >>= respond-
+ src/Control/Proxy/Attoparsec/Internal.hs view
@@ -0,0 +1,108 @@+{-# LANGUAGE DeriveDataTypeable #-}+{-# OPTIONS_HADDOCK hide prune #-}++-- | This module provides low-level integration with Attoparsec and is likely+-- to be modified in backwards-incompatible ways in the future.+--+-- Use the stable API exported by the "Control.Proxy.Attoparsec" module instead.++module Control.Proxy.Attoparsec.Internal+  ( -- * Types+    ParsingError(..)+  , ParserInput(..)+    -- * Parsing+  , parseWith+  , parseWithMay+    -- * Utils+  , mayInput+  ) where++--------------------------------------------------------------------------------++import           Control.Exception                 (Exception)+import           Data.Attoparsec.Types             (Parser, IResult(..))+import qualified Data.Attoparsec.ByteString        as AB+import qualified Data.Attoparsec.Text              as AT+import qualified Data.ByteString.Char8             as B+import           Data.Data                         (Data, Typeable)+import           Data.Monoid                       (Monoid(mempty))+import qualified Data.Text                         as T+import           Prelude                           hiding (null)++--------------------------------------------------------------------------------++-- | A parsing error report, as provided by Attoparsec's 'Fail'.+data ParsingError = ParsingError+  { peContexts :: [String]  -- ^ Contexts where the parsing error occurred.+  , peMessage  :: String    -- ^ Parsing error description message.+  } deriving (Show, Eq, Data, Typeable)++instance Exception ParsingError where++--------------------------------------------------------------------------------++-- | A class for valid Attoparsec input types: strict 'T.Text' and+-- strict 'B.ByteString'.+class (Monoid a) => ParserInput a where+    -- | Run a 'Parser' with input @a@.+    parse :: Parser a b -> a -> IResult a b+    -- | Tests whether @a@ is empty.+    null :: a -> Bool++instance ParserInput B.ByteString where+    parse      = AB.parse+    null       = B.null++instance ParserInput T.Text where+    parse      = AT.parse+    null       = T.null++--------------------------------------------------------------------------------++-- | Run a parser drawing input from the given monadic action as needed.+parseWith+  :: (Monad m, ParserInput a)+  => m a+  -- ^An action that will be executed to provide the parser with more input+  -- as needed. If the action returns 'mempty', then it's assumed no more+  -- input is available.+  -> Parser a r+  -- ^Parser to run on the given input+  -> m (Either ParsingError r, Maybe a)+  -- ^Either a parser error or a parsed result, together with any leftover.+parseWith refill p = step . parse p =<< refill where+    step (Partial k)  = step . k =<< refill+    step (Done t r)   = return (Right r, mayInput t)+    step (Fail t c m) = return (Left (ParsingError c m), mayInput t)+{-# INLINABLE parseWith #-}+++-- | Run a parser drawing input from the given monadic action as needed.+parseWithMay+  :: (Monad m, ParserInput a)+  => m (Maybe a)+  -- ^An action that will be executed to provide the parser with more input+  -- as needed. If the action returns 'Nothing', then it's assumed no more+  -- input is available. @'Just' 'mempty'@ input is discarded.+  -> Parser a r+  -- ^Parser to run on the given input+  -> m (Either ParsingError r, Maybe a)+  -- ^Either a parser error or a parsed result, together with any leftover.+parseWithMay refill p = parseWith loop p+  where+    loop = do+        ma <- refill+        case ma of+          Just a+           | null a    -> loop  -- retry on null input+           | otherwise -> return a+          Nothing      -> return mempty+{-# INLINABLE parseWithMay #-}+++-- | Wrap @a@ in 'Just' if not-null. Otherwise, 'Nothing'.+mayInput :: ParserInput a => a -> Maybe a+mayInput = \x -> if null x then Nothing else Just x+{-# INLINABLE mayInput #-}++
− src/Control/Proxy/Attoparsec/Tutorial.lhs
@@ -1,344 +0,0 @@-> {-# LANGUAGE OverloadedStrings #-}--| In this tutorial you will learn how to use this library. The /Simple example/-section should be enough to get you going, but you can keep reading if you want-to better understand how to deal with complex parsing scenarios.--You may import this module and try the subsequent examples as you go.---> module Control.Proxy.Attoparsec.Tutorial->   (-- * Simple example->    -- $example-simple->->    -- * Parsing control proxies->    -- ** Handling parser errors->    -- $example-errors->->    -- ** Composing->    -- $example-compose-control->->    -- ** Custom behavior->    -- $example-control-custom->->    -- * Try for yourself->    -- $example-try->     Name(..)->   , hello->   , input1->   , input2->   , helloPipe1->   , helloPipe2->   , helloPipe3->   , helloPipe4->   , helloPipe5->   , helloPipe6->   , skipPartialResults->   ) where->-> import Control.Proxy-> import Control.Proxy.Attoparsec-> import Control.Proxy.Trans.Either-> import Data.Attoparsec.Text-> import Data.Text->-> data Name = Name Text->           deriving (Show, Eq)->-> hello :: Parser Name-> hello = fmap Name $ "Hello " .*> takeWhile1 (/='.') <*. "."->-> input1 :: [Text]-> input1 =->   [ "Hello Kate."->   , "Hello Mary.Hello Jef"->   , "f."->   , "Hel"->   , "lo Tom."->   ]->-> input2 :: [Text]-> input2 =->   [ "Hello Amy."->   , "Hello, Hello Tim."->   , "Hello Bob."->   , "Hello James"->   , "Hello"->   , "Hello World."->   , "HexHello Jon."->   , "H"->   , "ello Ann"->   , "."->   , "Hello Jean-Luc."->   ]->-> helloPipe1 :: (Proxy p, Monad m) => () -> Pipe p Text Name m r-> helloPipe1 = parserInputD >-> parserD hello->-> helloPipe2 :: (Proxy p, Monad m) => () -> Pipe p Text Name m r-> helloPipe2 = parserInputD >-> retryLeftovers >-> parserD hello->-> helloPipe3 :: (Proxy p, Monad m) => () -> Pipe (EitherP BadInput p) Text Name m r-> helloPipe3 = parserInputD >-> throwParsingErrors >-> parserD hello->-> helloPipe4 :: (Proxy p, Monad m) => () -> Pipe (EitherP BadInput p) Text Name m r-> helloPipe4 = parserInputD >-> limitInputLength 10 >-> parserD hello->-> helloPipe5 :: (Proxy p, Monad m) => () -> Pipe (EitherP BadInput p) Text Name m r-> helloPipe5 = parserInputD >-> limitInputLength 10 >-> retryLeftovers >-> parserD hello->-> helloPipe6 :: (Proxy p, Monad m) => () -> Pipe p Text Name m r-> helloPipe6 = parserInputD >-> skipPartialResults >-> parserD hello->-> skipPartialResults->  :: (Monad m, Proxy p, AttoparsecInput a)->  => ParserStatus a->  -> p (ParserStatus a) (ParserSupply a) (ParserStatus a) (ParserSupply a) m r-> skipPartialResults = runIdentityK . foreverK $ go->   where go x@(Parsing _) = request x >>= \(_, a) -> respond (Start, a)->         go x             = request x >>= respond---$example-simple--We'll write a simple 'Parser' that turns 'Text' like /“Hello John Doe.”/-into @'Name' \"John Doe\"@, and then make a 'Pipe' that turns-those 'Text' values flowing downstream into 'Name' values flowing-downstream using that 'Parser'.--In this example we are using 'Text', but we may as well use-'Data.ByteString.ByteString'. Also, the 'OverloadedStrings' language-extension lets us write our parser easily.--  > {-# LANGUAGE OverloadedStrings #-}-  >-  > import Control.Proxy-  > import Control.Proxy.Attoparsec-  > import Control.Proxy.Trans.Either-  > import Data.Attoparsec.Text-  > import Data.Text-  >-  > data Name = Name Text-  >           deriving (Show)-  >-  > hello :: Parser Name-  > hello = fmap Name $ "Hello " .*> takeWhile1 (/='.') <*. "."--We are done with our parser, now lets make a simple parsing 'Pipe' with it.--  > helloPipe1 :: (Proxy p, Monad m) => () -> Pipe p Text Name m r-  > helloPipe1 = parserInputD >-> parserD hello--As the type indicates, this 'Pipe' receives 'Text' values from upstream and-sends 'Name' values downstream. This 'Pipe' is made of two smaller-two smaller cooperating 'Proxy's:--  1. 'parserInputD': Prepares @a@ input received from upstream to be-     consumed by a downstream parsing 'Proxy'.--  2. 'parserD': Repeatedly runs a given @'Parser' a b@ on input 'a' from-     upstream, and sends 'b' values downstream.--We need some sample input to test our simple 'helloPipe1'.--  > input1 :: [Text]-  > input1 =-  >   [ "Hello Kate."-  >   , "Hello Mary.Hello Jef"-  >   , "f."-  >   , "Hel"-  >   , "lo Tom."-  >   ]--We'll use @'fromListS' input1@ as our input source, which sends-downstream one element from the list at a time. We'll call each of these-elements a /chunk/. So, @'fromListS' input1@ sends 5 /chunks/ of-'Text' downstream.--Notice how some of our /chunks/ are not, by themselves, complete inputs-for our 'hello' 'Parser'. This is fine; we want to be able to feed the-'Parser' with either partial or complete input as soon as it's-received from upstream. More input will be requested when needed.-Attoparsec's 'Parser' handles partial parsing just fine.--  >>> runProxy $  fromListS input1 >-> helloPipe1 >-> printD-  Name "Kate"-  Name "Mary"-  Name "Jeff"-  Name "Tom"--We have accomplished our simple goal: We've made a 'Pipe' that parses-downstream flowing input using our 'Parser' 'hello'.---$example-errors--Let's try with some more complex input.--  > input2 :: [Text]-  > input2 =-  >   [ "Hello Amy."-  >   , "Hello, Hello Tim."-  >   , "Hello Bob."-  >   , "Hello James"-  >   , "Hello"-  >   , "Hello World."-  >   , "HexHello Jon."-  >   , "H"-  >   , "ello Ann"-  >   , "."-  >   , "Hello Jean-Luc."-  >   ]--  >>> runProxy $ fromListS input2 >-> helloPipe1 >-> printD-  Name "Amy"-  Name "Bob"-  Name "JamesHelloHello World"-  Name "Ann"-  Name "Jean-Luc"--The simple @helloPipe1@ we built skips /chunks/ of input that fail to be-parsed, and then continues parsing new input. That approach might be-enough if you are certain your input is always well-formed, but-sometimes you may prefer to act differently on these extraordinary-situations.--Instead of just using 'parserInputD' and 'parserD' to build our-'helloPipe1', we could have used an additional 'Proxy' in between them to-handle these situations. The module "Control.Proxy.Attoparsec.Control"-exports some useful 'Proxy's that serve this purpose. The default-behavior just mentioned resembles the one provided by-'skipMalformedChunks'.--Here are some other examples:--['retryLeftovers']-   On parsing failures, keep retrying with any left-over input, skipping-   individual bits each time. If there are no left-overs, then more-   input is requests form upstream.--  > helloPipe2 :: (Proxy p, Monad m) => () -> Pipe p Text Name m r-  > helloPipe2 = parserInputD >-> retryLeftovers >-> parserD hello--  >>> runProxy $ fromListS input2 >-> helloPipe2 >-> printD-  Name "Amy"-  Name "Tim"-  Name "Bob"-  Name "JamesHelloHello World"-  Name "Jon"-  Name "Ann"-  Name "Jean-Luc"--['throwParsingErrors']-  When a parsing error arises, aborts execution by throwing-  'MalformedInput' in the 'EitherP' proxy transformer.--  > helloPipe3 :: (Proxy p, Monad m) => () -> Pipe (EitherP BadInput p) Text Name m r-  > helloPipe3 = parserInputD >-> throwParsingErrors >-> parserD hello--  >>> runProxy . runEitherK $ fromListS input2 >-> helloPipe3 >-> printD-  Name "Amy"-  Left (MalformedInput {miParserErrror = ParserError {errorContexts = [], errorMessage = "Failed reading: takeWith"}})--[@'limitInputLength' n@]-  If a @'Parser' a b@ has consumed input @a@ of length longer than-  @n@ without producing a @b@ value, and it's still requesting more-  input, then throw 'InputTooLong' in the 'EitherP' proxy transformer.--  > helloPipe4 :: (Proxy p, Monad m) => () -> Pipe (EitherP BadInput p) Text Name m r-  > helloPipe4 = parserInputD >-> limitInputLength 10 >-> parserD hello--  >>> runProxy . runEitherK $ fromListS input2 >-> helloPipe4 >-> printD-  Name "Amy"-  Name "Bob"-  Left (InputTooLong {itlLenght = 11})--  Notice that by default, as mentioned earlier, parsing errors are-  ignored by skipping the malformed /chunk/. That's why we didn't get any-  complaint about the malformed input between /“Amy”/ and /“Bob”/.---$example-compose-control--These 'Proxy's that control the parsing behavior can be easily plugged-together with @('>->')@ to achieve a combined functionality. Keep in-mind that the order in which these 'Proxy's are used is important.--Suppose you don't want to parse inputs of length longer than 10, and on-parsing failures, you want to retry feeding the parser with any-left-overs. You could achieve that behaviour like this:--  > helloPipe5 :: (Proxy p, Monad m) => () -> Pipe (EitherP BadInput p) Text Name m r-  > helloPipe5 = parserInputD >-> limitInputLength 10 >-> retryLeftovers >-> parserD hello--  >>> runProxy . runEitherK $ fromListS input2 >-> helloPipe5 >-> printD-  Name "Amy"-  Name "Tim"-  Name "Bob"-  Left (InputTooLong {itlLenght = 11})---$example-control-custom--In case the 'Proxy's provided by "Control.Proxy.Attoparsec.Control" are not-enough for your needs, you can create your custom parsing control 'Proxy'.--Through a parsing control 'Proxy',  @'ParserStatus' a@ values flow-upstream and @'ParserSupply' a@ values flow downstream.  A parsing-control 'Proxy' may use, replace, or alter the flow of these values to-achieve its purpose. A @'ParserStatus' a@ value received from downstream-reports the status of a 'parserD' parsing 'Proxy', and in exchange,-downstream expects a @'ParserSupply' a@ value.--@'ParserSupply' a@ values carry raw input to be parsed and directives on how it-should be used. @'ParserSupply' a@ is just a type synonym for-@('SupplyUse', a)@. The @a@ value is the input chunk, and the 'SupplyUse' value-could be either 'Resume' if the parser currently waiting for input should be-fed, or 'Start', if a new parser should be started and fed the input,-effectively aborting any parsing activity currently waiting for more input.--See the documentation about 'ParserStatus' and 'ParserSupply' for more details.--Suppose you want to write a parsing control 'Proxy' that never provides-additional input to partial parsing results. Let's first take a look at the-type of this 'Proxy':--  > skipPartialResults-  >  :: (Monad m, Proxy p, AttoparsecInput a)-  >  => ParserStatus a-  >  -> p (ParserStatus a) (ParserSupply a) (ParserStatus a) (ParserSupply a) m r--Just like we said, @'ParserStatus' a@ values flow upstream and @'ParserSupply'-a@ values flow downstream.--Now to a simple implementation: If we receive @'Parsing' n@ from downstream,-then we know there is a partial parsing result waiting for more input. If we-were to respond to this request with @('Resume', a)@, then the partial parser-would continue consuming input, but if we change our response to @('Start', a)@,-then the partial parser would be aborted and a new parser would start-consuming the given input. A simple implementation is quite straightforward:--  > skipPartialResults = runIdentityK . foreverK $ go-  >   where go x@(Parsing _) = request x >>= \(_, a) -> respond (Start, a)-  >         go x             = request x >>= respond--We forward upstream the requests we get from downstream. However, in the case-of a @'Parsing' n@ status, we replace with 'Start' the first value in the-@'ParserSupply' a@ pair we receive from upstream before responding.--Now we can use this parsing control 'Proxy' with some simple input and see it-working.--  > helloPipe6 :: (Proxy p, Monad m) => () -> Pipe p Text Name m r-  > helloPipe6 = parserInputD >-> skipPartialResults >-> parserD hello--  >>> runProxy $ fromListS input1 >-> helloPipe6 >-> printD-  Name "Kate"-  Name "Mary"---$example-try--This module exports the following previous examples so that you can try-them.-
− src/Control/Proxy/Attoparsec/Types.hs
@@ -1,101 +0,0 @@-{-# LANGUAGE DeriveFunctor  #-}-{-# LANGUAGE KindSignatures #-}---- | This module exports common types used throughout--- @"Control.Proxy.Attoparsec".*@--module Control.Proxy.Attoparsec.Types-  ( -- * Proxy control-    ParserStatus(..)-  , SupplyUse(..)-  , ParserSupply-    -- * Error types-  , BadInput(..)-    -- * Attoparsec integration-  , AttoparsecInput(..)-  , ParserError(..)-  ) where--import qualified Data.Attoparsec.ByteString as ABS-import qualified Data.Attoparsec.Text       as AT-import           Data.Attoparsec.Types-import qualified Data.ByteString            as BS-import qualified Data.Text                  as T------ | Status of a parsing 'Proxy'.-data ParserStatus a-  -- | There is a 'Parser' running and waiting for more @a@ input.-  ---  -- Receiving @('Resume', a)@ from upstream would feed input @a@ to the 'Parser'-  -- waiting for more input.-  = Parsing-    { psLength :: Int -- ^Length of input consumed so far.-    }-  -- | A 'Parser' has failed parsing.-  ---  -- Receiving @('Resume', a)@ from upstream would feed input @a@ to a newly-  -- started 'Parser'.-  | Failed-    { psLeftover :: a -- ^Input not yet consumed when the failure occurred.-    , psError    :: ParserError -- ^Error found while parsing.-    }-  deriving (Show, Eq)---data ParserError = ParserError-    { errorContexts :: [String]  -- ^ Contexts where the error occurred.-    , errorMessage  :: String    -- ^ Error message.-    } deriving (Show, Eq)----- | Indicates how should a parsing 'Proxy' use new input.-data SupplyUse-  -- | Start a new parser and fed any new input to it.-  = Start-  -- | Resume feeding any new input to the current parser waiting for input.-  | Resume-  deriving (Eq, Show)----- | Input supplied to a parsing 'Proxy'.------ The 'SupplyUse' value indicates how the @a@ input chunk should be used.-type ParserSupply a = (SupplyUse, a)----- | Reasons for an input value to be unnaceptable.-data BadInput-  = InputTooLong-    { itlLength :: Int -- ^ Length of the input.-    }-  | MalformedInput-    { miParserErrror :: ParserError -- ^ Error found while parsing.-    }-  deriving (Show, Eq)----- | A class for valid Attoparsec input types.-class Eq a => AttoparsecInput a where-    -- | Run a 'Parser' with input @a@.-    parse :: Parser a b -> a -> IResult a b-    -- | Tests whether @a@ is empty.-    null :: a -> Bool-    -- | Skips the first @n@ elements from @a@ and returns the rest.-    drop :: Int -> a -> a-    -- | Number of elements in @a@.-    length :: a -> Int--instance AttoparsecInput BS.ByteString where-    parse = ABS.parse-    null = BS.null-    drop = BS.drop-    length = BS.length--instance AttoparsecInput T.Text where-    parse = AT.parse-    null = T.null-    drop = T.drop-    length = T.length-
tests/Main.hs view
@@ -1,19 +1,17 @@-module Main where+module Main (main) where  import Test.Framework (defaultMain, testGroup) import Test.Framework.Providers.QuickCheck2 (testProperty) import Test.Framework.Providers.HUnit (testCase) import Test.HUnit -import qualified Test.Tutorial import qualified Test.Attoparsec  main = defaultMain tests  tests =-    [ testGroup "Sample"   sampleTests-    , testGroup "Tutorial" Test.Tutorial.tests-    , testGroup "Attoparsec" Test.Attoparsec.tests+    [ testGroup "Sample."   sampleTests+    , testGroup "Attoparsec." Test.Attoparsec.tests     ]  sampleTests = [ testProperty "QuickCheck" $ \x -> const True (x :: Int) == True
tests/Test/Attoparsec.hs view
@@ -1,7 +1,67 @@ {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE NoMonomorphismRestriction #-}  module Test.Attoparsec (tests) where --- TODO+import           Control.Monad+import           Control.Proxy                  ((>->))+import qualified Control.Proxy                  as P+import qualified Control.Proxy.Trans.State      as P+import qualified Control.Proxy.Trans.Either     as P+import qualified Control.Proxy.Trans.Writer     as P+import qualified Control.Proxy.Parse            as Pa+import           Control.Proxy.Attoparsec       (parseD)+import qualified Data.Attoparsec.Text           as AT+import qualified Data.Text                      as T+import           Data.Maybe+import           Test.Framework.Providers.HUnit (testCase)+import           Test.HUnit                     (Assertion, assert) -tests = []+-- | Parses a 'Char' repeated four times.+four :: AT.Parser Char+four = do+   c <- AT.anyChar+   replicateM_ 3 $ AT.char c+   return c++type ParseTest = (Bool, String, [T.Text], [Char], [T.Text])+++assertFoursTest :: ParseTest -> Assertion+assertFoursTest (ok, _title, input, output, mlo) = assert . fromJust $ do+  let srcS = (Pa.wrap.) $ P.fromListS input+      run = P.runProxy . P.runWriterK . P.runStateK Pa.mempty . P.runEitherK+  ((epe,mlo'), res) <- run $ srcS >-> parseD four+                                  >-> P.liftP . P.liftP . P.toListD+  let okMlo = mlo' == mlo+      okErr = either (const $ not ok) (const $ ok) epe+      okRes = res == output+  return $ okMlo && okErr && okRes++foursTests :: [ParseTest]+foursTests =+  [ (True  ,"0 chunk"                   ,[]                 ,[]        ,[])+  , (True  ,"1 chunk: Empty"            ,[]                 ,[]        ,[])+  , (True  ,"1 chunk: One"              ,["aaaa"]           ,['a']     ,[])+  , (True  ,"1 chunk: One twice"        ,["aaaaaaaa"]       ,['a','a'] ,[])+  , (True  ,"1 chunk: Two"              ,["aaaabbbb"]       ,['a','b'] ,[])+  , (True  ,"1 chunk: Two between null" ,["aaaa","","bbbb"] ,['a','b'] ,[])+  , (False ,"1 chunk: Partial"          ,["aaaab"]          ,['a']     ,[])+  , (False ,"1 chunk: Wrong"            ,["aaxbb"]          ,[]        ,["xbb"])+  , (False ,"1 chunk: One then wrong"   ,["aaaavz"]         ,['a']     ,["z"])+  , (True  ,"2 chunk: Empty"            ,["",""]            ,[]        ,[])+  , (True  ,"2 chunk: Empty then one"   ,["","aaaa"]        ,['a']     ,[])+  , (True  ,"2 chunk: One"              ,["a","aaa"]        ,['a']     ,[])+  , (True  ,"2 chunk: One'"             ,["aa","aa"]        ,['a']     ,[])+  , (True  ,"2 chunk: One''"            ,["aaa","a"]        ,['a']     ,[])+  , (True  ,"2 chunk: One'''"           ,["aaaa",""]        ,['a']     ,[])+  , (True  ,"2 chunk: Two"              ,["aaaa","bbbb"]    ,['a','b'] ,[])+  , (False ,"2 chunk: Wrong"            ,["abcd","efgh"]    ,[]        ,["bcd"])+  , (False ,"2 chunk: One then wrong"   ,["aaaab","bxz"]    ,['a']     ,["xz"])+  , (True  ,"3 chunk: One"              ,["a","a","aa"]     ,['a']     ,[])+  ]++testCaseFoursTest ft@(_,name,_,_,_) =+  testCase ("Fours." ++ name) $ assertFoursTest ft++tests = map testCaseFoursTest foursTests
− tests/Test/Tutorial.hs
@@ -1,66 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}--module Test.Tutorial where--import           Control.Proxy-import           Control.Proxy.Attoparsec-import           Control.Proxy.Attoparsec.Tutorial-import           Control.Proxy.Trans.Either-import           Data.Maybe-import           Test.Framework.Providers.HUnit    (testCase)-import           Test.HUnit                        hiding (test)---testPipe pipe input test = fromJust $ do-  let proxy = fromListS input >-> pipe >-> toListD-  output <- runWriterT . runProxy . runEitherK $ proxy-  return . assert $ test output---test_helloPipe1_input1 = testPipe helloPipe1 input1 test-  where test (Right _, x) = x == fmap Name ["Kate" , "Mary", "Jeff", "Tom"]-        test _            = False--test_helloPipe1_input2 = testPipe helloPipe1 input2 test-  where test (Right _, x) = x == fmap Name [ "Amy", "Bob"-                                           , "JamesHelloHello World" , "Ann"-                                           , "Jean-Luc" ]-        test _            = False---test_helloPipe2_input2 = testPipe helloPipe2 input2 test-   where test (Right _, x) = x == fmap Name [ "Amy" ,"Tim", "Bob"-                                            , "JamesHelloHello World", "Jon"-                                            , "Ann" , "Jean-Luc" ]-         test _            = False--test_helloPipe3_input2 = testPipe helloPipe3 input2 test-   where test (Left (MalformedInput _), [Name "Amy"]) = True-         test _                                       = False---test_helloPipe4_input2 = testPipe helloPipe4 input2 test-   where test (Left (InputTooLong 11), [Name "Amy", Name "Bob"]) = True-         test _                                                  = False---test_helloPipe5_input2 = testPipe helloPipe5 input2 test-   where test (Left (InputTooLong 11),-               [Name "Amy", Name "Tim", Name "Bob"]) = True-         test _                                      = False--test_helloPipe6_input1 = testPipe helloPipe6 input1 test-   where test (Right _, [Name "Kate", Name "Mary"]) = True-         test _                                     = False---tests =-    [ testCase "helloPipe1 + input1" test_helloPipe1_input1-    , testCase "helloPipe1 + input2" test_helloPipe1_input2-    , testCase "helloPipe2 + input2" test_helloPipe2_input2-    , testCase "helloPipe3 + input2" test_helloPipe3_input2-    , testCase "helloPipe4 + input2" test_helloPipe4_input2-    , testCase "helloPipe5 + input2" test_helloPipe5_input2-    , testCase "helloPipe6 + input1" test_helloPipe6_input1-    ]-