pipes-parse (empty) → 1.0.0
raw patch · 5 files changed
+838/−0 lines, 5 filesdep +basedep +pipessetup-changed
Dependencies added: base, pipes
Files
- Control/Proxy/Parse.hs +311/−0
- Control/Proxy/Parse/Tutorial.hs +463/−0
- LICENSE +24/−0
- Setup.hs +2/−0
- pipes-parse.cabal +38/−0
+ Control/Proxy/Parse.hs view
@@ -0,0 +1,311 @@+-- | Parsing utilities for pipes++module Control.Proxy.Parse (+ -- * Pushback and Leftovers+ -- $pushback+ draw,+ unDraw,++ -- * Utilities+ peek,+ isEndOfInput,+ drawAll,+ skipAll,+ passUpTo,+ passWhile,++ -- * Adapters+ -- $adapters+ wrap,+ unwrap,+ fmapPull,+ returnPull,+ bindPull,++ -- * Lenses+ -- $lenses+ zoom,+ _fst,+ _snd,++ -- * Re-exports+ -- $reexports+ module Control.Proxy.Trans.State,+ module Data.Monoid+ ) where++import Control.Monad (forever)+import Control.Proxy ((>->), (\>\), (//>), (>\\), (?>=))+import qualified Control.Proxy as P+import Control.Proxy.Trans.State (+ StateP(StateP, unStateP),+ state,+ stateT,+ runStateP,+ runStateK,+ evalStateP,+ evalStateK,+ execStateP,+ execStateK,+ get,+ put,+ modify,+ gets )+import Data.Monoid (Monoid(mempty, mappend))++{- $pushback+ 'unDraw' stores all leftovers in a 'StateP' buffer and 'draw' retrieves+ leftovers from this buffer before drawing new input from upstream.+-}++{-| Like @request ()@, except try to use the leftovers buffer first++ A 'Nothing' return value indicates end of input.+-}+draw :: (Monad m, P.Proxy p) => StateP [a] p () (Maybe a) y' y m (Maybe a)+draw = do+ s <- get+ case s of+ [] -> P.request ()+ a:as -> do+ put as+ return (Just a)+{-# INLINABLE draw #-}++-- | Push an element back onto the leftovers buffer+unDraw :: (Monad m, P.Proxy p) => a -> StateP [a] p x' x y' y m ()+unDraw a = modify (a:)+{-# INLINABLE unDraw #-}++-- | Peek at the next element without consuming it+peek :: (Monad m, P.Proxy p) => StateP [a] p () (Maybe a) y' y m (Maybe a)+peek = do+ ma <- draw+ case ma of+ Nothing -> return ()+ Just a -> unDraw a+ return ma+{-# INLINABLE peek #-}++-- | Check if at end of input stream.+isEndOfInput :: (Monad m, P.Proxy p) => StateP [a] p () (Maybe a) y' y m Bool+isEndOfInput = do+ ma <- peek+ case ma of+ Nothing -> return True+ Just _ -> return False+{-# INLINABLE isEndOfInput #-}++{-| Fold all input into a list++ Note: 'drawAll' is usually an anti-pattern.+-}+drawAll :: (Monad m, P.Proxy p) => () -> StateP [a] p () (Maybe a) y' y m [a]+drawAll = \() -> go id+ where+ go diffAs = do+ ma <- draw+ case ma of+ Nothing -> return (diffAs [])+ Just a -> go (diffAs . (a:))+{-# INLINABLE drawAll #-}++-- | Consume the input completely, discarding all values+skipAll :: (Monad m, P.Proxy p) => () -> StateP [a] p () (Maybe a) y' y m ()+skipAll = \() -> go+ where+ go = do+ ma <- draw+ case ma of+ Nothing -> return ()+ Just _ -> go+{-# INLINABLE skipAll #-}++-- | Forward up to the specified number of elements downstream+passUpTo+ :: (Monad m, P.Proxy p)+ => Int -> () -> P.Pipe (StateP [a] p) (Maybe a) (Maybe a) m r+passUpTo n0 = \() -> go n0+ where+ go n0 =+ if (n0 <= 0)+ then forever $ P.respond Nothing+ else do+ ma <- draw+ P.respond ma+ case ma of+ Nothing -> forever $ P.respond Nothing+ Just _ -> go (n0 - 1)+{-# INLINABLE passUpTo #-}++{-| Forward downstream as many consecutive elements satisfying a predicate as+ possible+-}+passWhile+ :: (Monad m, P.Proxy p)+ => (a -> Bool) -> () -> P.Pipe (StateP [a] p) (Maybe a) (Maybe a) m r+passWhile pred = \() -> go+ where+ go = do+ ma <- draw+ case ma of+ Nothing -> forever $ P.respond Nothing+ Just a ->+ if (pred a)+ then do+ P.respond ma+ go+ else do+ unDraw a+ forever $ P.respond Nothing+{-# INLINABLE passWhile #-}++{- $adapters+ Use 'wrap' and 'unwrap' to convert between guarded and unguarded pipes.++ 'fmapPull', 'returnPull', and 'bindPull' promote compatibility with+ existing utilities that are not 'Maybe'-aware.+-}++{-| Guard a pipe from terminating by wrapping every output in 'Just' and ending+ with a never-ending stream of 'Nothing's.+-}+wrap :: (Monad m, P.Proxy p) => p a' a b' b m r -> p a' a b' (Maybe b) m s+wrap = \p -> P.runIdentityP $ do+ P.IdentityP p //> \b -> P.respond (Just b)+ forever $ P.respond Nothing+{-# INLINABLE wrap #-}++{-| Compose 'unwrap' downstream of a guarded pipe to unwrap all 'Just's and+ terminate on the first 'Nothing'.+-}+unwrap :: (Monad m, P.Proxy p) => x -> p x (Maybe a) x a m ()+unwrap = \x -> P.runIdentityP (go x)+ where+ go x = do+ ma <- P.request x+ case ma of+ Nothing -> return ()+ Just a -> do+ x2 <- P.respond a+ go x2+{-# INLINABLE unwrap #-}++{-| Lift a 'Maybe'-oblivious pipe to a 'Maybe'-aware pipe by auto-forwarding+ all 'Nothing's.++> fmapPull f >-> fmapPull g = fmapPull (f >-> g)+>+> fmapPull pull = pull+-}+fmapPull+ :: (Monad m, P.Proxy p)+ => (x -> p x a x b m r)+ -> (x -> p x (Maybe a) x (Maybe b) m r)+fmapPull f = bindPull (f >-> returnPull)+{-# INLINABLE fmapPull #-}++-- | Wrap all values flowing downstream in 'Just'.+returnPull :: (Monad m, P.Proxy p) => x -> p x a x (Maybe a) m r+returnPull = P.mapD Just+{-# INLINABLE returnPull #-}++{-| Lift a 'Maybe'-generating pipe to a 'Maybe'-transforming pipe by+ auto-forwarding all 'Nothing's++> -- Using: f >>> g = f >-> bindPull g+>+> returnPull >>> f = f+>+> f >>> returnPull = f+>+> (f >>> g) >>> h = f >>> (g >>> h)++Or equivalently:++> returnPull >-> bindPull f = f+>+> bindPull returnPull = pull+>+> bindPull (f >-> bindPull g) = bindPull f >-> bindPull g+-}+bindPull+ :: (Monad m, P.Proxy p)+ => (x -> p x a x (Maybe b) m r)+ -> (x -> p x (Maybe a) x (Maybe b) m r)+bindPull f = P.runIdentityP . (up \>\ P.IdentityP . f)+ where+ up a' = do+ ma <- P.request a'+ case ma of+ Nothing -> do+ a'2 <- P.respond Nothing+ up a'2+ Just a -> return a+{-# INLINABLE bindPull #-}++{- $lenses+ Use 'zoom', '_fst', and '_snd' to mix pipes that have different leftover+ buffers or to isolate leftover buffers of different parsing stages.+-}++{-| 'zoom' in on a sub-state using a @Lens'@.++> zoom :: Lens' s1 s2 -> StateP s2 p a' a b' b m r -> StateP s1 p a' a b' b m r++> zoom (f . g) = zoom f . zoom g+>+> zoom id = id+-}+zoom+ :: (Monad m, P.Proxy p)+ => ((s2 -> (s2, s2)) -> (s1 -> (s2, s1)))+ -- ^ @Lens'@ s1 s2+ -> StateP s2 p a' a b' b m r+ -- ^ Local state+ -> StateP s1 p a' a b' b m r+ -- ^ Global state+zoom lens = \p -> StateP $ \s2_0 ->+ let (s1_0, s2_0') = lens (\x -> (x, x)) s2_0+ in (up >\\ P.thread_P (unStateP p s1_0) s2_0' //> dn) ?>= nx+ where+ up ((a', s1), s2) =+ let (_, s2') = lens (\x -> (x, s1)) s2+ in P.request (a', s2') ?>= \(a, s2'') ->+ let (s1', s2''') = lens (\x -> (x, x)) s2''+ in P.return_P ((a, s1'), s2''')+ dn ((b, s1), s2) =+ let (_, s2') = lens (\x -> (x, s1)) s2+ in P.respond (b, s2') ?>= \(b', s2'') ->+ let (s1', s2''') = lens (\x -> (x, x)) s2''+ in P.return_P ((b', s1'), s2''')+ nx ((r, s1), s2) =+ let (_, s2') = lens (\x -> (x, s1)) s2+ in P.return_P (r, s2')+{-# INLINABLE zoom #-}++{-| A @Lens'@ to the first element of a pair.++ Like @_1@, but more monomorphic++> _fst :: Lens' (a, b) a+-}+_fst :: (Functor f) => (a -> f b) -> ((a, x) -> f (b, x))+_fst = \f (a, x) -> fmap (\b -> (b, x)) (f a)+{-# INLINABLE _fst #-}++{-| A @Lens'@ to the second element of a pair.++ Like @_2@, but more monomorphic++> _snd :: Lens' (a, b) b+-}+_snd :: (Functor f) => (a -> f b) -> ((x, a) -> f (x, b))+_snd = \f (x, a) -> fmap (\b -> (x, b)) (f a)+{-# INLINABLE _snd #-}++{- $reexports+ "Control.Proxy.Trans.State" re-exports all functions.++ "Data.Monoid" re-exports the 'Monoid' class.+-}
+ Control/Proxy/Parse/Tutorial.hs view
@@ -0,0 +1,463 @@+{-| This module provides the tutorial for the @pipes-parse@ library++ This tutorial assumes that you have read the @pipes@ tutorial in+ @Control.Proxy.Tutorial@.+-}++module Control.Proxy.Parse.Tutorial (+ -- * Introduction+ -- $introduction++ -- * End of input+ -- $eof++ -- * Compatibility+ -- $compatibility++ -- * Pushback and leftovers+ -- $leftovers++ -- * Diverse leftovers+ -- $diverse++ -- * Isolating leftovers+ -- $mix++ -- * Return value+ -- $return++ -- * Resumable Parsing+ -- $resume++ -- * Nesting+ -- $nesting++ -- * Conclusion+ -- $conclusion+ ) where++import Control.Proxy+import Control.Proxy.Parse++{- $introduction+ @pipes-parse@ provides utilities commonly required for parsing streams using+ @pipes@:++ * End of input utilities and conventions for the @pipes@ ecosystem++ * Pushback and leftovers support for saving unused input++ * Tools to combine parsing stages with diverse or isolated leftover buffers++ * Ways to delimit parsers to subsets of streams++ Use these utilities to parse and validate streaming input in constant+ memory.+-}++{- $eof+ To guard an input stream against termination, protect it with the 'wrap'+ function:++> wrap :: (Monad m, Proxy p) => p a' a b' b m r -> p a' a b' (Maybe b) m s++ This wraps all output values in a 'Just' and then protects against+ termination by producing a never-ending stream of 'Nothing' values:++>>> -- Before+>>> runProxy $ enumFromToS 1 3 >-> printD+1+2+3+>>> -- After+>>> runProxy $ wrap . enumFromToS 1 3 >-> printD+Just 1+Just 2+Just 3+Nothing+Nothing+Nothing+Nothing+...++ You can also 'unwrap' streams:++> unwrap :: (Monad m, Proxy p) => x -> p x (Maybe a) x a m ()++ 'unwrap' behaves like the inverse of 'wrap'. Compose 'unwrap' downstream of+ a pipe to unwrap every 'Just' and terminate on the first 'Nothing':++> wrap . p >-> unwrap = p++ You will commonly use 'unwrap' to terminate an infinite stream:++>>> runProxy $ wrap . enumFromToS 1 3 >-> printD >-> unwrap+Just 1+Just 2+Just 3+Nothing++-}++{- $compatibility+ What if we want to ignore the 'Maybe' machinery entirely and interact with+ the original unwrapped stream? We can use 'fmapPull' to lift existing+ proxies to ignore all 'Nothing's and only operate on the 'Just's:++> fmapPull+> :: (Monad m, Proxy p)+> => (x -> p x a x b m r)+> -> (x -> p x (Maybe a) x (Maybe b) m r)++ We can use this to lift 'printD' to operate on the original stream:++>>> runProxy $ wrap . enumFromToS 1 3 >-> fmapPull printD >-> unwrap+1+2+3++ This lifting cleanly distributes over composition and obeys the following+ laws:++> fmapPull (f >-> g) = fmapPull f >-> fmapPull g+>+> fmapPull pull = pull++ You can navigate even more complicated mixtures of 'Maybe'-aware and+ 'Maybe'-oblivious code using 'bindPull' and 'returnPull'.++ @pipes-parse@ requires no buy-in from the rest of the @pipes@ ecosystem+ thanks to these adapter routines that automatically lift existing pipes to+ interoperate with end-of-input protocols.+-}++{- $leftovers+ To take advantage of leftovers support, just replace your 'request's with+ 'draw':++> draw :: (Monad m, Proxy p) => StateP [a] p () (Maybe a) y' y m (Maybe a)++ ... and use 'unDraw' to push back leftovers:++> unDraw :: (Monad m, Proxy p) => a -> StateP [a] p x' x y' y m ()++ These both use a last-in-first-out (LIFO) leftovers buffer of type @[a]@+ stored in a 'StateP' layer. 'unDraw' prepends elements to this list of+ leftovers and 'draw' will consume elements from the head of the leftovers+ list until it is empty before requesting new input from upstream:++> consumer :: (Proxy p) => () -> Consumer (StateP [a] p) (Maybe Int) IO ()+> consumer () = do+> ma <- draw+> lift $ print ma+> -- You can push back values you never drew+> unDraw 99+> -- You can push back more than one value at a time+> case ma of+> Nothing -> return ()+> -- The leftovers buffer only stores unwrapped values+> Just a -> unDraw a+> -- Values come out of the buffer in last-in-first-out (LIFO) order+> replicateM_ 2 $ do+> ma <- draw+> lift $ print ma++ To run the 'StateP' layer, just provide an empty initial state using+ 'mempty':++>>> runProxy $ evalStateK mempty $ wrap . enumFromS 1 >-> consumer+Just 1+Just 1+Just 99++-}++{- $diverse+ Why use 'mempty' instead of @[]@? @pipes-parse@ lets you easily mix+ distinct leftovers buffers into the same 'StateP' layer and 'mempty' will+ still do the correct thing when you use multiple buffers.++ For example, suppose that we need to compose parsing pipes that have+ different input types and therefore different types of leftovers buffers,+ such as the following two parsers:++> tallyLength+> :: (Monad m, Proxy p)+> => () -> Pipe (StateP [String] p) (Maybe String) (Maybe Int) m r+> tallyLength () = loop 0+> where+> loop tally = do+> respond (Just tally)+> mstr <- draw+> case mstr of+> Nothing -> forever $ respond Nothing+> Just str -> loop (tally + length str)+>+> adder+> :: (Monad m, Proxy p)+> => () -> Consumer (StateP [Int] p) (Maybe Int) m Int+> adder () = fmap sum $ drawAll ()++ We can use 'zoom' to unify these two parsers to share the same 'StateP'+ layer:++> combined+> :: (Monad m, Proxy p)+> => () -> Consumer (StateP ([String], [Int]) p) (Maybe String) m Int+> -- ^ ^+> -- | |+> -- Two leftovers buffers ---+-------++> combined = zoom _fst . tallyLength >-> zoom _snd . adder+>+> source :: (Monad m, Proxy p) => () -> Producer p String m ()+> source = fromListS ["One", "Two", "Three"]++ 'zoom' takes a @Lens'@ as an argument which specifies which subset of the+ state that each parser will use. '_fst' directs the @tallyLength@ parser to+ use the @[String]@ leftovers buffer and '_snd' directs the @adder@ parser to+ use the @[Int]@ leftovers buffer.++ Notice that we can still run the mixture of buffers by supplying 'mempty':++>>> runProxy $ evalStateK mempty $ wrap . source >-> combined+20++ This works because:++> (mempty :: ([String], [Int])) = ([], [])++ Let's study the type of 'zoom' to understand how it works:++> -- zoom's true type is slightly different to avoid a dependency on `lens`+> zoom :: Lens' s1 s2 -> StateP s2 p a' a b' b m r -> StateP s1 p a' a b' b m r++ 'zoom' behaves like the function of the same name from the @lens@ package+ and zooms in on a sub-state using the provided lens. When we give it the+ '_fst' lens we zoom in on the first element of a tuple:++> _fst :: Lens' (s1, s2) s1+>+> zoom _fst :: StateP s1 p a' a b' b m r -> StateP (s1, s2) p a' a b' b m r++ ... and when we give it the '_snd' lens we zoom in on the second element of+ a tuple:++> _snd :: Lens' (s1, s2) s2+>+> zoom _snd :: StateP s2 p a' a b' b m r -> StateP (s1, s2) p a' a b' b m r++ '_fst' and '_snd' are like '_1' and '_2' from the @lens@ package, except+ with a more monomorphic type. This ensures that type inference works+ correctly when supplying 'mempty' as the initial state.++ If you want to merge more than one leftovers buffer, you can either nest+ pairs of tuples:++> p = zoom _fst . p1 >-> zoom (_snd . _fst) . p2 >-> zoom (_snd . _snd) . p3++ ... or you can create a data type that holds all your leftovers and generate+ lenses to its fields:++> import Control.Lens hiding (zoom)+>+> data Leftovers = Leftovers+> { _buf1 :: [String]+> , _buf2 :: [Int]+> , _buf3 :: [Double]+> }+> makeLenses ''Leftovers+> -- Generates:+> -- buf1 :: Lens' Leftovers [String]+> -- buf2 :: Lens' Leftovers [Int]+> -- buf3 :: Lens' Leftovers [Double]+>+> instance Monoid Leftovers where+> mempty = Leftovers [] [] []+> mappend (Leftovers as bs cs) (Leftovers as' bs' cs')+> = Leftovers (as ++ as') (bs ++ bs') (cs ++ cs')+>+> p = zoom buf1 . p1 >-> zoom buf2 . p2 >-> zoom buf3 . p3++ 'zoom' works seamlessly with all lenses from the @lens@ package, but you+ don't need a @lens@ dependency to use @pipes-parse@.+-}++{- $mix+ 'zoom' isn't the only way to isolate buffers. Let's say that you want to+ mix the following three @pipes-parse@ utilities:++> -- Transmit up to the specified number of elements+> passUpTo+> :: (Monad m, Proxy p)+> => Int -> () -> Pipe (StateP [a] p) (Maybe a) (Maybe a) m r+>+> -- Fold all input into a list+> drawAll :: (Monad m, Proxy p) => () -> StateP [a] p () (Maybe a) y' y m [a]+>+> -- Check if at end of input stream+> isEndOfInput :: (Monad m, Proxy p) => StateP [a] p () (Maybe a) y' y m Bool++ We might expect the following code to yield chunks of three elements at a+ time:++> chunks :: (Monad m, Proxy p) => () -> Pipe (StateP [a] p) (Maybe a) [a] m ()+> chunks () = loop+> where+> loop = do+> as <- (passUpTo 3 >-> drawAll) ()+> respond as+> eof <- isEndOfInput+> unless eof loop++ ... but it doesn't:++>>> runProxy $ evalStateK mempty $ wrap . enumFromToS 1 15 >-> chunks >-> printD+[1,2,3]+[4,5,6,7]+[8,9,10,11]+[12,13,14,15]++ @chunks@ behaves strangely because 'drawAll' shares the same leftovers+ buffer as 'passUpTo' and 'isEndOfInput'. After the first chunk completes,+ 'isEndOfInput' peeks at the next value, @4@, and immediately 'unDraw's the+ value. 'drawAll' retrieves this undrawn value from the leftovers before+ consulting 'passUpTo' which is why every subsequent list contains an extra+ element.++ We often don't want composed parsing stages like 'drawAll' to share the same+ leftovers buffer as upstream stages, but we also don't want to use 'zoom' to+ add yet another permanent buffer to our global leftovers state. To solve+ this, we embed 'drawAll' within a transient 'StateP' layer using+ 'evalStateK':++> chunks () = loop+> where+> loop = do+> as <- (passUpTo 3 >-> evalStateK mempty drawAll) ()+> respond as+> eof <- isEndOfInput+> unless eof loop++ This runs 'drawAll' within a fresh temporary buffer so that it does not+ reuse the same buffer as the surrounding pipe:++>>> runProxy $ evalStateK mempty $ wrap . enumFromToS 1 15 >-> chunks >-> printD+[1,2,3]+[4,5,6]+[7,8,9]+[10,11,12]+[13,14,15]++ Conversely, remove the 'evalStateK' if you deliberately want downstream+ parsers to share the same leftovers buffers.+-}++{- $return+ 'wrap' allows you to return values directly from parsers because it produces+ a polymorphic return value:++> -- The 's' is polymorphic and will type-check as anything+> wrap :: (Monad m, Proxy p) => p a' a b' b m r -> p a' a b' (Maybe b) m s++ This means that if you compose a parser downstream the parser can return the+ result directly:++> parser+> :: (Monad m, Proxy p)+> => () -> Consumer (StateP [a] p) (Maybe a) m (Maybe a, Maybe a)+> parser () = do+> mx <- draw+> my <- draw+> return (mx, my) -- Return the result++ The polymorphic return value of 'wrap' will type-check as anything,+ including our parser's result:++> session+> :: (Monad m, Proxy p)+> => () -> Session (StateP [Int] p) m (Maybe Int, Maybe Int)+> session = wrap . enumFromToS 0 9 >-> parser++ So we can run this 'Session' and retrieve the result directly from the+ return value:++>>> runProxy $ evalStateK session+(Just 0, Just 1)++-}++{- $resume+ You can save leftovers buffers if you need to interrupt parsing for any+ reason. Just replace 'evalStateK' with 'runStateK':++>>> let session = wrap . enumFromS 0 >-> passWhile (< 3) >-> printD >-> unwrap+>>> runProxy $ runStateK mempty session+Just 0+Just 1+Just 2+Nothing+((), [3])++ This returns the leftovers buffers in the result so that you can reuse them+ later on. In the above example, 'passWhile' pushed back the @3@ input onto+ the leftovers buffer, so the result includes the unused @3@.+-}++{- $nesting+ @pipes-parse@ allows you to cleanly delimit the scope of sub-parsers by+ restricting them to a subset of the stream, as the following example+ illustrates:++> import Control.Proxy+> import Control.Proxy.Parse+>+> parser+> :: (Proxy p)+> => () -> Consumer (StateP [Int] p) (Maybe Int) IO ([Int], [Int])+> parser () = do+> lift $ putStrLn "Skip the first three elements"+> (passUpTo 3 >-> evalStateK mempty skipAll) ()+> lift $ putStrLn "Restrict subParser to consecutive elements less than 10"+> (passWhile (< 10) >-> evalStateK mempty subParser) ()+>+> subParser+> :: (Proxy p)+> => () -> Consumer (StateP [Int] p) (Maybe Int) IO ([Int], [Int])+> subParser () = do+> lift $ putStrLn "- Get the next four elements"+> xs <- (passUpTo 4 >-> evalStateK mempty drawAll) ()+> lift $ putStrLn "- Get the rest of the input"+> ys <- drawAll ()+> return (xs, ys)++ Notice how we use 'evalStateK' each time we subset a parser so that the+ sub-parser uses a fresh and transient leftovers buffer.++>>> runProxy $ evalStateK mempty $ wrap . enumFromS 0 >-> parser+Skip the first three elements+Restrict subParser to consecutive elements less than 10+- Get the next four elements+- Get the rest of the input+([3,4,5,6],[7,8,9])++-}++{- $conclusion+ @pipes-parse@ provides standardized end-of-input and leftovers utilities for+ you to use in your @pipes@-based libraries. Unlike other streaming+ libraries, you can:++ * mix or isolate leftovers buffers in a precise and type-safe way,++ * easily delimit parsers to subsets of the input, and++ * ignore standardization, thanks to compatibility functions like 'fmapPull'.++ This library is intentionally minimal and datatype-specific parsers belong+ in derived libraries. This makes @pipes-parse@ a very light-weight and+ stable dependency that you can use in your own projects.++ You can ask any questions about @pipes-parse@ and other @pipes@ libraries on+ the official @pipes@ mailing list at+ <mailto:haskell-pipes@googlegroups.com>.+-}
+ LICENSE view
@@ -0,0 +1,24 @@+Copyright (c) 2013 Gabriel Gonzalez+All rights reserved.++Redistribution and use in source and binary forms, with or without modification,+are permitted provided that the following conditions are met:+ * Redistributions of source code must retain the above copyright notice,+ this list of conditions and the following disclaimer.+ * Redistributions in binary form must reproduce the above copyright notice,+ this list of conditions and the following disclaimer in the documentation+ and/or other materials provided with the distribution.+ * Neither the name of Gabriel Gonzalez nor the names of other contributors+ may be used to endorse or promote products derived from this software+ without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ pipes-parse.cabal view
@@ -0,0 +1,38 @@+Name: pipes-parse+Version: 1.0.0+Cabal-Version: >=1.8.0.2+Build-Type: Simple+License: BSD3+License-File: LICENSE+Copyright: 2013 Gabriel Gonzalez+Author: Gabriel Gonzalez+Maintainer: Gabriel439@gmail.com+Bug-Reports: https://github.com/Gabriel439/Haskell-Pipes-Parse-Library/issues+Synopsis: Parsing infrastructure for the pipes ecosystem+Description: This package defines the generic machinery necessary for common+ parsing tasks using @pipes@:+ .+ * /End of input/: Detect and handle end of input+ .+ * /Push-back/: Save unused input for later steps+ .+ * /Lens Support/: Mix proxies with different leftover buffers using lenses+ .+ * /Compatibility/: Transparently upgrade proxies to work with @pipes-parse@+ .+ Import @Control.Proxy.Parse@ to use this library.+ .+ Read @Control.Proxy.Parse.Tutorial@ for an introductory tutorial.+Category: Control, Pipes, Proxies, Parsing+Source-Repository head+ Type: git+ Location: https://github.com/Gabriel439/Haskell-Pipes-Parse-Library++Library+ Build-Depends:+ base >= 4 && < 5 ,+ pipes >= 3.3 && < 3.4+ Exposed-Modules:+ Control.Proxy.Parse,+ Control.Proxy.Parse.Tutorial+ GHC-Options: -O2