diff --git a/Control/Proxy/Parse.hs b/Control/Proxy/Parse.hs
new file mode 100644
--- /dev/null
+++ b/Control/Proxy/Parse.hs
@@ -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.
+-}
diff --git a/Control/Proxy/Parse/Tutorial.hs b/Control/Proxy/Parse/Tutorial.hs
new file mode 100644
--- /dev/null
+++ b/Control/Proxy/Parse/Tutorial.hs
@@ -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>.
+-}
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -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.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/pipes-parse.cabal b/pipes-parse.cabal
new file mode 100644
--- /dev/null
+++ b/pipes-parse.cabal
@@ -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
