Safe Haskell | None |
---|

Element-agnostic parsing utilities for `pipes`

`pipes-parse`

provides two ways to parse and transform streams in constant
space:

- The "list-like" approach, using the split / transform / join paradigm
- The monadic approach, using parser combinators

The top half of this module provides the list-like approach, which is easier to use, but less powerful. The key idea is that:

-- '~' means "is analogous to" Producer a m () ~ [a] FreeT (Producer a m) m () ~ [[a]]

`FreeT`

nests each subsequent `Producer`

within the return value of the
previous `Producer`

so that you cannot access the next `Producer`

until you
completely drain the current `Producer`

. However, you rarely need to work
with `FreeT`

directly. Instead, you structure everything using
"splitters", "transformations" and "joiners":

-- A "splitter" Producer a m () -> FreeT (Producer a m) m () ~ [a] -> [[a]] -- A "transformation" FreeT (Producer a m) m () -> FreeT (Producer a m) m () ~ [[a]] -> [[a]] -- A "joiner" FreeT (Producer a m) m () -> Producer a m () ~ [[a]] -> [a]

For example, if you wanted to group standard input by equal lines and take the first three groups, you would write:

import Pipes import qualified Pipes.Parse as Parse import qualified Pipes.Prelude as Prelude threeGroups :: (Monad m, Eq a) => Producer a m () -> Producer a m () threeGroups = Parse.concat . Parse.takeFree 3 . Parse.groupBy (==) -- ^ Joiner ^ Transformation ^ Splitter

This then limits standard input to the first three consecutive groups of equal lines:

`>>>`

Group1<Enter> Group1 Group1<Enter> Group1 Group2<Enter> Group2 Group3<Enter> Group3 Group3<Enter> Group3 Group4<Enter>`runEffect $ threeGroups Prelude.stdinLn >-> Prelude.stdoutLn`

`>>>`

`-- Done, because we began entering our fourth group`

The advantage of this style or programming is that you never bring more than
a single element into memory. This works because `FreeT`

sub-divides the
`Producer`

without concatenating elements together, preserving the laziness
of the underlying `Producer`

.

The bottom half of this module lets you implement your own list-like transformations using monadic parsers.

For example, if you wanted to repeatedly sum every 3 elements and yield the result, you would write:

import Control.Monad (unless) import Pipes import qualified Pipes.Prelude as P import Pipes.Parse sum3 :: (Monad m, Num a) => Producer a (StateT (Producer a m ()) m) () sum3 = do eof <- lift isEndOfInput unless eof $ do n <- lift $ P.sum (input >-> P.take 3) yield n sum3

When you are done building the parser, you convert your parser to a
list-like function using `evalStateP`

:

import Pipes.Lift (evalStateP) -- sum3' ~ (Num a) => [a] -> [a] sum3' :: (Monad m, Num a) => Producer a m () -> Producer a m () sum3' p = evalStateP p sum3

... then apply it to the `Producer`

you want to transform:

`>>>`

1<Enter> 4<Enter> 5<Enter> 10 2<Enter> 0<Enter> 2`runEffect $ sum3' (P.readLn >-> P.takeWhile (/= 0)) >-> P.print`

`>>>`

- groupBy :: Monad m => (a -> a -> Bool) -> Producer a m r -> FreeT (Producer a m) m r
- chunksOf :: Monad m => Int -> Producer a m r -> FreeT (Producer a m) m r
- splitOn :: Monad m => (a -> Bool) -> Producer a m r -> FreeT (Producer a m) m r
- takeFree :: (Functor f, Monad m) => Int -> FreeT f m () -> FreeT f m ()
- dropFree :: Monad m => Int -> FreeT (Producer a m) m r -> FreeT (Producer a m) m r
- concat :: Monad m => FreeT (Producer a m) m r -> Producer a m r
- intercalate :: Monad m => Producer a m () -> FreeT (Producer a m) m r -> Producer a m r
- draw :: Monad m => StateT (Producer a m r) m (Either r a)
- unDraw :: Monad m => a -> StateT (Producer a m r) m ()
- peek :: Monad m => StateT (Producer a m r) m (Either r a)
- isEndOfInput :: Monad m => StateT (Producer a m r) m Bool
- input :: Monad m => Producer' a (StateT (Producer a m r) m) r
- takeWhile :: Monad m => (a -> Bool) -> Pipe a a (StateT (Producer a m r) m) ()
- module Control.Monad.Trans.Free

# Splitters

# Transformations

takeFree :: (Functor f, Monad m) => Int -> FreeT f m () -> FreeT f m ()Source

`(takeFree n)`

only keeps the first `n`

functor layers of a `FreeT`

dropFree :: Monad m => Int -> FreeT (Producer a m) m r -> FreeT (Producer a m) m rSource

`(dropFree n)`

peels off the first `n`

layers of a `FreeT`

Use carefully: the peeling off is not free. This runs the first `n`

layers, just discarding everything they produce.

# Joiners

# Low-level Parsers

`pipes-parse`

handles end-of-input and pushback by storing a `Producer`

in
a `StateT`

layer.

unDraw :: Monad m => a -> StateT (Producer a m r) m ()Source

Push back an element onto the underlying `Producer`

isEndOfInput :: Monad m => StateT (Producer a m r) m BoolSource

Check if the underlying `Producer`

is empty

isEndOfInput = liftM isLeft peek

# High-level Parsers

`input`

provides a `Producer`

that streams from the underlying `Producer`

.

Streaming from `input`

differs from streaming directly from the underlying
`Producer`

because any unused input is saved for later, as the following
example illustrates:

import Control.Monad.Trans.State.Strict import Pipes import Pipes.Parse import qualified Pipes.Prelude as P parser :: (Show a) => StateT (Producer a IO ()) IO () parser = do runEffect $ input >-> P.take 2 >-> P.show >-> P.stdoutLn liftIO $ putStrLn "Intermission" runEffect $ input >-> P.take 2 >-> P.show >-> P.stdoutLn

The second pipeline resumes where the first pipeline left off:

`>>>`

1 2 Intermission 3 4`evalStateT parser (each [1..])`

You can see more examples of how to use these parsing utilities by studying the source code for the above splitters.

# Utilities

# Re-exports

`Control.Monad.Trans.Free`

re-exports `FreeF`

, `FreeT`

, and `runFreeT`

.

`Control.Monad.Trans.State.Strict`

re-exports `StateT`

, `runStateT`

,
`evalStateT`

, and `execStateT`

.

module Control.Monad.Trans.Free