iteratee-0.1.0: src/Data/Iteratee/Char.hs
-- Haskell98!
-- |Utilties for Char-based iteratee processing.
-- The running example, parts 1 and 2
-- Part 1 is reading the headers, the sequence of lines terminated by an
-- empty line. Each line is terminated by CR, LF, or CRLF.
-- We should return the headers in order. In the case of error,
-- we should return the headers read so far and the description of the error.
-- Part 2 is reading the headers and reading all the lines from the
-- HTTP-chunk-encoded content that follows the headers. Part 2 thus
-- verifies layering of streams, and processing of one stream
-- embedded (chunk encoded) into another stream.
module Data.Iteratee.Char (
-- * Type synonyms
Stream,
Iteratee,
IterateeM,
EnumeratorM,
Line,
-- * Word and Line processors
line,
print_lines,
enum_lines,
enum_words,
module Data.Iteratee.Base
)
where
import Data.Iteratee.Base (StreamG (..), IterateeG, IterateeGM, EnumeratorGM, (==<<), liftI)
import qualified Data.Iteratee.Base as Iter
import Data.Char
import Control.Monad.Trans
-- |A particular instance of StreamG: the stream of characters.
-- This stream is used by many input parsers.
type Stream = StreamG [] Char
type Iteratee m a = IterateeG [] Char m a
type IterateeM m a = IterateeGM [] Char m a
-- Useful combinators for implementing iteratees and enumerators
type Line = String -- The line of text, terminators are not included
-- |Read the line of text from the stream
-- The line can be terminated by CR, LF or CRLF.
-- Return (Right Line) if successful. Return (Left Line) if EOF or
-- a stream error were encountered before the terminator is seen.
-- The returned line is the string read so far.
-- The code is the same as that of pure Iteratee, only the signature
-- has changed.
-- Compare the code below with GHCBufferIO.line_lazy
line :: Monad m => IterateeM m (Either Line Line)
line = Iter.break (\c -> c == '\r' || c == '\n') >>= check_next
where
check_next (line',Just '\r') = Iter.peek >>= \c ->
case c of
Just '\n' -> Iter.head >> return (Right line')
Just _ -> return (Right line')
Nothing -> return (Left line')
check_next (line',Just _) = return (Right line')
check_next (line',Nothing) = return (Left line')
-- Line iteratees: processors of a stream whose elements are made of Lines
-- Collect all read lines and return them as a list
-- see stream2list
-- |Print lines as they are received. This is the first `impure' iteratee
-- with non-trivial actions during chunk processing
print_lines :: IterateeGM [] Line IO ()
print_lines = liftI $ Iter.Cont step
where
step (Chunk []) = print_lines
step (Chunk ls) = lift (mapM_ pr_line ls) >> print_lines
step EOF = lift (putStrLn ">> natural end") >> liftI (Iter.Done () EOF)
step stream = lift (putStrLn ">> unnatural end") >>
liftI (Iter.Done () stream)
pr_line line' = putStrLn $ ">> read line: " ++ line'
-- |Convert the stream of characters to the stream of lines, and
-- apply the given iteratee to enumerate the latter.
-- The stream of lines is normally terminated by the empty line.
-- When the stream of characters is terminated, the stream of lines
-- is also terminated, abnormally.
-- This is the first proper iteratee-enumerator: it is the iteratee of the
-- character stream and the enumerator of the line stream.
enum_lines :: Monad m =>
IterateeG [] Line m a ->
IterateeGM [] Char m (IterateeG [] Line m a)
enum_lines iter@Iter.Done{} = return iter
enum_lines Iter.Seek{} = error "Seeking is not supported by enum_lines"
enum_lines (Iter.Cont k) = line >>= check_line k
where
check_line k' (Right "") =
enum_lines ==<< k' EOF -- empty line, normal term
check_line k' (Right l) = enum_lines ==<< k' (Chunk [l])
check_line k' _ = enum_lines ==<< k' (Error "EOF") -- abnormal termin
-- |Convert the stream of characters to the stream of words, and
-- apply the given iteratee to enumerate the latter.
-- Words are delimited by white space.
-- This is the analogue of List.words
-- One should keep in mind that enum_words is a more general, monadic
-- function.
enum_words :: Monad m =>
IterateeG [] String m a ->
IterateeGM [] Char m (IterateeG [] String m a)
enum_words iter@Iter.Done{} = return iter
enum_words Iter.Seek{} = error "Seeking not supported by enum_words"
enum_words (Iter.Cont k) = Iter.dropWhile isSpace >>
Iter.break isSpace >>= check_word k
where
check_word k' ("",_) = enum_words ==<< k' EOF
check_word k' (str,_) = enum_words ==<< k' (Chunk [str])
-- ------------------------------------------------------------------------
-- Enumerators
type EnumeratorM m a = EnumeratorGM [] Char m a