]> git.immae.eu Git - github/fretlink/text-pipes.git/blame - Data/Text/Pipes.hs
license nonsense
[github/fretlink/text-pipes.git] / Data / Text / Pipes.hs
CommitLineData
91727d11 1{-# LANGUAGE RankNTypes, TypeFamilies #-}
2
3{-| This module provides @pipes@ utilities for \"byte streams\", which are
4 streams of strict 'Text's chunks. Use byte streams to interact
5 with both 'IO.Handle's and lazy 'Text's.
6
7 To stream to or from 'IO.Handle's, use 'fromHandle' or 'toHandle'. For
8 example, the following program copies data from one file to another:
9
10> import Pipes
11> import qualified Pipes.Text as P
12> import System.IO
13>
14> main =
15> withFile "inFile.txt" ReadMode $ \hIn ->
16> withFile "outFile.txt" WriteMode $ \hOut ->
17> runEffect $ P.fromHandle hIn >-> P.toHandle hOut
18
19 You can stream to and from 'stdin' and 'stdout' using the predefined 'stdin'
20 and 'stdout' proxies, like in the following \"echo\" program:
21
22> main = runEffect $ P.stdin >-> P.stdout
23
24 You can also translate pure lazy 'TL.Text's to and from proxies:
25
26> import qualified Data.Text.Lazy as TL
27>
28> main = runEffect $ P.fromLazy (TL.pack "Hello, world!\n") >-> P.stdout
29
30 In addition, this module provides many functions equivalent to lazy
31 'Text' functions so that you can transform or fold byte streams. For
32 example, to stream only the first three lines of 'stdin' to 'stdout' you
33 would write:
34
35> import Pipes
36> import qualified Pipes.Text as PT
37> import qualified Pipes.Parse as PP
38>
39> main = runEffect $ takeLines 3 PB.stdin >-> PT.stdout
40> where
41> takeLines n = PB.unlines . PP.takeFree n . PT.lines
42
43 The above program will never bring more than one chunk (~ 32 KB) into
44 memory, no matter how long the lines are.
45
46 Note that functions in this library are designed to operate on streams that
47 are insensitive to chunk boundaries. This means that they may freely split
48 chunks into smaller chunks and /discard empty chunks/. However, they will
49 /never concatenate chunks/ in order to provide strict upper bounds on memory
50 usage.
51-}
52
53module Data.Text.Pipes (
54 -- * Producers
55 fromLazy,
56 stdin,
57 fromHandle,
58 readFile,
59 stdinLn,
60-- hGetSome,
61-- hGet,
62
63 -- * Servers
64-- hGetSomeN,
65-- hGetN,
66
67 -- * Consumers
68 stdout,
69 stdoutLn,
70 toHandle,
71 writeFile,
72
73 -- * Pipes
74 map,
75 concatMap,
76 take,
77 drop,
78 takeWhile,
79 dropWhile,
80 filter,
81-- elemIndices,
82-- findIndices,
83 scan,
84
85 -- * Folds
86 toLazy,
87 toLazyM,
88 fold,
89 head,
90 last,
91 null,
92 length,
93 any,
94 all,
95 maximum,
96 minimum,
97-- elem,
98-- notElem,
99 find,
100 index,
101-- elemIndex,
102-- findIndex,
103-- count,
104
105 -- * Splitters
106 splitAt,
107 chunksOf,
108 span,
109 break,
110 splitWith,
111 split,
112 groupBy,
113 group,
114 lines,
115 words,
116
117 -- * Transformations
118 intersperse,
119
120 -- * Joiners
121 intercalate,
122 unlines,
123 unwords,
124
125 -- * Low-level Parsers
126 -- $parse
127 nextByte,
128 drawByte,
129 unDrawByte,
130 peekByte,
131 isEndOfBytes,
132-- takeWhile',
133
134 -- * Re-exports
135 -- $reexports
136 module Data.Text,
137-- module Data.Word,
138 module Pipes.Parse
139 ) where
140
141import Control.Exception (throwIO, try)
142import Control.Monad (liftM, unless)
143import Control.Monad.Trans.State.Strict (StateT)
144import qualified Data.Text as T
145import qualified Data.Text.IO as T
146import Data.Text (Text)
147import qualified Data.Text.Lazy as TL
148import qualified Data.Text.Lazy.IO as TL
149import Data.Text.Lazy.Internal (foldrChunks, defaultChunkSize)
150import Data.ByteString.Unsafe (unsafeTake, unsafeDrop)
151import Data.Char (ord)
152import Data.Functor.Identity (Identity)
153import qualified Data.List as List
154import Foreign.C.Error (Errno(Errno), ePIPE)
155import qualified GHC.IO.Exception as G
156import Pipes
157import qualified Pipes.ByteString.Parse as PBP
158import Pipes.ByteString.Parse (
159 nextByte, drawByte, unDrawByte, peekByte, isEndOfBytes )
160import Pipes.Core (respond, Server')
161import qualified Pipes.Parse as PP
162import Pipes.Parse (input, concat, FreeT)
163import qualified Pipes.Safe.Prelude as Safe
164import qualified Pipes.Safe as Safe
165import Pipes.Safe (MonadSafe(..), Base(..))
166import qualified Pipes.Prelude as P
167import qualified System.IO as IO
168import Data.Char (isSpace)
169import Prelude hiding (
170 all,
171 any,
172 break,
173 concat,
174 concatMap,
175 drop,
176 dropWhile,
177 elem,
178 filter,
179 head,
180 last,
181 lines,
182 length,
183 map,
184 maximum,
185 minimum,
186 notElem,
187 null,
188 readFile,
189 span,
190 splitAt,
191 take,
192 takeWhile,
193 unlines,
194 unwords,
195 words,
196 writeFile )
197
198-- | Convert a lazy 'TL.Text' into a 'Producer' of strict 'Text's
199fromLazy :: (Monad m) => TL.Text -> Producer' Text m ()
200fromLazy = foldrChunks (\e a -> yield e >> a) (return ())
201{-# INLINABLE fromLazy #-}
202
203-- | Stream bytes from 'stdin'
204stdin :: MonadIO m => Producer' Text m ()
205stdin = fromHandle IO.stdin
206{-# INLINABLE stdin #-}
207
6f6f9974 208-- | Convert a 'IO.Handle' into a text stream using a chunk size
209-- determined by the good sense of the text library.
91727d11 210fromHandle :: MonadIO m => IO.Handle -> Producer' Text m ()
211fromHandle h = go where
212 go = do txt <- liftIO (T.hGetChunk h)
213 unless (T.null txt) $ do yield txt
214 go
215{-# INLINABLE fromHandle#-}
216
6f6f9974 217{-| Stream text from a file using Pipes.Safe
218
219>>> runSafeT $ runEffect $ readFile "README.md" >-> map toUpper >-> hoist lift stdout
220TEXT-PIPES
221==========
222TEXT PIPES, SOMEHOW TO BE FUSED WITH `PIPES-TEXT`.
223...
224>>>
225-}
226
91727d11 227readFile :: (MonadSafe m, Base m ~ IO) => FilePath -> Producer' Text m ()
228readFile file = Safe.withFile file IO.ReadMode fromHandle
229{-# INLINABLE readFile #-}
230
231stdinLn :: MonadIO m => Producer' Text m ()
232stdinLn = go
233 where
234 go = do
235 eof <- liftIO (IO.hIsEOF IO.stdin)
236 unless eof $ do
237 txt <- liftIO (T.hGetLine IO.stdin)
238 yield txt
239 go
240
241{-| Convert a handle into a byte stream using a fixed chunk size
242
243 'hGet' waits until exactly the requested number of bytes are available for
244 each chunk.
245-}
246-- hGet :: MonadIO m => Int -> IO.Handle -> Producer' Text m ()
247-- hGet size h = go where
248-- go = do
249-- eof <- liftIO (IO.hIsEOF h)
250-- if eof
251-- then return ()
252-- else do
253-- bs <- liftIO (T.hGet h size)
254-- yield bs
255-- go
256-- {-# INLINABLE hGet #-}
257
258{-| Like 'hGetSome', except you can vary the maximum chunk size for each request
259-}
260-- hGetSomeN :: MonadIO m => IO.Handle -> Int -> Server' Int Text m ()
261-- hGetSomeN h = go where
262-- go size = do
263-- eof <- liftIO (IO.hIsEOF h)
264-- if eof
265-- then return ()
266-- else do
267-- bs <- liftIO (T.hGetSome h size)
268-- size2 <- respond bs
269-- go size2
270-- {-# INLINABLE hGetSomeN #-}
271--
272-- -- | Like 'hGet', except you can vary the chunk size for each request
273-- hGetN :: MonadIO m => IO.Handle -> Int -> Server' Int Text m ()
274-- hGetN h = go where
275-- go size = do
276-- eof <- liftIO (IO.hIsEOF h)
277-- if eof
278-- then return ()
279-- else do
280-- bs <- liftIO (T.hGet h size)
281-- size2 <- respond bs
282-- go size2
283-- {-# INLINABLE hGetN #-}
284
285{-| Stream bytes to 'stdout'
286
287 Unlike 'toHandle', 'stdout' gracefully terminates on a broken output pipe.
288
289 Note: For best performance, use @(for source (liftIO . putStr))@ instead of
290 @(source >-> stdout)@.
291-}
292stdout :: MonadIO m => Consumer' Text m ()
293stdout = go
294 where
295 go = do
296 txt <- await
297 x <- liftIO $ try (T.putStr txt)
298 case x of
299 Left (G.IOError { G.ioe_type = G.ResourceVanished
300 , G.ioe_errno = Just ioe })
301 | Errno ioe == ePIPE
302 -> return ()
303 Left e -> liftIO (throwIO e)
304 Right () -> go
305{-# INLINABLE stdout #-}
306
307stdoutLn :: (MonadIO m) => Consumer' Text m ()
308stdoutLn = go
309 where
310 go = do
311 str <- await
312 x <- liftIO $ try (T.putStrLn str)
313 case x of
314 Left (G.IOError { G.ioe_type = G.ResourceVanished
315 , G.ioe_errno = Just ioe })
316 | Errno ioe == ePIPE
317 -> return ()
318 Left e -> liftIO (throwIO e)
319 Right () -> go
320{-# INLINABLE stdoutLn #-}
321
322{-| Convert a byte stream into a 'Handle'
323
324 Note: For best performance, use @(for source (liftIO . hPutStr handle))@
325 instead of @(source >-> toHandle handle)@.
326-}
327toHandle :: MonadIO m => IO.Handle -> Consumer' Text m r
328toHandle h = for cat (liftIO . T.hPutStr h)
329{-# INLINABLE toHandle #-}
330
331writeFile :: (MonadSafe m, Base m ~ IO) => FilePath -> Consumer' Text m ()
332writeFile file = Safe.withFile file IO.WriteMode toHandle
333
334-- | Apply a transformation to each 'Char' in the stream
335map :: (Monad m) => (Char -> Char) -> Pipe Text Text m r
336map f = P.map (T.map f)
337{-# INLINABLE map #-}
338
339-- | Map a function over the byte stream and concatenate the results
340concatMap
341 :: (Monad m) => (Char -> Text) -> Pipe Text Text m r
342concatMap f = P.map (T.concatMap f)
343{-# INLINABLE concatMap #-}
344
345-- | @(take n)@ only allows @n@ bytes to pass
346take :: (Monad m, Integral a) => a -> Pipe Text Text m ()
347take n0 = go n0 where
348 go n
349 | n <= 0 = return ()
350 | otherwise = do
351 bs <- await
352 let len = fromIntegral (T.length bs)
353 if (len > n)
354 then yield (T.take (fromIntegral n) bs)
355 else do
356 yield bs
357 go (n - len)
358{-# INLINABLE take #-}
359
360-- | @(dropD n)@ drops the first @n@ bytes
361drop :: (Monad m, Integral a) => a -> Pipe Text Text m r
362drop n0 = go n0 where
363 go n
364 | n <= 0 = cat
365 | otherwise = do
366 bs <- await
367 let len = fromIntegral (T.length bs)
368 if (len >= n)
369 then do
370 yield (T.drop (fromIntegral n) bs)
371 cat
372 else go (n - len)
373{-# INLINABLE drop #-}
374
375-- | Take bytes until they fail the predicate
376takeWhile :: (Monad m) => (Char -> Bool) -> Pipe Text Text m ()
377takeWhile predicate = go
378 where
379 go = do
380 bs <- await
381 let (prefix, suffix) = T.span predicate bs
382 if (T.null suffix)
383 then do
384 yield bs
385 go
386 else yield prefix
387{-# INLINABLE takeWhile #-}
388
389-- | Drop bytes until they fail the predicate
390dropWhile :: (Monad m) => (Char -> Bool) -> Pipe Text Text m r
391dropWhile predicate = go where
392 go = do
393 bs <- await
394 case T.findIndex (not . predicate) bs of
395 Nothing -> go
396 Just i -> do
397 yield (T.drop i bs)
398 cat
399{-# INLINABLE dropWhile #-}
400
401-- | Only allows 'Char's to pass if they satisfy the predicate
402filter :: (Monad m) => (Char -> Bool) -> Pipe Text Text m r
403filter predicate = P.map (T.filter predicate)
404{-# INLINABLE filter #-}
405
406-- | Stream all indices whose elements match the given 'Char'
407-- elemIndices :: (Monad m, Num n) => Char -> Pipe Text n m r
408-- elemIndices w8 = findIndices (w8 ==)
409-- {-# INLINABLE elemIndices #-}
410
411-- | Stream all indices whose elements satisfy the given predicate
412-- findIndices :: (Monad m, Num n) => (Char -> Bool) -> Pipe Text n m r
413-- findIndices predicate = go 0
414-- where
415-- go n = do
416-- bs <- await
417-- each $ List.map (\i -> n + fromIntegral i) (T.findIndices predicate bs)
418-- go $! n + fromIntegral (T.length bs)
419-- {-# INLINABLE findIndices #-}
420
421-- | Strict left scan over the bytes
422scan
423 :: (Monad m)
424 => (Char -> Char -> Char) -> Char -> Pipe Text Text m r
425scan step begin = go begin
426 where
427 go w8 = do
428 bs <- await
429 let bs' = T.scanl step w8 bs
430 w8' = T.last bs'
431 yield bs'
432 go w8'
433{-# INLINABLE scan #-}
434
435{-| Fold a pure 'Producer' of strict 'Text's into a lazy
436 'TL.Text'
437-}
438toLazy :: Producer Text Identity () -> TL.Text
439toLazy = TL.fromChunks . P.toList
440{-# INLINABLE toLazy #-}
441
442{-| Fold an effectful 'Producer' of strict 'Text's into a lazy
443 'TL.Text'
444
445 Note: 'toLazyM' is not an idiomatic use of @pipes@, but I provide it for
446 simple testing purposes. Idiomatic @pipes@ style consumes the chunks
447 immediately as they are generated instead of loading them all into memory.
448-}
449toLazyM :: (Monad m) => Producer Text m () -> m TL.Text
450toLazyM = liftM TL.fromChunks . P.toListM
451{-# INLINABLE toLazyM #-}
452
453-- | Reduce the stream of bytes using a strict left fold
454fold
455 :: Monad m
456 => (x -> Char -> x) -> x -> (x -> r) -> Producer Text m () -> m r
457fold step begin done = P.fold (\x bs -> T.foldl' step x bs) begin done
458{-# INLINABLE fold #-}
459
460-- | Retrieve the first 'Char'
461head :: (Monad m) => Producer Text m () -> m (Maybe Char)
462head = go
463 where
464 go p = do
465 x <- nextChar p
466 case x of
467 Left _ -> return Nothing
468 Right (w8, _) -> return (Just w8)
469{-# INLINABLE head #-}
470
471-- | Retrieve the last 'Char'
472last :: (Monad m) => Producer Text m () -> m (Maybe Char)
473last = go Nothing
474 where
475 go r p = do
476 x <- next p
477 case x of
478 Left () -> return r
479 Right (bs, p') ->
480 if (T.null bs)
481 then go r p'
482 else go (Just $ T.last bs) p'
483 -- TODO: Change this to 'unsafeLast' when bytestring-0.10.2.0
484 -- becomes more widespread
485{-# INLINABLE last #-}
486
487-- | Determine if the stream is empty
488null :: (Monad m) => Producer Text m () -> m Bool
489null = P.all T.null
490{-# INLINABLE null #-}
491
492-- | Count the number of bytes
493length :: (Monad m, Num n) => Producer Text m () -> m n
494length = P.fold (\n bs -> n + fromIntegral (T.length bs)) 0 id
495{-# INLINABLE length #-}
496
497-- | Fold that returns whether 'M.Any' received 'Char's satisfy the predicate
498any :: (Monad m) => (Char -> Bool) -> Producer Text m () -> m Bool
499any predicate = P.any (T.any predicate)
500{-# INLINABLE any #-}
501
502-- | Fold that returns whether 'M.All' received 'Char's satisfy the predicate
503all :: (Monad m) => (Char -> Bool) -> Producer Text m () -> m Bool
504all predicate = P.all (T.all predicate)
505{-# INLINABLE all #-}
506
507-- | Return the maximum 'Char' within a byte stream
508maximum :: (Monad m) => Producer Text m () -> m (Maybe Char)
509maximum = P.fold step Nothing id
510 where
511 step mw8 bs =
512 if (T.null bs)
513 then mw8
514 else Just $ case mw8 of
515 Nothing -> T.maximum bs
516 Just w8 -> max w8 (T.maximum bs)
517{-# INLINABLE maximum #-}
518
519-- | Return the minimum 'Char' within a byte stream
520minimum :: (Monad m) => Producer Text m () -> m (Maybe Char)
521minimum = P.fold step Nothing id
522 where
523 step mw8 bs =
524 if (T.null bs)
525 then mw8
526 else case mw8 of
527 Nothing -> Just (T.minimum bs)
528 Just w8 -> Just (min w8 (T.minimum bs))
529{-# INLINABLE minimum #-}
530
531-- | Determine whether any element in the byte stream matches the given 'Char'
532-- elem :: (Monad m) => Char -> Producer Text m () -> m Bool
533-- elem w8 = P.any (T.elem w8)
534-- {-# INLINABLE elem #-}
535--
536-- {-| Determine whether all elements in the byte stream do not match the given
537-- 'Char'
538-- -}
539-- notElem :: (Monad m) => Char -> Producer Text m () -> m Bool
540-- notElem w8 = P.all (T.notElem w8)
541-- {-# INLINABLE notElem #-}
542
543-- | Find the first element in the stream that matches the predicate
544find
545 :: (Monad m)
546 => (Char -> Bool) -> Producer Text m () -> m (Maybe Char)
547find predicate p = head (p >-> filter predicate)
548{-# INLINABLE find #-}
549
550-- | Index into a byte stream
551index
552 :: (Monad m, Integral a)
553 => a-> Producer Text m () -> m (Maybe Char)
554index n p = head (p >-> drop n)
555{-# INLINABLE index #-}
556
557-- | Find the index of an element that matches the given 'Char'
558-- elemIndex
559-- :: (Monad m, Num n) => Char -> Producer Text m () -> m (Maybe n)
560-- elemIndex w8 = findIndex (w8 ==)
561-- {-# INLINABLE elemIndex #-}
562
563-- | Store the first index of an element that satisfies the predicate
564-- findIndex
565-- :: (Monad m, Num n)
566-- => (Char -> Bool) -> Producer Text m () -> m (Maybe n)
567-- findIndex predicate p = P.head (p >-> findIndices predicate)
568-- {-# INLINABLE findIndex #-}
569--
570-- -- | Store a tally of how many elements match the given 'Char'
571-- count :: (Monad m, Num n) => Char -> Producer Text m () -> m n
572-- count w8 p = P.fold (+) 0 id (p >-> P.map (fromIntegral . T.count w8))
573-- {-# INLINABLE count #-}
574
575-- | Splits a 'Producer' after the given number of bytes
576splitAt
577 :: (Monad m, Integral n)
578 => n
579 -> Producer Text m r
580 -> Producer' Text m (Producer Text m r)
581splitAt = go
582 where
583 go 0 p = return p
584 go n p = do
585 x <- lift (next p)
586 case x of
587 Left r -> return (return r)
588 Right (bs, p') -> do
589 let len = fromIntegral (T.length bs)
590 if (len <= n)
591 then do
592 yield bs
593 go (n - len) p'
594 else do
595 let (prefix, suffix) = T.splitAt (fromIntegral n) bs
596 yield prefix
597 return (yield suffix >> p')
598{-# INLINABLE splitAt #-}
599
600-- | Split a byte stream into 'FreeT'-delimited byte streams of fixed size
601chunksOf
602 :: (Monad m, Integral n)
603 => n -> Producer Text m r -> FreeT (Producer Text m) m r
604chunksOf n p0 = PP.FreeT (go p0)
605 where
606 go p = do
607 x <- next p
608 return $ case x of
609 Left r -> PP.Pure r
610 Right (bs, p') -> PP.Free $ do
611 p'' <- splitAt n (yield bs >> p')
612 return $ PP.FreeT (go p'')
613{-# INLINABLE chunksOf #-}
614
615{-| Split a byte stream in two, where the first byte stream is the longest
616 consecutive group of bytes that satisfy the predicate
617-}
618span
619 :: (Monad m)
620 => (Char -> Bool)
621 -> Producer Text m r
622 -> Producer' Text m (Producer Text m r)
623span predicate = go
624 where
625 go p = do
626 x <- lift (next p)
627 case x of
628 Left r -> return (return r)
629 Right (bs, p') -> do
630 let (prefix, suffix) = T.span predicate bs
631 if (T.null suffix)
632 then do
633 yield bs
634 go p'
635 else do
636 yield prefix
637 return (yield suffix >> p')
638{-# INLINABLE span #-}
639
640{-| Split a byte stream in two, where the first byte stream is the longest
641 consecutive group of bytes that don't satisfy the predicate
642-}
643break
644 :: (Monad m)
645 => (Char -> Bool)
646 -> Producer Text m r
647 -> Producer Text m (Producer Text m r)
648break predicate = span (not . predicate)
649{-# INLINABLE break #-}
650
651{-| Split a byte stream into sub-streams delimited by bytes that satisfy the
652 predicate
653-}
654splitWith
655 :: (Monad m)
656 => (Char -> Bool)
657 -> Producer Text m r
658 -> PP.FreeT (Producer Text m) m r
659splitWith predicate p0 = PP.FreeT (go0 p0)
660 where
661 go0 p = do
662 x <- next p
663 case x of
664 Left r -> return (PP.Pure r)
665 Right (bs, p') ->
666 if (T.null bs)
667 then go0 p'
668 else return $ PP.Free $ do
669 p'' <- span (not . predicate) (yield bs >> p')
670 return $ PP.FreeT (go1 p'')
671 go1 p = do
672 x <- nextChar p
673 return $ case x of
674 Left r -> PP.Pure r
675 Right (_, p') -> PP.Free $ do
676 p'' <- span (not . predicate) p'
677 return $ PP.FreeT (go1 p'')
678{-# INLINABLE splitWith #-}
679
680-- | Split a byte stream using the given 'Char' as the delimiter
681split :: (Monad m)
682 => Char
683 -> Producer Text m r
684 -> FreeT (Producer Text m) m r
685split w8 = splitWith (w8 ==)
686{-# INLINABLE split #-}
687
688{-| Group a byte stream into 'FreeT'-delimited byte streams using the supplied
689 equality predicate
690-}
691groupBy
692 :: (Monad m)
693 => (Char -> Char -> Bool)
694 -> Producer Text m r
695 -> FreeT (Producer Text m) m r
696groupBy equal p0 = PP.FreeT (go p0)
697 where
698 go p = do
699 x <- next p
700 case x of
701 Left r -> return (PP.Pure r)
702 Right (bs, p') -> case (T.uncons bs) of
703 Nothing -> go p'
704 Just (w8, _) -> do
705 return $ PP.Free $ do
706 p'' <- span (equal w8) (yield bs >> p')
707 return $ PP.FreeT (go p'')
708{-# INLINABLE groupBy #-}
709
710-- | Group a byte stream into 'FreeT'-delimited byte streams of identical bytes
711group
712 :: (Monad m) => Producer Text m r -> FreeT (Producer Text m) m r
713group = groupBy (==)
714{-# INLINABLE group #-}
715
716{-| Split a byte stream into 'FreeT'-delimited lines
717
718 Note: This function is purely for demonstration purposes since it assumes a
719 particular encoding. You should prefer the 'Data.Text.Text' equivalent of
720 this function from the upcoming @pipes-text@ library.
721-}
722lines
723 :: (Monad m) => Producer Text m r -> FreeT (Producer Text m) m r
724lines p0 = PP.FreeT (go0 p0)
725 where
726 go0 p = do
727 x <- next p
728 case x of
729 Left r -> return (PP.Pure r)
730 Right (bs, p') ->
731 if (T.null bs)
732 then go0 p'
733 else return $ PP.Free $ go1 (yield bs >> p')
734 go1 p = do
735 p' <- break ('\n' ==) p
736 return $ PP.FreeT (go2 p')
737 go2 p = do
738 x <- nextChar p
739 return $ case x of
740 Left r -> PP.Pure r
741 Right (_, p') -> PP.Free (go1 p')
742{-# INLINABLE lines #-}
743nextChar = undefined
744{-| Split a byte stream into 'FreeT'-delimited words
745
746 Note: This function is purely for demonstration purposes since it assumes a
747 particular encoding. You should prefer the 'Data.Text.Text' equivalent of
748 this function from the upcoming @pipes-text@ library.
749-}
750words
751 :: (Monad m) => Producer Text m r -> FreeT (Producer Text m) m r
752words p0 = removeEmpty (splitWith isSpace p0)
753 where
754 removeEmpty f = PP.FreeT $ do
755 x <- PP.runFreeT f
756 case x of
757 PP.Pure r -> return (PP.Pure r)
758 PP.Free p -> do
759 y <- next p
760 case y of
761 Left f' -> PP.runFreeT (removeEmpty f')
762 Right (bs, p') -> return $ PP.Free $ do
763 yield bs
764 f' <- p'
765 return (removeEmpty f')
766{-# INLINABLE words #-}
767
768-- | Intersperse a 'Char' in between the bytes of the byte stream
769intersperse
770 :: (Monad m) => Char -> Producer Text m r -> Producer Text m r
771intersperse w8 = go0
772 where
773 go0 p = do
774 x <- lift (next p)
775 case x of
776 Left r -> return r
777 Right (bs, p') -> do
778 yield (T.intersperse w8 bs)
779 go1 p'
780 go1 p = do
781 x <- lift (next p)
782 case x of
783 Left r -> return r
784 Right (bs, p') -> do
785 yield (T.singleton w8)
786 yield (T.intersperse w8 bs)
787 go1 p'
788{-# INLINABLE intersperse #-}
789
790{-| 'intercalate' concatenates the 'FreeT'-delimited byte streams after
791 interspersing a byte stream in between them
792-}
793intercalate
794 :: (Monad m)
795 => Producer Text m ()
796 -> FreeT (Producer Text m) m r
797 -> Producer Text m r
798intercalate p0 = go0
799 where
800 go0 f = do
801 x <- lift (PP.runFreeT f)
802 case x of
803 PP.Pure r -> return r
804 PP.Free p -> do
805 f' <- p
806 go1 f'
807 go1 f = do
808 x <- lift (PP.runFreeT f)
809 case x of
810 PP.Pure r -> return r
811 PP.Free p -> do
812 p0
813 f' <- p
814 go1 f'
815{-# INLINABLE intercalate #-}
816
817{-| Join 'FreeT'-delimited lines into a byte stream
818
819 Note: This function is purely for demonstration purposes since it assumes a
820 particular encoding. You should prefer the 'Data.Text.Text' equivalent of
821 this function from the upcoming @pipes-text@ library.
822-}
823unlines
824 :: (Monad m) => FreeT (Producer Text m) m r -> Producer Text m r
825unlines = go
826 where
827 go f = do
828 x <- lift (PP.runFreeT f)
829 case x of
830 PP.Pure r -> return r
831 PP.Free p -> do
832 f' <- p
833 yield $ T.singleton '\n'
834 go f'
835{-# INLINABLE unlines #-}
836
837{-| Join 'FreeT'-delimited words into a byte stream
838
839 Note: This function is purely for demonstration purposes since it assumes a
840 particular encoding. You should prefer the 'Data.Text.Text' equivalent of
841 this function from the upcoming @pipes-text@ library.
842-}
843unwords
844 :: (Monad m) => FreeT (Producer Text m) m r -> Producer Text m r
845unwords = intercalate (yield $ T.pack " ")
846{-# INLINABLE unwords #-}
847
848{- $parse
849 The following parsing utilities are single-byte analogs of the ones found
850 in @pipes-parse@.
851-}
852
853{-| Take bytes until they fail the predicate
854
855 Unlike 'takeWhile', this 'PP.unDraw's unused bytes
856-}
857-- takeWhile'
858-- :: (Monad m)
859-- => (Char -> Bool)
860-- -> Pipe Text Text (StateT (Producer Text m r) m) ()
861-- takeWhile' = PBP.takeWhile
862-- {-# INLINABLE takeWhile' #-}
863-- {-# DEPRECATED takeWhile' "Use Pipes.Text.Parse.takeWhile instead" #-}
864
865{- $reexports
866 "Pipes.Text.Parse" re-exports 'nextByte', 'drawByte', 'unDrawByte',
867 'peekByte', and 'isEndOfBytes'.
868
869 @Data.Text@ re-exports the 'Text' type.
870
871 @Data.Word@ re-exports the 'Char' type.
872
873 @Pipes.Parse@ re-exports 'input', 'concat', and 'FreeT' (the type).
874-}