packages feed

enumerator 0.4.4 → 0.4.5

raw patch · 34 files changed

+5786/−851 lines, 34 filesdep ~textPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependency ranges changed: text

API changes (from Hackage documentation)

+ Data.Enumerator: concatMapM :: Monad m => (ao -> m [ai]) -> Enumeratee ao ai m b
+ Data.Enumerator: filter :: Monad m => (a -> Bool) -> Enumeratee a a m b
+ Data.Enumerator: filterM :: Monad m => (a -> m Bool) -> Enumeratee a a m b
+ Data.Enumerator: foldM :: Monad m => (b -> a -> m b) -> b -> Iteratee a m b
+ Data.Enumerator: foldl :: Monad m => (b -> a -> b) -> b -> Iteratee a m b
+ Data.Enumerator: foldl' :: Monad m => (b -> a -> b) -> b -> Iteratee a m b
+ Data.Enumerator: generateM :: Monad m => m (Maybe a) -> Enumerator a m b
+ Data.Enumerator: instance Applicative Stream
+ Data.Enumerator: iterate :: Monad m => (a -> a) -> a -> Enumerator a m b
+ Data.Enumerator: iterateM :: Monad m => (a -> m a) -> a -> Enumerator a m b
+ Data.Enumerator: joinE :: Monad m => Enumerator ao m (Step ai m b) -> Enumeratee ao ai m b -> Enumerator ai m b
+ Data.Enumerator: repeat :: Monad m => a -> Enumerator a m b
+ Data.Enumerator: repeatM :: Monad m => m a -> Enumerator a m b
+ Data.Enumerator: replicate :: Monad m => Integer -> a -> Enumerator a m b
+ Data.Enumerator: replicateM :: Monad m => Integer -> m a -> Enumerator a m b
+ Data.Enumerator.Binary: consume :: Monad m => Iteratee ByteString m ByteString
+ Data.Enumerator.Binary: drop :: Monad m => Integer -> Iteratee ByteString m ()
+ Data.Enumerator.Binary: dropWhile :: Monad m => (Word8 -> Bool) -> Iteratee ByteString m ()
+ Data.Enumerator.Binary: enumFile :: FilePath -> Enumerator ByteString IO b
+ Data.Enumerator.Binary: enumHandle :: MonadIO m => Integer -> Handle -> Enumerator ByteString m b
+ Data.Enumerator.Binary: head :: Monad m => Iteratee ByteString m (Maybe Word8)
+ Data.Enumerator.Binary: isolate :: Monad m => Integer -> Enumeratee ByteString ByteString m b
+ Data.Enumerator.Binary: iterHandle :: MonadIO m => Handle -> Iteratee ByteString m ()
+ Data.Enumerator.Binary: require :: Monad m => Integer -> Iteratee ByteString m ()
+ Data.Enumerator.Binary: take :: Monad m => Integer -> Iteratee ByteString m ByteString
+ Data.Enumerator.Binary: takeWhile :: Monad m => (Word8 -> Bool) -> Iteratee ByteString m ByteString
+ Data.Enumerator.List: consume :: Monad m => Iteratee a m [a]
+ Data.Enumerator.List: drop :: Monad m => Integer -> Iteratee a m ()
+ Data.Enumerator.List: dropWhile :: Monad m => (a -> Bool) -> Iteratee a m ()
+ Data.Enumerator.List: head :: Monad m => Iteratee a m (Maybe a)
+ Data.Enumerator.List: isolate :: Monad m => Integer -> Enumeratee a a m b
+ Data.Enumerator.List: require :: Monad m => Integer -> Iteratee a m ()
+ Data.Enumerator.List: take :: Monad m => Integer -> Iteratee a m [a]
+ Data.Enumerator.List: takeWhile :: Monad m => (a -> Bool) -> Iteratee a m [a]
+ Data.Enumerator.Text: consume :: Monad m => Iteratee Text m Text
+ Data.Enumerator.Text: drop :: Monad m => Integer -> Iteratee Text m ()
+ Data.Enumerator.Text: dropWhile :: Monad m => (Char -> Bool) -> Iteratee Text m ()
+ Data.Enumerator.Text: head :: Monad m => Iteratee Text m (Maybe Char)
+ Data.Enumerator.Text: isolate :: Monad m => Integer -> Enumeratee Text Text m b
+ Data.Enumerator.Text: require :: Monad m => Integer -> Iteratee Text m ()
+ Data.Enumerator.Text: take :: Monad m => Integer -> Iteratee Text m Text
+ Data.Enumerator.Text: takeWhile :: Monad m => (Char -> Bool) -> Iteratee Text m Text
- Data.Enumerator: type Enumeratee aOut aIn m b = Step aIn m b -> Iteratee aOut m (Step aIn m b)
+ Data.Enumerator: type Enumeratee ao ai m b = Step ai m b -> Iteratee ao m (Step ai m b)

Files

− Examples/cat.hs
@@ -1,122 +0,0 @@--------------------------------------------------------------------------------- |--- Copyright: 2010 John Millikin--- License: MIT------ Maintainer: jmillikin@gmail.com--- Portability: portable----------------------------------------------------------------------------------module Main (main) where-import Prelude as Prelude-import Control.Exception as E-import Data.Enumerator-import qualified Data.ByteString as B-import qualified Foreign as F-import System.IO-import System.IO.Error (isEOFError)-import System.Environment (getArgs)---- The following definitions of 'enumHandle', 'enumFile', and 'iterHandle' are--- copied from "Data.Enumerator.IO", with additional comments so they're easier--- to understand.--enumHandle :: Integer -- ^ Buffer size-           -> Handle-           -> Enumerator B.ByteString IO b-enumHandle bufferSize h = Iteratee . loop where-	intSize = fromInteger bufferSize-	-	-- If more input is required before the enumerator's iteratee can-	-- yield a result, feed it from the handle.-	loop (Continue k) = do-		-- While not strictly necessary to proper operation, catching-		-- exceptions here allows more unified exception handling when-		-- the enumerator/iteratee is run.-		eitherBytes <- E.try $ do-			-			-- The enumerator must function normally when the-			-- handle is something like a slow file, or network-			-- socket; if there's not enough data to fill the-			-- buffer yet, a partial read is returned.-			hasInput <- E.catch-				(hWaitForInput h (1))-				(\err -> if isEOFError err-					then return False-					else E.throwIO err)-			-			-- An EOF is represented by the empty bytestring-			if hasInput-				then B.hGetNonBlocking h intSize-				else return B.empty-			-		case eitherBytes of-			-- Interacting with the socket threw an IO error of-			-- some sort-			Left err -> return $ Error err-			-			-- The socket has reached EOF; pass control to the-			-- next enumerator-			Right bytes | B.null bytes -> return (Continue k)-			-			-- Bytes were read successfully; feed them to the-			-- iteratee and continue looping-			Right bytes -> runIteratee (k (Chunks [bytes])) >>= loop-	-	-- If a different step is received ('Error' or 'Yield'), just pass-	-- it through.-	loop step = return step--enumFile :: FilePath -> Enumerator B.ByteString IO b-enumFile path s = Iteratee $ do-	-- Opening the file can be performed either inside or outside of the-	-- Iteratee. Inside allows exceptions to be caught and propagated-	-- through the 'Error' step constructor.-	eitherH <- E.try $ openBinaryFile path ReadMode-	case eitherH of-		Left err -> return $ Error err-		Right h -> finally-			(runIteratee (enumHandle 4096 h s))-			(hClose h)---- 'iterHandle' is the opposite of 'enumHandle', in that it *writes to* a--- handle instead of reading from it. An enumerator is a source, an iteratee--- is a sink.-iterHandle :: Handle -> Iteratee B.ByteString IO ()---- Most iteratees start in the 'Continue' state, as they need some--- input before they can produce any value.-iterHandle h = continue step where-	-	-- This iteratee produces no value; its only purpose is its-	-- side-effects. When 'EOF' is received, it simply yields ().-	step EOF = yield () EOF-	-	-- When some chunks are received from the Enumeratee, they're written-	-- to the handle. Any exceptions are caught and reported, as in-	-- 'enumHandle'.-	step (Chunks bytes) = Iteratee $ do-		eitherErr <- E.try $ mapM_ (B.hPut h) bytes-		return $ case eitherErr of-			Left err -> Error err-			_ -> Continue step--main :: IO ()-main = do-	-- Our example enumlates standard /bin/cat, where if the argument list-	-- is empty, data is echoed from stdin.-	args <- getArgs-	let enum = if null args-		then enumHandle 1 stdin-		else concatEnums (Prelude.map enumFile args)-	-	-- 'run' sends an EOF to an iteratee and returns its output, which-	-- is either a 'Yield' or an 'Error'.-	res <- run (enum $$ iterHandle stdout)-	-	-- Finally, 'run' has returned either an error or the iteratee's-	-- result. 'iterHandle' doesn't return a useful result, so as long-	-- as it succeeded the actual value is ignored.-	case res of-		Left err -> putStrLn $ "ERROR: " ++ show err-		Right _ -> return ()
− Examples/enumerator-wcl.hs
@@ -1,22 +0,0 @@-module Main (main) where-import Data.Enumerator-import Data.Enumerator.IO-import qualified Data.ByteString as B-import qualified Data.ByteString.Char8 as B8-import System.IO-import System.Environment--iterLines :: Monad m => Iteratee e B.ByteString m Integer-iterLines = continue (step 0) where-	step acc EOF = yield acc EOF-	step acc (Chunks xs) = continue $ step $! foldl foldStep acc xs-	foldStep acc bytes = acc + countChar '\n' bytes--countChar :: Char -> B.ByteString -> Integer-countChar c = B8.foldl (\acc c' -> if c' == c then acc + 1 else acc) 0--main :: IO ()-main = do-	filename:_ <- getArgs-	h <- openBinaryFile filename ReadMode-	run (iterLines >>== enumHandle 4096 h) >>= print
− Examples/iteratee-wcl.hs
@@ -1,24 +0,0 @@-module Main (main) where-import Data.Iteratee-import Data.Iteratee.IO-import Data.Iteratee.WrappedByteString-import Data.Word (Word8)-import qualified Data.ByteString as B-import qualified Data.ByteString.Char8 as B8-import System.IO-import System.Environment--iterLines :: Monad m => IterateeG WrappedByteString Word8 m Integer-iterLines = IterateeG (step 0) where-	step acc s@(EOF _) = return $ Done acc s-	step acc (Chunk wrapped) = return $ Cont (IterateeG (step $! acc')) Nothing where-		acc' = acc + countChar '\n' (unWrap wrapped)--countChar :: Char -> B.ByteString -> Integer-countChar c = B8.foldl (\acc c' -> if c' == c then acc + 1 else acc) 0--main :: IO ()-main = do-	filename:_ <- getArgs-	h <- openBinaryFile filename ReadMode-	enumHandle h iterLines >>= run >>= print
− Examples/lazy-bytestring-wcl.hs
@@ -1,16 +0,0 @@-import qualified Data.ByteString.Lazy as B-import qualified Data.ByteString.Lazy.Char8 as B8-import System.IO-import System.Environment--countChar :: Char -> B.ByteString -> Integer-countChar c = B8.foldl' (\acc c' -> if c' == c then acc + 1 else acc) 0--countHandle :: Handle -> IO Integer-countHandle h = fmap (countChar '\n') (B.hGetContents h)--main :: IO ()-main = do-	filename:_ <- getArgs-	h <- openBinaryFile filename ReadMode-	countHandle h >>= print
− Examples/skip.hs
@@ -1,16 +0,0 @@-module Main where-import Prelude hiding (head)-import Data.Enumerator-import Control.Monad.Trans.Class--skip :: Monad m => Enumeratee a a m b-skip (Continue k) = do-    x <- head-    _ <- head -- the one we're skipping-    case x of-        Nothing -> return $ Continue k-        Just y -> do-            newStep <- lift $ runIteratee $ k $ Chunks [y]-            skip newStep-skip step = return step-
− Examples/strict-bytestring-wcl.hs
@@ -1,21 +0,0 @@-import qualified Data.ByteString as B-import qualified Data.ByteString.Char8 as B8-import System.IO-import System.Environment--countChar :: Char -> B.ByteString -> Integer-countChar c = B8.foldl' (\acc c' -> if c' == c then acc + 1 else acc) 0--countHandle :: Handle -> IO Integer-countHandle h = loop 0 where-	loop acc = do-		bytes <- B.hGet h 4096-		if B.null bytes-			then return acc-			else let acc' = acc + countChar '\n' bytes in loop $! acc'--main :: IO ()-main = do-	filename:_ <- getArgs-	h <- openBinaryFile filename ReadMode-	countHandle h >>= print
− Examples/wc.hs
@@ -1,108 +0,0 @@--------------------------------------------------------------------------------- |--- Copyright: 2010 John Millikin--- License: MIT------ Maintainer: jmillikin@gmail.com--- Portability: portable----------------------------------------------------------------------------------module Main (main) where-import Data.Enumerator-import qualified Data.Enumerator.IO as EIO-import qualified Data.Enumerator.Text as ET-import qualified Data.ByteString as B-import qualified Data.ByteString.Char8 as B8-import qualified Data.Text as T---- support imports-import Control.Exception as E-import Data.List-import Control.Monad (unless, forM_)-import System.IO-import System.Console.GetOpt-import System.Environment-import System.Exit---- support wc modes -c (bytes), -m (characters), and -l (lines)---- iterBytes simply counts how many bytes are in each chunk, accumulates this--- count, and returns it when EOF is received--iterBytes :: Monad m => Iteratee B.ByteString m Integer-iterBytes = continue (step 0) where-	step acc EOF = yield acc EOF-	step acc (Chunks xs) = continue $ step $! foldl' foldStep acc xs-	foldStep acc bytes = acc + toInteger (B.length bytes)---- iterLines is similar, except it only counts newlines ('\n')------ Because it's basically the same as 'iterBytes', we use it to demonstrate--- the 'liftFoldL\'' helper function.--iterLines :: Monad m => Iteratee B.ByteString m Integer-iterLines = liftFoldL' step 0 where-	step acc bytes = acc + countChar '\n' bytes-	countChar c = B8.foldl (\acc c' -> if c' == c then acc + 1 else acc) 0---- iterChars is a bit more complicated. It has to decode the input (for now,--- assuming UTF-8) before performing any counting. Leftover bytes, not part--- of a valid UTF-8 character, are yielded as surplus------ Note the use of joinI. 'ET.decode' is an enumeratee, which means it returns--- an iteratee yielding an inner step. 'joinI' "collapses" an enumeratee's--- return value, much as 'join' does to monadic values.--iterChars :: Monad m => Iteratee B.ByteString m Integer-iterChars = joinI (ET.decode ET.utf8 $$ count) where-	count = liftFoldL' (\acc t -> acc + toInteger (T.length t)) 0--main :: IO ()-main = do-	(mode, files) <- getMode-	-	-- Exactly matching wc's output is too annoying, so this example-	-- will just print one line per file, and support counting at most-	-- one statistic per run-	let iter = case mode of-		OptionBytes -> iterBytes-		OptionLines -> iterLines-		OptionChars -> iterChars-	-	forM_ files $ \filename -> do-		putStr $ filename ++ ": "-		-		-- see cat.hs for commented implementation of 'Data.Enumerator.IO.enumFile'-		eitherStat <- run (EIO.enumFile filename $$ iter)-		putStrLn $ case eitherStat of-			Left err -> "ERROR: " ++ show err-			Right stat -> show stat---- uninteresting option parsing follows--data Option-	= OptionBytes-	| OptionChars-	| OptionLines--optionInfo :: [OptDescr Option]-optionInfo =-	[ Option ['c'] ["bytes"] (NoArg OptionBytes) "count bytes"-	, Option ['m'] ["chars"] (NoArg OptionChars) "count characters"-	, Option ['l'] ["lines"] (NoArg OptionLines) "count lines"-	]--usage :: String -> String-usage name = "Usage: " ++ name ++ " <MODE> [FILES]"--getMode :: IO (Option, [FilePath])-getMode = do-	args <- getArgs-	let (options, files, errors) = getOpt Permute optionInfo args-	unless (null errors && not (null options) && not (null files)) $ do-		name <- getProgName-		hPutStrLn stderr $ concat errors-		hPutStrLn stderr $ usageInfo (usage name) optionInfo-		exitFailure-	-	return (Prelude.head options, files)
enumerator.cabal view
@@ -1,6 +1,6 @@ name: enumerator-version: 0.4.4-synopsis: Implementation of Oleg Kiselyov's left-fold enumerators+version: 0.4.5+synopsis: Reliable, high-performance processing with left-fold enumerators license: MIT license-file: license.txt author: John Millikin <jmillikin@gmail.com>@@ -15,27 +15,78 @@ tested-with: GHC==6.12.1  description:-  Based on Oleg Kiselyov's IterateeM: <http://okmij.org/ftp/Haskell/Iteratee/IterateeM.hs>+  Typical buffer&#x2013;based incremental I/O is based around a single loop,+  which reads data from some source (such as a socket or file), transforms+  it, and generates one or more outputs (such as a line count, HTTP+  responses, or modified file). Although efficient and safe, these loops are+  all single&#x2013;purpose; it is difficult or impossible to compose+  buffer&#x2013;based processing loops.+  .+  Haskell&#x2019;s concept of &#x201C;lazy I/O&#x201D; allows pure code to+  operate on data from an external source. However, lazy I/O has several+  shortcomings. Most notably, resources such as memory and file handles can+  be retained for arbitrarily long periods of time, causing unpredictable+  performance and error conditions.+  .+  Enumerators are an efficient, predictable, and safe alternative to lazy+  I/O. Discovered by Oleg Kiselyov, they allow large datasets to be processed+  in near&#x2013;constant space by pure code. Although somewhat more complex+  to write, using enumerators instead of lazy I/O produces more correct+  programs.+  .+  This library contains an enumerator implementation for Haskell, designed to+  be both simple and efficient. Three core types are defined, along with+  numerous helper functions:+  .+  * /Iteratee/: Data sinks, analogous to left folds. Iteratees consume+  a sequence of /input/ values, and generate a single /output/ value.+  Many iteratees are designed to perform side effects (such as printing to+  @stdout@), so they can also be used as monad transformers.+  .+  * /Enumerator/: Data sources, which generate input sequences. Typical+  enumerators read from a file handle, socket, random number generator, or+  other external stream. To operate, enumerators are passed an iteratee, and+  provide that iteratee with input until either the iteratee has completed its+  computation, or EOF.+  .+  * /Enumeratee/: Data transformers, which operate as both enumerators and+  iteratees. Enumeratees read from an /outer/ enumerator, and provide the+  transformed data to an /inner/ iteratee.  extra-source-files:-  Examples/*.hs   readme.txt+  --+  src/api-docs.anansi+  src/binary.anansi+  src/compat.anansi+  src/enumerator.anansi+  src/list.anansi+  src/primitives.anansi+  src/text.anansi+  src/types.anansi+  src/util.anansi+  --+  examples/cat.hs+  examples/wc.hs+  --+  scripts/cabal-dist+  scripts/run-tests+  --+  tests/enumerator-tests.cabal+  tests/Properties.hs  source-repository head   type: bazaar   location: http://john-millikin.com/software/enumerator/ --- text-0.11 changed some function names to appease a few bikeshedding idiots--- in -cafe; to support it, a special compatibility module is needed.-flag text-names-broken- library-  ghc-options: -Wall+  ghc-options: -Wall -O2   hs-source-dirs: hs    build-depends:       transformers >= 0.2 && < 0.3     , bytestring >= 0.9 && < 0.10+    , text >= 0.7 && < 0.12    if impl(ghc >= 6.10)     build-depends:@@ -45,20 +96,12 @@         base >= 3 && < 4       , extensible-exceptions >= 0.1 && < 0.2 -  if flag(text-names-broken)-    hs-source-dirs: hs/text-0.11-    build-depends:-      text >= 0.11 && < 0.12-  else-    hs-source-dirs: hs/text-0.10-    build-depends:-      text >= 0.7 && < 0.11-   exposed-modules:     Data.Enumerator-    Data.Enumerator.IO+    Data.Enumerator.Binary     Data.Enumerator.Text+    Data.Enumerator.List+    Data.Enumerator.IO    other-modules:     Data.Enumerator.Util-    Data.Enumerator.Text.Compat
+ examples/cat.hs view
@@ -0,0 +1,123 @@+-----------------------------------------------------------------------------+-- |+-- Copyright: 2010 John Millikin+-- License: MIT+--+-- Maintainer: jmillikin@gmail.com+-- Portability: portable+--+-----------------------------------------------------------------------------+module Main (main) where+import Prelude as Prelude+import Control.Exception as E+import Control.Monad.IO.Class (liftIO)+import Data.Enumerator+import qualified Data.ByteString as B+import qualified Foreign as F+import System.IO+import System.IO.Error (isEOFError)+import System.Environment (getArgs)++-- The following definitions of 'enumHandle', 'enumFile', and 'iterHandle' are+-- copied from "Data.Enumerator.Binary", with additional comments so they're+-- easier to understand.++enumHandle :: Integer -- ^ Buffer size+           -> Handle+           -> Enumerator B.ByteString IO b+enumHandle bufferSize h = loop where+	intSize = fromInteger bufferSize+	+	-- If more input is required before the enumerator's iteratee can+	-- yield a result, feed it from the handle.+	loop (Continue k) = do+		-- While not strictly necessary to proper operation, catching+		-- exceptions here allows more unified exception handling when+		-- the enumerator/iteratee is run.+		eitherBytes <- liftIO $ E.try $ do+			+			-- The enumerator must function normally when the+			-- handle is something like a slow file, or network+			-- socket; if there's not enough data to fill the+			-- buffer yet, a partial read is returned.+			hasInput <- E.catch+				(hWaitForInput h (1))+				(\err -> if isEOFError err+					then return False+					else E.throwIO err)+			+			-- An EOF is represented by the empty bytestring+			if hasInput+				then B.hGetNonBlocking h intSize+				else return B.empty+			+		case eitherBytes of+			-- Interacting with the socket threw an IO error of+			-- some sort+			Left err -> throwError (err :: E.SomeException)+			+			-- The socket has reached EOF; pass control to the+			-- next enumerator+			Right bytes | B.null bytes -> continue k+			+			-- Bytes were read successfully; feed them to the+			-- iteratee and continue looping+			Right bytes -> k (Chunks [bytes]) >>== loop+	+	-- If a different step is received ('Error' or 'Yield'), just pass+	-- it through.+	loop step = returnI step++enumFile :: FilePath -> Enumerator B.ByteString IO b+enumFile path s = do+	-- Opening the file can be performed either inside or outside of the+	-- Iteratee. Inside allows exceptions to be caught and propagated+	-- through the 'Error' step constructor.+	eitherH <- liftIO (E.try (openBinaryFile path ReadMode))+	case eitherH of+		Left err -> throwError (err :: E.SomeException)+		Right h -> Iteratee $ finally+			(runIteratee (enumHandle 4096 h s))+			(hClose h)++-- 'iterHandle' is the opposite of 'enumHandle', in that it *writes to* a+-- handle instead of reading from it. An enumerator is a source, an iteratee+-- is a sink.+iterHandle :: Handle -> Iteratee B.ByteString IO ()++-- Most iteratees start in the 'Continue' state, as they need some+-- input before they can produce any value.+iterHandle h = continue step where+	+	-- This iteratee produces no value; its only purpose is its+	-- side-effects. When 'EOF' is received, it simply yields ().+	step EOF = yield () EOF+	+	-- When some chunks are received from the Enumeratee, they're written+	-- to the handle. Any exceptions are caught and reported, as in+	-- 'enumHandle'.+	step (Chunks bytes) = do+		eitherErr <- liftIO (E.try (mapM_ (B.hPut h) bytes))+		case eitherErr of+			Left err -> throwError (err :: E.SomeException)+			_ -> continue step++main :: IO ()+main = do+	-- Our example enumlates standard /bin/cat, where if the argument list+	-- is empty, data is echoed from stdin.+	args <- getArgs+	let enum = if null args+		then enumHandle 1 stdin+		else concatEnums (Prelude.map enumFile args)+	+	-- 'run' sends an EOF to an iteratee and returns its output, which+	-- is either a 'Yield' or an 'Error'.+	res <- run (enum $$ iterHandle stdout)+	+	-- Finally, 'run' has returned either an error or the iteratee's+	-- result. 'iterHandle' doesn't return a useful result, so as long+	-- as it succeeded the actual value is ignored.+	case res of+		Left err -> putStrLn $ "ERROR: " ++ show err+		Right _ -> return ()
+ examples/wc.hs view
@@ -0,0 +1,108 @@+-----------------------------------------------------------------------------+-- |+-- Copyright: 2010 John Millikin+-- License: MIT+--+-- Maintainer: jmillikin@gmail.com+-- Portability: portable+--+-----------------------------------------------------------------------------+module Main (main) where+import Data.Enumerator as E+import qualified Data.Enumerator.Binary as EB+import qualified Data.Enumerator.Text as ET+import qualified Data.ByteString as B+import qualified Data.ByteString.Char8 as B8+import qualified Data.Text as T++-- support imports+import Control.Exception as E+import Data.List+import Control.Monad (unless, forM_)+import System.IO+import System.Console.GetOpt+import System.Environment+import System.Exit++-- support wc modes -c (bytes), -m (characters), and -l (lines)++-- iterBytes simply counts how many bytes are in each chunk, accumulates this+-- count, and returns it when EOF is received++iterBytes :: Monad m => Iteratee B.ByteString m Integer+iterBytes = continue (step 0) where+	step acc EOF = yield acc EOF+	step acc (Chunks xs) = continue $ step $! Data.List.foldl' foldStep acc xs+	foldStep acc bytes = acc + toInteger (B.length bytes)++-- iterLines is similar, except it only counts newlines ('\n')+--+-- Because it's basically the same as 'iterBytes', we use it to demonstrate+-- the 'liftFoldL\'' helper function.++iterLines :: Monad m => Iteratee B.ByteString m Integer+iterLines = E.foldl' step 0 where+	step acc bytes = acc + countChar '\n' bytes+	countChar c = B8.foldl (\acc c' -> if c' == c then acc + 1 else acc) 0++-- iterChars is a bit more complicated. It has to decode the input (for now,+-- assuming UTF-8) before performing any counting. Leftover bytes, not part+-- of a valid UTF-8 character, are yielded as surplus+--+-- Note the use of joinI. 'ET.decode' is an enumeratee, which means it returns+-- an iteratee yielding an inner step. 'joinI' "collapses" an enumeratee's+-- return value, much as 'join' does to monadic values.++iterChars :: Monad m => Iteratee B.ByteString m Integer+iterChars = joinI (ET.decode ET.utf8 $$ count) where+	count = E.foldl' (\acc t -> acc + toInteger (T.length t)) 0++main :: IO ()+main = do+	(mode, files) <- getMode+	+	-- Exactly matching wc's output is too annoying, so this example+	-- will just print one line per file, and support counting at most+	-- one statistic per run+	let iter = case mode of+		OptionBytes -> iterBytes+		OptionLines -> iterLines+		OptionChars -> iterChars+	+	forM_ files $ \filename -> do+		putStr $ filename ++ ": "+		+		-- see cat.hs for commented implementation of 'Data.Enumerator.IO.enumFile'+		eitherStat <- run (EB.enumFile filename $$ iter)+		putStrLn $ case eitherStat of+			Left err -> "ERROR: " ++ show err+			Right stat -> show stat++-- uninteresting option parsing follows++data Option+	= OptionBytes+	| OptionChars+	| OptionLines++optionInfo :: [OptDescr Option]+optionInfo =+	[ Option ['c'] ["bytes"] (NoArg OptionBytes) "count bytes"+	, Option ['m'] ["chars"] (NoArg OptionChars) "count characters"+	, Option ['l'] ["lines"] (NoArg OptionLines) "count lines"+	]++usage :: String -> String+usage name = "Usage: " ++ name ++ " <MODE> [FILES]"++getMode :: IO (Option, [FilePath])+getMode = do+	args <- getArgs+	let (options, files, errors) = getOpt Permute optionInfo args+	unless (null errors && not (null options) && not (null files)) $ do+		name <- getProgName+		hPutStrLn stderr $ concat errors+		hPutStrLn stderr $ usageInfo (usage name) optionInfo+		exitFailure+	+	return (Prelude.head options, files)
hs/Data/Enumerator.hs view
@@ -1,3 +1,5 @@++ ----------------------------------------------------------------------------- -- | -- Module: Data.Enumerator@@ -7,105 +9,185 @@ -- Maintainer: jmillikin@gmail.com -- Portability: portable ----- An implementation of Oleg Kiselyov&#x2019;s left-fold enumerators+-- Core enumerator types, and some useful primitives. --+-- This module is intended to be imported qualified:+--+-- @+-- import qualified Data.Enumerator as E+-- @+-- -----------------------------------------------------------------------------+ module Data.Enumerator (-	  -- * Types++	-- * Core+	-- ** Types 	  Stream (..)-	, Step (..) 	, Iteratee (..)+	, Step (..) 	, Enumerator 	, Enumeratee-	  -- * Primitives-	  -- ** Combinators-	  -- | These are common patterns which occur whenever iteratees are-	  -- being defined.+ 	, returnI 	, yield 	, continue-	, throwError-	, catchError-	, liftI++	-- ** Operators 	, (>>==) 	, (==<<) 	, ($$) 	, (>==>) 	, (<==<)-	  -- ** Iteratees-	, run-	, run_-	, consume-	, isEOF-	, liftTrans-	, liftFoldL-	, liftFoldL'-	, liftFoldM-	, printChunks-	  -- ** Enumerators-	, enumEOF-	, enumList-	, concatEnums-	  -- ** Enumeratees-	, checkDone-	, checkDoneEx++	-- * Primitives++	-- ** Error handling+	, throwError+	, catchError++	-- ** Iteratees+	, Data.Enumerator.foldl+	, Data.Enumerator.foldl'+	, Data.Enumerator.foldM++	-- ** Enumerators+	, Data.Enumerator.iterate+	, iterateM+	, Data.Enumerator.repeat+	, repeatM+	, Data.Enumerator.replicate+	, replicateM+	, generateM++	-- ** Enumeratees 	, Data.Enumerator.map 	, Data.Enumerator.concatMap+	, Data.Enumerator.filter 	, Data.Enumerator.mapM-	, Data.Enumerator.sequence+	, concatMapM+	, Data.Enumerator.filterM++	-- ** Debugging+	, printChunks++	-- * Misc. utilities++	, concatEnums+ 	, joinI-	  -- * Parser combinators-	  -- | Oleg&#x2019;s original @IterateeM.hs@ includes some basic iteratees-	  -- for parsing, so this section ports them to the new interface. However,-	  -- in practice most parsing will be performed with enumerator-based-	  -- interfaces to existing parser libraries (such as Parsec or Attoparsec).-	, Data.Enumerator.head+	, joinE+	, Data.Enumerator.sequence+	, enumList++	, enumEOF+	, run+	, run_++	, checkDone+	, checkDoneEx++	, isEOF++	-- * Compatibility++	-- ** Obsolete functions+	, liftTrans+	, liftI 	, peek 	, Data.Enumerator.last 	, Data.Enumerator.length++	-- ** Deprecated aliases+	, Data.Enumerator.head 	, Data.Enumerator.drop 	, Data.Enumerator.dropWhile-	, span+	, Data.Enumerator.span 	, Data.Enumerator.break+	, Data.Enumerator.consume++	, liftFoldL+	, liftFoldL'+	, liftFoldM+ 	) where-import Data.List (genericDrop, genericLength, genericSplitAt)-import qualified Control.Exception as E++import qualified Prelude as Prelude+import Prelude hiding (++	concatMap,++	)+ import Data.Monoid (Monoid, mempty, mappend, mconcat)++import qualified Control.Exception as Exc++import Control.Monad.Trans.Class (MonadTrans, lift)+import Control.Monad.IO.Class (MonadIO, liftIO)+ import qualified Control.Applicative as A-import Control.Monad (liftM, ap)-import qualified Control.Monad.IO.Class as MIO-import qualified Control.Monad.Trans.Class as MT-import qualified Data.List as DataList-import Control.Monad (foldM)-import Prelude hiding (span)-import qualified Prelude as Prelude--- | Not to be confused with types from the @Stream@ or--- @stream-fusion@ packages, a 'Stream' is a sequence of chunks--- generated by an 'Enumerator'. In contrast to Oleg&#x2019;s implementation,--- this stream does not support error handling -- errors encountered--- while generating a stream are reported in the 'Step' type instead.+import qualified Control.Monad as CM++import Data.List (foldl')++import Data.List (genericSplitAt)++import Data.List (genericLength)++import {-# SOURCE #-} qualified Data.Enumerator.List as EL+++-- | A 'Stream' is a sequence of chunks generated by an 'Enumerator'. ----- @(Chunks [])@ is used to indicate that a stream is still active, but+-- @('Chunks' [])@ is used to indicate that a stream is still active, but -- currently has no available data. Iteratees should ignore empty chunks.+ data Stream a 	= Chunks [a] 	| EOF 	deriving (Show, Eq)++instance Monad Stream where+	return = Chunks . return+	Chunks xs >>= f = mconcat (fmap f xs)+	EOF >>= _ = EOF++instance Functor Stream where+	fmap f (Chunks xs) = Chunks (fmap f xs)+	fmap _ EOF = EOF++instance A.Applicative Stream where+	pure = return+	(<*>) = CM.ap++instance Monoid (Stream a) where+	mempty = Chunks mempty+	mappend (Chunks xs) (Chunks ys) = Chunks (xs ++ ys)+	mappend _ _ = EOF+ data Step a m b+ 	-- | The 'Iteratee' is capable of accepting more input. Note that more input 	-- is not necessarily required; the 'Iteratee' might be able to generate a 	-- value immediately if it receives 'EOF'.+ 	= Continue (Stream a -> Iteratee a m b) 	-	-- | The 'Iteratee' has received enough input to generate a result.-	-- Included in this value is left-over input, which can be passed to++	-- | The 'Iteratee' cannot receive any more input, and has generated a+	-- result. Included in this value is left-over input, which can be passed to 	-- composed 'Iteratee's.+ 	| Yield b (Stream a) 	+ 	-- | The 'Iteratee' encountered an error which prevents it from proceeding 	-- further.-	| Error E.SomeException +	| Error Exc.SomeException++ -- | The primary data type for this library, which consumes -- input from a 'Stream' until it either generates a value or encounters -- an error. Rather than requiring all input at once, an iteratee will@@ -115,9 +197,31 @@ -- passed to the continuation, the iteratee returns the next step: -- 'Continue' for more data, 'Yield' when it's finished, or 'Error' to -- abort processing.+ newtype Iteratee a m b = Iteratee 	{ runIteratee :: m (Step a m b) 	}+++-- | @returnI step = 'Iteratee' (return step)@++returnI :: Monad m => Step a m b -> Iteratee a m b+returnI step = Iteratee (return step)+++-- | @yield x extra = 'returnI' ('Yield' x extra)@++yield :: Monad m => b -> Stream a -> Iteratee a m b+yield x extra = returnI (Yield x extra)+++-- | @continue k = 'returnI' ('Continue' k)@++continue :: Monad m => (Stream a -> Iteratee a m b)+         -> Iteratee a m b+continue k = returnI (Continue k)++ -- | While 'Iteratee's consume data, enumerators generate it. Since -- @'Iteratee'@ is an alias for @m ('Step' a m b)@, 'Enumerator's can -- be considered step transformers of type@@ -127,321 +231,607 @@ -- random generator, etc). They feed chunks into an 'Iteratee' until the -- source runs out of data (triggering 'EOF') or the iteratee finishes -- processing ('Yield's a value).+ type Enumerator a m b = Step a m b -> Iteratee a m b + -- | In cases where an enumerator acts as both a source and sink, the resulting -- type is named an 'Enumeratee'. Enumeratees have two input types, -- &#x201c;outer a&#x201d; (@aOut@) and &#x201c;inner a&#x201d; (@aIn@).-type Enumeratee aOut aIn m b = Step aIn m b -> Iteratee aOut m (Step aIn m b)-instance Monoid (Stream a) where-	mempty = Chunks mempty-	mappend (Chunks xs) (Chunks ys) = Chunks $ mappend xs ys-	mappend _ _ = EOF -instance Functor Stream where-	fmap f (Chunks xs) = Chunks $ fmap f xs-	fmap _ EOF = EOF+type Enumeratee ao ai m b = Step ai m b+          -> Iteratee ao m (Step ai m b) -instance Monad Stream where-	return = Chunks . return-	Chunks xs >>= f = mconcat $ fmap f xs-	EOF >>= _ = EOF-instance Monad m => Monad (Iteratee a m) where-	return x = Iteratee . return $ Yield x $ Chunks []-	{-# INLINE return #-}-	-	m >>= f = Iteratee $ runIteratee m >>=-		\r1 -> case r1 of-			Continue k -> return $ Continue ((>>= f) . k)-			Error err -> return $ Error err-			Yield x (Chunks []) -> runIteratee $ f x-			Yield x chunk -> runIteratee (f x) >>=-				\r2 -> case r2 of-					Continue k -> runIteratee $ k chunk-					Error err -> return $ Error err-					Yield x' _ -> return $ Yield x' chunk-instance Monad m => Functor (Iteratee a m) where-	fmap = liftM-	{-# INLINE fmap #-}+infixl 1 >>== -instance Monad m => A.Applicative (Iteratee a m) where-	pure = return-	{-# INLINE pure #-}-	-	(<*>) = ap-	{-# INLINE (<*>) #-}-instance MT.MonadTrans (Iteratee a) where-	lift m = Iteratee $ m >>= runIteratee . return-	{-# INLINE lift #-} -instance MIO.MonadIO m => MIO.MonadIO (Iteratee a m) where-	liftIO = MT.lift . MIO.liftIO-	{-# INLINE liftIO #-}--- | Lift an 'Iteratee' onto a monad transformer, re-wrapping the--- 'Iteratee'&#x2019;s inner monadic values.-liftTrans :: (Monad m, MT.MonadTrans t, Monad (t m)) =>-             Iteratee a m b -> Iteratee a (t m) b-liftTrans iter = Iteratee $ do-	step <- MT.lift $ runIteratee iter-	return $ case step of-		Yield x cs -> Yield x cs-		Error err -> Error err-		Continue k -> Continue (liftTrans . k)--- | @returnI x = Iteratee (return x)@-returnI :: Monad m => Step a m b -> Iteratee a m b-returnI = Iteratee . return-{-# INLINE returnI #-}---- | @yield x chunk = returnI (Yield x chunk)@-yield :: Monad m => b -> Stream a -> Iteratee a m b-yield x chunk = returnI (Yield x chunk)-{-# INLINE yield #-}---- | @continue k = returnI (Continue k)@-continue :: Monad m => (Stream a -> Iteratee a m b) -> Iteratee a m b-continue = returnI . Continue-{-# INLINE continue #-}---- | @throwError err = returnI (Error err)@-throwError :: (Monad m, E.Exception e) => e -> Iteratee a m b-throwError = returnI . Error . E.toException-{-# INLINE throwError #-}+-- | Equivalent to '(>>=)' for @m ('Step' a m b)@; allows 'Iteratee's with+-- different input types to be composed. --- | @liftI f = continue (returnI . f)@-liftI :: Monad m => (Stream a -> Step a m b) -> Iteratee a m b-liftI k = continue $ returnI . k-{-# INLINE liftI #-}-catchError :: Monad m => Iteratee a m b -> (E.SomeException -> Iteratee a m b) -> Iteratee a m b-catchError iter h = iter >>== step where-	step (Yield b as) = yield b as-	step (Error err) = h err-	step (Continue k) = continue (\stream -> k stream >>== step)-infixl 1 >>==+(>>==) :: Monad m+       => Iteratee a m b+       -> (Step a m b -> Iteratee a' m b')+       -> Iteratee a' m b'+i >>== f = Iteratee (runIteratee i >>= runIteratee . f) --- | Equivalent to (>>=), but allows 'Iteratee's with different input types--- to be composed.-(>>==) :: Monad m =>-	Iteratee a m b ->-	(Step a m b -> Iteratee a' m b') ->-	Iteratee a' m b'-i >>== f = Iteratee $ runIteratee i >>= runIteratee . f-{-# INLINE (>>==) #-} infixr 1 ==<< + -- | @(==\<\<) = flip (\>\>==)@-(==<<):: Monad m =>-	(Step a m b -> Iteratee a' m b') ->-	Iteratee a m b ->-	Iteratee a' m b'++(==<<) :: Monad m+       => (Step a m b -> Iteratee a' m b')+       -> Iteratee a m b+       -> Iteratee a' m b' (==<<) = flip (>>==)-{-# INLINE (==<<) #-}+ infixr 0 $$ + -- | @($$) = (==\<\<)@ -- -- This might be easier to read when passing a chain of iteratees to an -- enumerator.-($$):: Monad m =>-	(Step a m b -> Iteratee a' m b') ->-	Iteratee a m b ->-	Iteratee a' m b'+--+-- Since: 0.1.1++($$) :: Monad m+     => (Step a m b -> Iteratee a' m b')+     -> Iteratee a m b+     -> Iteratee a' m b' ($$) = (==<<)-{-# INLINE ($$) #-}+ infixr 1 >==> + -- | @(>==>) e1 e2 s = e1 s >>== e2@-(>==>) :: Monad m =>-	Enumerator a m b ->-	(Step a m b -> Iteratee a' m b') ->-	Step a m b ->-	Iteratee a' m b'+--+-- Since: 0.1.1++(>==>) :: Monad m+       => Enumerator a m b+       -> (Step a m b -> Iteratee a' m b')+       -> Step a m b+       -> Iteratee a' m b' (>==>) e1 e2 s = e1 s >>== e2-{-# INLINE (>==>) #-}+ infixr 1 <==< + -- | @(\<==\<) = flip (>==>)@-(<==<) :: Monad m =>-	(Step a m b -> Iteratee a' m b') ->-	Enumerator a m b ->-	Step a m b ->-	Iteratee a' m b'+--+-- Since: 0.1.1++(<==<) :: Monad m+       => (Step a m b -> Iteratee a' m b')+       -> Enumerator a m b+       -> Step a m b+       -> Iteratee a' m b' (<==<) = flip (>==>)-{-# INLINE (<==<) #-}--- | Consume all input until 'EOF', then return consumed input as a list.-consume :: Monad m => Iteratee a m [a]-consume = liftI $ step id where-	step acc chunk = case chunk of-		Chunks [] -> Continue $ returnI . step acc-		Chunks xs -> Continue $ returnI . (step $ acc . (xs ++))-		EOF -> Yield (acc []) EOF--- | Return 'True' if the next 'Stream' is 'EOF'.-isEOF :: Monad m => Iteratee a m Bool-isEOF = liftI $ \c -> case c of-	EOF -> Yield True c-	_   -> Yield False c--- | Lifts a pure left fold into an iteratee.-liftFoldL :: Monad m => (b -> a -> b) -> b -> Iteratee a m b-liftFoldL f = liftI . step where-	step acc chunk = case chunk of-		Chunks [] -> Continue $ returnI . step acc-		Chunks xs -> Continue $ returnI . step (Prelude.foldl f acc xs)-		EOF -> Yield acc EOF--- | As 'liftFoldL', but strict in its accumulator.-liftFoldL' :: Monad m => (b -> a -> b) -> b -> Iteratee a m b-liftFoldL' f = liftI . step where-	fold = DataList.foldl' f-	step acc chunk = case chunk of-		Chunks [] -> Continue $ returnI . step acc-		Chunks xs -> Continue $ returnI . (step $! fold acc xs)-		EOF -> Yield acc EOF--- | Lifts a monadic left fold into an iteratee.-liftFoldM :: Monad m => (b -> a -> m b) -> b -> Iteratee a m b-liftFoldM f = continue . step where-	step acc chunk = case chunk of-		Chunks [] -> continue $ step acc-		Chunks xs -> Iteratee $ liftM (Continue . step) (foldM f acc xs)++instance Monad m => Monad (Iteratee a m) where+	return x = yield x (Chunks [])+	+	m >>= f = Iteratee $ runIteratee m >>=+		\r1 -> case r1 of+			Continue k -> return (Continue ((>>= f) . k))+			Error err -> return (Error err)+			Yield x (Chunks []) -> runIteratee (f x)+			Yield x extra -> runIteratee (f x) >>=+				\r2 -> case r2 of+					Continue k -> runIteratee (k extra)+					Error err -> return (Error err)+					Yield x' _ -> return (Yield x' extra)++instance MonadTrans (Iteratee a) where+	lift m = Iteratee (m >>= runIteratee . return)++instance MonadIO m => MonadIO (Iteratee a m) where+	liftIO = lift . liftIO++instance Monad m => Functor (Iteratee a m) where+	fmap = CM.liftM++instance Monad m => A.Applicative (Iteratee a m) where+	pure = return+	(<*>) = CM.ap+++-- | @throwError exc = 'returnI' ('Error' ('Exc.toException' exc))@++throwError :: (Monad m, Exc.Exception e) => e+           -> Iteratee a m b+throwError exc = returnI (Error (Exc.toException exc))+++-- | Runs the iteratee, and calls an exception handler if an 'Error' is+-- returned. By handling errors within the enumerator library, and requiring+-- all errors to be represented by 'Exc.SomeException', libraries with+-- varying error types can be easily composed.+--+-- Since: 0.1.1++catchError :: Monad m => Iteratee a m b+           -> (Exc.SomeException -> Iteratee a m b)+           -> Iteratee a m b+catchError iter h = iter >>== step where+	step (Yield b as) = yield b as+	step (Error err) = h err+	step (Continue k) = continue (\s -> k s >>== step)+++-- | Run the entire input stream through a pure left fold, yielding when+-- there is no more input.+--+-- Since: 0.4.5++foldl :: Monad m => (b -> a -> b) -> b+      -> Iteratee a m b+foldl step = continue . loop where+	fold = Prelude.foldl step+	loop acc stream = case stream of+		Chunks [] -> continue (loop acc)+		Chunks xs -> continue (loop (fold acc xs)) 		EOF -> yield acc EOF+++-- | Run the entire input stream through a pure strict left fold, yielding+-- when there is no more input.+--+-- Since: 0.4.5++foldl' :: Monad m => (b -> a -> b) -> b+       -> Iteratee a m b+foldl' step = continue . loop where+	fold = Data.List.foldl' step+	loop acc stream = case stream of+		Chunks [] -> continue (loop acc)+		Chunks xs -> continue (loop (fold acc xs))+		EOF -> yield acc EOF+++-- | Run the entire input stream through a monadic left fold, yielding+-- when there is no more input.+--+-- Since: 0.4.5++foldM :: Monad m => (b -> a -> m b) -> b+      -> Iteratee a m b+foldM step = continue . loop where+	fold acc = lift . CM.foldM step acc+	+	loop acc stream = case stream of+		Chunks [] -> continue (loop acc)+		Chunks xs -> fold acc xs >>= continue . loop+		EOF -> yield acc EOF+++-- | @iterate f x@ enumerates an infinite stream of repeated applications+-- of /f/ to /x/.+--+-- Analogous to 'Prelude.iterate'.+--+-- Since: 0.4.5++iterate :: Monad m => (a -> a) -> a -> Enumerator a m b+iterate f = loop where+	loop a (Continue k) = k (Chunks [a]) >>== loop (f a)+	loop _ step = returnI step+++-- | Similar to 'iterate', except the iteration function is monadic.+--+-- Since: 0.4.5++iterateM :: Monad m => (a -> m a) -> a+         -> Enumerator a m b+iterateM f base = loop (return base) where+	loop m_a (Continue k) = do+		a <- lift m_a+		k (Chunks [a]) >>== loop (f a)+	loop _ step = returnI step+++-- | Enumerates an infinite stream of the provided value.+--+-- Analogous to 'Prelude.repeat'.+--+-- Since: 0.4.5++repeat :: Monad m => a -> Enumerator a m b+repeat a = Data.Enumerator.iterate (const a) a+++-- | Enumerates an infinite stream by running the provided computation and+-- passing each result to the iteratee.+--+-- Since: 0.4.5++repeatM :: Monad m => m a -> Enumerator a m b+repeatM m_a step = do+	a <- lift m_a+	iterateM (const m_a) a step+++-- | @replicateM n m_x@ enumerates a stream of /n/ input elements; each+-- element is generated by running the input computation /m_x/ once.+--+-- Since: 0.4.5++replicateM :: Monad m => Integer -> m a+           -> Enumerator a m b+replicateM maxCount getNext = loop maxCount where+	loop 0 step = returnI step+	loop n (Continue k) = do+		next <- lift getNext+		k (Chunks [next]) >>== loop (n - 1)+	loop _ step = returnI step+++-- | @replicate n x = 'replicateM' n (return x)@+--+-- Analogous to 'Prelude.replicate'.+--+-- Since: 0.4.5++replicate :: Monad m => Integer -> a+          -> Enumerator a m b+replicate maxCount a = replicateM maxCount (return a)+++-- | Like 'repeatM', except the computation may terminate the stream by+-- returning 'Nothing'.+--+-- Since: 0.4.5++generateM :: Monad m => m (Maybe a)+          -> Enumerator a m b+generateM getNext = loop where+	loop (Continue k) = do+		next <- lift getNext+		case next of+			Nothing -> continue k+			Just x -> k (Chunks [x]) >>== loop+	loop step = returnI step+++-- | @concatMapM f@ applies /f/ to each input element and feeds the+-- resulting outputs to the inner iteratee.+--+-- Since: 0.4.5++concatMapM :: Monad m => (ao -> m [ai])+           -> Enumeratee ao ai m b+concatMapM f = checkDone (continue . step) where+	step k EOF = yield (Continue k) EOF+	step k (Chunks xs) = loop k xs+	+	loop k [] = continue (step k)+	loop k (x:xs) = do+		fx <- lift (f x)+		k (Chunks fx) >>==+			checkDoneEx (Chunks xs) (\k' -> loop k' xs)+++-- | @concatMap f = 'concatMapM' (return . f)@+--+-- Since: 0.4.3++concatMap :: Monad m => (ao -> [ai])+          -> Enumeratee ao ai m b+concatMap f = concatMapM (return . f)+++-- | @map f = 'concatMap' (\x -> 'Prelude.map' f [x])@++map :: Monad m => (ao -> ai)+    -> Enumeratee ao ai m b+map f = concatMap (\x -> Prelude.map f [x])+++-- | @filter p = 'concatMap' (\x -> 'Prelude.filter' p [x])@+--+-- Since: 0.4.5++filter :: Monad m => (a -> Bool)+       -> Enumeratee a a m b+filter p = concatMap (\x -> Prelude.filter p [x])+++-- | @mapM f = 'concatMapM' (\x -> 'Prelude.mapM' f [x])@+--+-- Since: 0.4.3++mapM :: Monad m => (ao -> m ai)+     -> Enumeratee ao ai m b+mapM f = concatMapM (\x -> Prelude.mapM f [x])+++-- | @filterM p = 'concatMapM' (\x -> 'CM.filterM' p [x])@+--+-- Since: 0.4.5++filterM :: Monad m => (a -> m Bool)+        -> Enumeratee a a m b+filterM p = concatMapM (\x -> CM.filterM p [x])+++-- | Print chunks as they're received from the enumerator, optionally+-- printing empty chunks.++printChunks :: (MonadIO m, Show a)+            => Bool -- ^ Print empty chunks+            -> Iteratee a m ()+printChunks printEmpty = continue loop where+	loop (Chunks xs) = do+		let hide = null xs && not printEmpty+		CM.unless hide (liftIO (print xs))+		continue loop+	+	loop EOF = do+		liftIO (putStrLn "EOF")+		yield () EOF+++-- | Compose a list of 'Enumerator's using @'(>>==)'@++concatEnums :: Monad m => [Enumerator a m b]+            -> Enumerator a m b+concatEnums = Prelude.foldl (>==>) returnI+++-- | 'joinI' is used to &#x201C;flatten&#x201D; 'Enumeratee's into an+-- 'Iteratee'.++joinI :: Monad m => Iteratee a m (Step a' m b)+      -> Iteratee a m b+joinI outer = outer >>= check where+	check (Continue k) = k EOF >>== \s -> case s of+		Continue _ -> error "joinI: divergent iteratee"+		_ -> check s+	check (Yield x _) = return x+	check (Error e) = throwError e+++-- | Flatten an enumerator/enumeratee pair into a single enumerator.++joinE :: Monad m+      => Enumerator ao m (Step ai m b)+      -> Enumeratee ao ai m b+      -> Enumerator ai m b+joinE enum enee s = Iteratee $ do+	step <- runIteratee (enumEOF $$ enum $$ enee s)+	case step of+		Error err -> return (Error err)+		Yield x _ -> return x+		Continue _ -> error "joinE: divergent iteratee"+++-- | Feeds outer input elements into the provided iteratee until it yields+-- an inner input, passes that to the inner iteratee, and then loops.++sequence :: Monad m => Iteratee ao m ai+         -> Enumeratee ao ai m b+sequence i = loop where+	loop = checkDone check+	check k = isEOF >>= \f -> if f+		then yield (Continue k) EOF+		else step k+	step k = i >>= \v -> k (Chunks [v]) >>== loop+++-- | @enumList n xs@ enumerates /xs/ as a stream, passing /n/ inputs per+-- chunk.+--+-- Primarily useful for testing and debugging.++enumList :: Monad m => Integer -> [a] -> Enumerator a m b+enumList n = loop where+	loop xs (Continue k) | not (null xs) = let+		(s1, s2) = genericSplitAt n xs+		in k (Chunks s1) >>== loop s2+	loop _ step = returnI step++ -- | Run an iteratee until it finishes, and return either the final value -- (if it succeeded) or the error (if it failed).-run :: Monad m => Iteratee a m b -> m (Either E.SomeException b)++run :: Monad m => Iteratee a m b+    -> m (Either Exc.SomeException b) run i = do 	mStep <- runIteratee $ enumEOF ==<< i 	case mStep of 		Error err -> return $ Left err 		Yield x _ -> return $ Right x 		Continue _ -> error "run: divergent iteratee"-run_ :: Monad m => Iteratee a m b -> m b-run_ i = run i >>= either E.throw return--- | Print chunks as they're received from the enumerator, optionally--- printing empty chunks.-printChunks :: (MIO.MonadIO m, Show a) => Bool -> Iteratee a m ()-printChunks printEmpty = continue step where-	step (Chunks []) | not printEmpty = continue step-	step (Chunks xs) = MIO.liftIO (print xs) >> continue step-	step EOF = MIO.liftIO (putStrLn "EOF") >> yield () EOF--- | The most primitive enumerator; simply sends 'EOF'. The iteratee must--- either yield a value or throw an error continuing receiving 'EOF' will--- not terminate with any useful value.+++-- | docs TODO+ enumEOF :: Monad m => Enumerator a m b enumEOF (Yield x _) = yield x EOF enumEOF (Error err) = throwError err enumEOF (Continue k) = k EOF >>== check where 	check (Continue _) = error "enumEOF: divergent iteratee" 	check s = enumEOF s--- | Another small, useful enumerator separates an input list into chunks,--- and sends them to the iteratee. This is useful for testing iteratees in pure--- code.-enumList :: Monad m => Integer -> [a] -> Enumerator a m b-enumList n xs (Continue k) | not (null xs) = k chunk >>== loop where-	(s1, s2) = genericSplitAt n xs-	chunk = Chunks s1-	loop = enumList n s2-enumList _ _ step = returnI step--- | Compose a list of 'Enumerator's using '(>>==)'-concatEnums :: Monad m => [Enumerator a m b] -> Enumerator a m b-concatEnums = foldl (>==>) returnI-{-# INLINE concatEnums #-}--- | 'joinI' is used to &#x201C;flatten&#x201D; 'Enumeratee's into an--- 'Iteratee'.-joinI :: Monad m => Iteratee a m (Step a' m b) -> Iteratee a m b-joinI outer = outer >>= check where-	check (Continue k) = k EOF >>== \s -> case s of-		Continue _ -> error "joinI: divergent iteratee"-		_ -> check s-	check (Yield x _) = return x-	check (Error e) = throwError e+++-- | Like 'run', except errors are converted to exceptions and thrown.+-- Primarily useful for small scripts or other simple cases.+--+-- Since: 0.4.1++run_ :: Monad m => Iteratee a m b -> m b+run_ i = run i >>= either Exc.throw return++ -- | A common pattern in 'Enumeratee' implementations is to check whether -- the inner 'Iteratee' has finished, and if so, to return its output. -- 'checkDone' passes its parameter a continuation if the 'Iteratee' -- can still consume input, or yields otherwise.+--+-- Since: 0.4.3+ checkDoneEx :: Monad m => 	Stream a' -> 	((Stream a -> Iteratee a m b) -> Iteratee a' m (Step a m b)) -> 	Enumeratee a' a m b-checkDoneEx extra _ (Yield x chunk) = returnI (Yield (Yield x chunk) extra)-checkDoneEx _ f (Continue k) = f k-checkDoneEx _ _ (Error err) = throwError err-{-# INLINE checkDoneEx #-}+checkDoneEx _     f (Continue k) = f k+checkDoneEx extra _ step         = yield step extra --- | @checkDone = checkDoneEx (Chunks [])@++-- | @checkDone = 'checkDoneEx' ('Chunks' [])@ -- -- Use this for enumeratees which do not have an input buffer.+ checkDone :: Monad m => 	((Stream a -> Iteratee a m b) -> Iteratee a' m (Step a m b)) -> 	Enumeratee a' a m b checkDone = checkDoneEx (Chunks [])-{-# INLINE checkDone #-}-map :: Monad m => (ao -> ai) -> Enumeratee ao ai m b-map f = loop where-	loop = checkDone $ continue . step-	step k EOF = yield (Continue k) EOF-	step k (Chunks []) = continue $ step k-	step k (Chunks xs) = k (Chunks (Prelude.map f xs)) >>== loop-concatMap :: Monad m => (ao -> [ai]) -> Enumeratee ao ai m b-concatMap f = loop where-	loop = checkDone $ continue . step-	step k EOF = yield (Continue k) EOF-	step k (Chunks []) = continue $ step k-	step k (Chunks xs) = k (Chunks (Prelude.concatMap f xs)) >>== loop-mapM :: Monad m => (ao -> m ai) -> Enumeratee ao ai m b-mapM f = checkDone (continue . step) where-	step k EOF = yield (Continue k) EOF-	step k (Chunks xs) = loop k xs-	-	loop k [] = continue (step k)-	loop k (x:xs) = do-		fx <- MT.lift (f x)-		k (Chunks [fx]) >>== checkDoneEx (Chunks xs) (\k' -> loop k' xs)-sequence :: Monad m => Iteratee ao m ai -> Enumeratee ao ai m b-sequence i = loop where-	loop = checkDone check-	check k = isEOF >>= \f -> if f-		then yield (Continue k) EOF-		else step k-	step k = i >>= \v -> k (Chunks [v]) >>== loop-head :: Monad m => Iteratee a m (Maybe a)-head = liftI step where-	step (Chunks []) = Continue $ returnI . step-	step (Chunks (x:xs)) = Yield (Just x) (Chunks xs)-	step EOF = Yield Nothing EOF+++-- | docs TODO++isEOF :: Monad m => Iteratee a m Bool+isEOF = continue $ \s -> case s of+	EOF -> yield True s+	_ -> yield False s+++-- | Lift an 'Iteratee' onto a monad transformer, re-wrapping the+-- 'Iteratee'&#x2019;s inner monadic values.+--+-- Since: 0.1.1++liftTrans :: (Monad m, MonadTrans t, Monad (t m)) =>+             Iteratee a m b -> Iteratee a (t m) b+liftTrans iter = Iteratee $ do+	step <- lift (runIteratee iter)+	return $ case step of+		Yield x cs -> Yield x cs+		Error err -> Error err+		Continue k -> Continue (liftTrans . k)++{-# DEPRECATED liftI+     "Use 'Data.Enumerator.continue' instead" #-}++-- | Deprecated in 0.4.5: use 'Data.Enumerator.continue' instead++liftI :: Monad m => (Stream a -> Step a m b)+      -> Iteratee a m b+liftI k = continue (returnI . k)+++-- | Peek at the next element in the stream, or 'Nothing' if the stream+-- has ended.+ peek :: Monad m => Iteratee a m (Maybe a)-peek = liftI step where-	step (Chunks []) = Continue $ returnI . step-	step chunk@(Chunks (x:_)) = Yield (Just x) chunk-	step chunk = Yield Nothing chunk+peek = continue loop where+	loop (Chunks []) = continue loop+	loop chunk@(Chunks (x:_)) = yield (Just x) chunk+	loop EOF = yield Nothing EOF+++-- | Get the last element in the stream, or 'Nothing' if the stream+-- has ended.+--+-- Consumes the entire stream.+ last :: Monad m => Iteratee a m (Maybe a)-last = liftI $ step Nothing where-	step ret (Chunks xs) = let-		ret' = case xs of-			[] -> ret-			_  -> Just $ Prelude.last xs-		in Continue $ returnI . step ret'-	step ret EOF = Yield ret EOF+last = continue (loop Nothing) where+	loop ret (Chunks xs) = continue . loop $ case xs of+		[] -> ret+		_ -> Just (Prelude.last xs)+	loop ret EOF = yield ret EOF+++-- | Get how many elements remained in the stream.+--+-- Consumes the entire stream.+ length :: Monad m => Iteratee a m Integer-length = liftI $ step 0 where-	step n (Chunks xs) = Continue $ returnI . step (n + genericLength xs)-	step n EOF = Yield n EOF-drop :: Monad m => Integer -> Iteratee a m ()-drop 0 = return ()-drop n = liftI $ step n where-	step n' (Chunks xs)-		| len xs < n' = Continue $ returnI . step (n' - len xs)-		| otherwise   = Yield () $ Chunks $ genericDrop n' xs-	step _ EOF = Yield () EOF+length = continue (loop 0) where 	len = genericLength+	loop n (Chunks xs) = continue (loop (n + len xs))+	loop n EOF = yield n EOF++{-# DEPRECATED head+     "Use 'Data.Enumerator.List.head' instead" #-}++-- | Deprecated in 0.4.5: use 'Data.Enumerator.List.head' instead++head :: Monad m => Iteratee a m (Maybe a)+head = EL.head++{-# DEPRECATED drop+     "Use 'Data.Enumerator.List.drop' instead" #-}++-- | Deprecated in 0.4.5: use 'Data.Enumerator.List.drop' instead++drop :: Monad m => Integer -> Iteratee a m ()+drop = EL.drop++{-# DEPRECATED dropWhile+     "Use 'Data.Enumerator.List.dropWhile' instead" #-}++-- | Deprecated in 0.4.5: use 'Data.Enumerator.List.dropWhile' instead+ dropWhile :: Monad m => (a -> Bool) -> Iteratee a m ()-dropWhile p = liftI step where-	step (Chunks xs) = case Prelude.dropWhile p xs of-		[] -> Continue $ returnI . step-		xs' -> Yield () $ Chunks xs'-	step EOF = Yield () EOF+dropWhile = EL.dropWhile++{-# DEPRECATED span+     "Use 'Data.Enumerator.List.takeWhile' instead" #-}++-- | Deprecated in 0.4.5: use 'Data.Enumerator.List.takeWhile' instead+ span :: Monad m => (a -> Bool) -> Iteratee a m [a]-span f = liftI $ step [] where-	step acc (Chunks xs) = case Prelude.span f xs of-		(_, []) -> Continue $ returnI . step (acc ++ xs)-		(head', tail') -> Yield (acc ++ head') (Chunks tail')-	step acc EOF = Yield acc EOF+span = EL.takeWhile --- | @break p = 'span' (not . p)@+{-# DEPRECATED break+     "Use 'Data.Enumerator.List.takeWhile' instead" #-}++-- | Deprecated in 0.4.5: use 'Data.Enumerator.List.takeWhile' instead+ break :: Monad m => (a -> Bool) -> Iteratee a m [a]-break p = span $ not . p+break p = EL.takeWhile (not . p)++{-# DEPRECATED consume+     "Use 'Data.Enumerator.List.consume' instead" #-}++-- | Deprecated in 0.4.5: use 'Data.Enumerator.List.consume' instead++consume :: Monad m => Iteratee a m [a]+consume = EL.consume++{-# DEPRECATED liftFoldL+     "Use 'Data.Enumerator.foldl' instead" #-}++-- | Deprecated in 0.4.5: use 'Data.Enumerator.foldl' instead+--+-- Since: 0.1.1++liftFoldL :: Monad m => (b -> a -> b) -> b+          -> Iteratee a m b+liftFoldL = Data.Enumerator.foldl++{-# DEPRECATED liftFoldL'+     "Use 'Data.Enumerator.foldl' ' instead" #-}++-- | Deprecated in 0.4.5: use 'Data.Enumerator.foldl'' instead+--+-- Since: 0.1.1++liftFoldL' :: Monad m => (b -> a -> b) -> b+           -> Iteratee a m b+liftFoldL' = Data.Enumerator.foldl'++{-# DEPRECATED liftFoldM+     "Use 'Data.Enumerator.foldM' instead" #-}++-- | Deprecated in 0.4.5: use 'Data.Enumerator.foldM' instead+--+-- Since: 0.1.1++liftFoldM :: Monad m => (b -> a -> m b) -> b+          -> Iteratee a m b+liftFoldM = Data.Enumerator.foldM
+ hs/Data/Enumerator.hs-boot view
@@ -0,0 +1,11 @@++module Data.Enumerator where+import qualified Control.Exception as Exc+data Stream a+data Step a m b+	= Continue (Stream a -> Iteratee a m b)+	| Yield b (Stream a)+	| Error Exc.SomeException+newtype Iteratee a m b = Iteratee+	{ runIteratee :: m (Step a m b)+	}
+ hs/Data/Enumerator/Binary.hs view
@@ -0,0 +1,254 @@+++-----------------------------------------------------------------------------+-- |+-- Module: Data.Enumerator.Binary+-- Copyright: 2010 John Millikin+-- License: MIT+--+-- Maintainer: jmillikin@gmail.com+-- Portability: portable+--+-- This module is intended to be imported qualified:+--+-- @+-- import qualified Data.Enumerator.Binary as EB+-- @+--+-- Since: 0.4.5+--+-----------------------------------------------------------------------------++module Data.Enumerator.Binary (++	  -- * Binary IO+	  enumHandle+	, enumFile+	, iterHandle++	-- * List analogues+	, Data.Enumerator.Binary.head+	, Data.Enumerator.Binary.drop+	, Data.Enumerator.Binary.dropWhile+	, Data.Enumerator.Binary.take+	, Data.Enumerator.Binary.takeWhile+	, Data.Enumerator.Binary.consume+	, require+	, isolate++	) where+import Prelude hiding (head, drop, takeWhile)+import Data.Enumerator hiding (head, drop)+import qualified Data.ByteString as B++import Data.Enumerator.Util (tryStep)+import qualified Control.Exception as Exc+import Control.Monad.IO.Class (MonadIO)+import qualified System.IO as IO+import System.IO.Error (isEOFError)++import Data.Word (Word8)+import qualified Data.ByteString.Lazy as BL+++-- | Read bytes (in chunks of the given buffer size) from the handle, and+-- stream them to an 'Iteratee'. If an exception occurs during file IO,+-- enumeration will stop and 'Error' will be returned. Exceptions from the+-- iteratee are not caught.+--+-- This enumerator blocks until at least one byte is available from the+-- handle, and might read less than the maximum buffer size in some+-- cases.+--+-- The handle should be opened with no encoding, and in 'IO.ReadMode' or+-- 'IO.ReadWriteMode'.++enumHandle :: MonadIO m+           => Integer -- ^ Buffer size+           -> IO.Handle+           -> Enumerator B.ByteString m b+enumHandle bufferSize h = loop where+	loop (Continue k) = withBytes $ \bytes ->+		if B.null bytes+			then continue k+			else k (Chunks [bytes]) >>== loop+	+	loop step = returnI step+	+	intSize = fromInteger bufferSize+	withBytes = tryStep $ do+		hasInput <- Exc.catch+			(IO.hWaitForInput h (-1))+			(\err -> if isEOFError err+				then return False+				else Exc.throwIO err)+		if hasInput+			then B.hGetNonBlocking h intSize+			else return B.empty+++-- | Opens a file path in binary mode, and passes the handle to 'enumHandle'.+-- The file will be closed when the 'Iteratee' finishes.++enumFile :: FilePath -> Enumerator B.ByteString IO b+enumFile path = enum where+	withHandle = tryStep (IO.openBinaryFile path IO.ReadMode)+	enum step = withHandle $ \h -> do+		Iteratee $ Exc.finally+			(runIteratee (enumHandle 4096 h step))+			(IO.hClose h)+++-- | Read bytes from a stream and write them to a handle. If an exception+-- occurs during file IO, enumeration will stop and 'Error' will be+-- returned.+--+-- The handle should be opened with no encoding, and in 'IO.WriteMode' or+-- 'IO.ReadWriteMode'.++iterHandle :: MonadIO m => IO.Handle+           -> Iteratee B.ByteString m ()+iterHandle h = continue step where+	step EOF = yield () EOF+	step (Chunks []) = continue step+	step (Chunks bytes) = let+		put = mapM_ (B.hPut h) bytes+		in tryStep put (\_ -> continue step)++toChunks :: BL.ByteString -> Stream B.ByteString+toChunks = Chunks . BL.toChunks+++-- | Get the next byte from the stream, or 'Nothing' if the stream has+-- ended.+--+-- Since: 0.4.5++head :: Monad m => Iteratee B.ByteString m (Maybe Word8)+head = continue loop where+	loop (Chunks xs) = case BL.uncons (BL.fromChunks xs) of+		Just (char, extra) -> yield (Just char) (toChunks extra)+		Nothing -> head+	loop EOF = yield Nothing EOF+++-- | @drop n@ ignores /n/ bytes of input from the stream.+--+-- Since: 0.4.5++drop :: Monad m => Integer -> Iteratee B.ByteString m ()+drop n | n <= 0 = return ()+drop n = continue (loop n) where+	loop n' (Chunks xs) = iter where+		lazy = BL.fromChunks xs+		len = toInteger (BL.length lazy)+		iter = if len < n'+			then drop (n' - len)+			else yield () (toChunks (BL.drop (fromInteger n') lazy))+	loop _ EOF = yield () EOF+++-- | @dropWhile p@ ignores input from the stream until the first byte which+-- does not match the predicate.+--+-- Since: 0.4.5++dropWhile :: Monad m => (Word8 -> Bool) -> Iteratee B.ByteString m ()+dropWhile p = continue loop where+	loop (Chunks xs) = iter where+		lazy = BL.dropWhile p (BL.fromChunks xs)+		iter = if BL.null lazy+			then continue loop+			else yield () (toChunks lazy)+	loop EOF = yield () EOF+++-- | @take n@ extracts the next /n/ bytes from the stream, as a lazy+-- ByteString.+--+-- Since: 0.4.5++take :: Monad m => Integer -> Iteratee B.ByteString m BL.ByteString+take n | n <= 0 = return BL.empty+take n = continue (loop id n) where+	loop acc n' (Chunks xs) = iter where+		lazy = BL.fromChunks xs+		len = toInteger (BL.length lazy)+		+		iter = if len < n'+			then continue (loop (acc . (BL.append lazy)) (n' - len))+			else let+				(xs', extra) = BL.splitAt (fromInteger n') lazy+				in yield (acc xs') (toChunks extra)+	loop acc _ EOF = yield (acc BL.empty) EOF+++-- | @takeWhile p@ extracts input from the stream until the first byte which+-- does not match the predicate.+--+-- Since: 0.4.5++takeWhile :: Monad m => (Word8 -> Bool) -> Iteratee B.ByteString m BL.ByteString+takeWhile p = continue (loop id) where+	loop acc (Chunks []) = continue (loop acc)+	loop acc (Chunks xs) = iter where+		lazy = BL.fromChunks xs+		(xs', extra) = BL.span p lazy+		iter = if BL.null extra+			then continue (loop (acc . (BL.append lazy)))+			else yield (acc xs') (toChunks extra)+	loop acc EOF = yield (acc BL.empty) EOF+++-- | Read all remaining input from the stream, and return as a lazy+-- ByteString.+--+-- Since: 0.4.5++consume :: Monad m => Iteratee B.ByteString m BL.ByteString+consume = continue (loop id) where+	loop acc (Chunks []) = continue (loop acc)+	loop acc (Chunks xs) = iter where+		lazy = BL.fromChunks xs+		iter = continue (loop (acc . (BL.append lazy)))+	loop acc EOF = yield (acc BL.empty) EOF+++-- | @require n@ buffers input until at least /n/ bytes are available, or+-- throws an error if the stream ends early.+--+-- Since: 0.4.5++require :: Monad m => Integer -> Iteratee B.ByteString m ()+require n | n <= 0 = return ()+require n = continue (loop id n) where+	loop acc n' (Chunks xs) = iter where+		lazy = BL.fromChunks xs+		len = toInteger (BL.length lazy)+		iter = if len < n'+			then continue (loop (acc . (BL.append lazy)) (n' - len))+			else yield () (toChunks (acc lazy))+	loop _ _ EOF = throwError (Exc.ErrorCall "require: Unexpected EOF")+++-- | @isolate n@ reads at most /n/ bytes from the stream, and passes them+-- to its iteratee. If the iteratee finishes early, bytes continue to be+-- consumed from the outer stream until /n/ have been consumed.+--+-- Since: 0.4.5++isolate :: Monad m => Integer -> Enumeratee B.ByteString B.ByteString m b+isolate n step | n <= 0 = return step+isolate n (Continue k) = continue loop where+	loop (Chunks []) = continue loop+	loop (Chunks xs) = iter where+		lazy = BL.fromChunks xs+		len = toInteger (BL.length lazy)+		+		iter = if len <= n+			then k (Chunks xs) >>== isolate (n - len)+			else let+				(s1, s2) = BL.splitAt (fromInteger n) lazy+				in k (toChunks s1) >>== (\step -> yield step (toChunks s2))+	loop EOF = k EOF >>== (\step -> yield step EOF)+isolate n step = drop n >> return step
hs/Data/Enumerator/IO.hs view
@@ -1,3 +1,5 @@++ ----------------------------------------------------------------------------- -- | -- Module: Data.Enumerator.IO@@ -7,71 +9,47 @@ -- Maintainer: jmillikin@gmail.com -- Portability: portable ----- Enumerator-based IO+-- Deprecated in 0.4.5: use "Data.Enumerator.Binary" instead -- -----------------------------------------------------------------------------+ module Data.Enumerator.IO+	{-# DEPRECATED+	     "Use 'Data.Enumerator.Binary' instead" #-} 	( enumHandle 	, enumFile 	, iterHandle 	) where-import Data.Enumerator-import Data.Enumerator.Util+import qualified Data.Enumerator as E+import qualified Data.Enumerator.Binary as EB import Control.Monad.IO.Class (MonadIO)-import qualified Control.Exception as E import qualified Data.ByteString as B import qualified System.IO as IO-import System.IO.Error (isEOFError)--- | Read bytes (in chunks of the given buffer size) from the handle, and--- stream them to an 'Iteratee'. If an exception occurs during file IO,--- enumeration will stop and 'Error' will be returned. Exceptions from the--- iteratee are not caught.------ This enumerator blocks until at least one byte is available from the--- handle, and might read less than the maximum buffer size in some--- cases.------ The handle should be opened with no encoding, and in 'IO.ReadMode' or--- 'IO.ReadWriteMode'.++{-# DEPRECATED enumHandle+     "Use 'Data.Enumerator.Binary.enumHandle' instead" #-}++-- | Deprecated in 0.4.5: use 'Data.Enumerator.Binary.enumHandle' instead+ enumHandle :: MonadIO m-           => Integer -- ^ Buffer size+           => Integer            -> IO.Handle-           -> Enumerator B.ByteString m b-enumHandle bufferSize h = Iteratee . loop where-	loop (Continue k) = withBytes $ \bytes -> if B.null bytes-		then return $ Continue k-		else runIteratee (k (Chunks [bytes])) >>= loop-	-	loop step = return step-	-	intSize = fromInteger bufferSize-	withBytes = tryStep $ do-		hasInput <- E.catch-			(IO.hWaitForInput h (-1))-			(\err -> if isEOFError err-				then return False-				else E.throwIO err)-		if hasInput-			then B.hGetNonBlocking h intSize-			else return B.empty--- | Opens a file path in binary mode, and passes the handle to 'enumHandle'.--- The file will be closed when the 'Iteratee' finishes.-enumFile :: FilePath -> Enumerator B.ByteString IO b-enumFile path s = Iteratee io where-	withHandle = tryStep (IO.openBinaryFile path IO.ReadMode)-	io = withHandle $ \h -> E.finally-		(runIteratee (enumHandle 4096 h s))-		(IO.hClose h)--- | Read bytes from a stream and write them to a handle. If an exception--- occurs during file IO, enumeration will stop and 'Error' will be--- returned.------ The handle should be opened with no encoding, and in 'IO.WriteMode' or--- 'IO.ReadWriteMode'.-iterHandle :: MonadIO m => IO.Handle -> Iteratee B.ByteString m ()-iterHandle h = continue step where-	step EOF = yield () EOF-	step (Chunks []) = continue step-	step (Chunks bytes) = Iteratee io where-		put = mapM_ (B.hPut h) bytes-		io = tryStep put (\_ -> return $ Continue step)+           -> E.Enumerator B.ByteString m b+enumHandle = EB.enumHandle++{-# DEPRECATED enumFile+     "Use 'Data.Enumerator.Binary.enumFile' instead" #-}++-- | Deprecated in 0.4.5: use 'Data.Enumerator.Binary.enumFile' instead++enumFile :: FilePath -> E.Enumerator B.ByteString IO b+enumFile = EB.enumFile++{-# DEPRECATED iterHandle+     "Use 'Data.Enumerator.Binary.iterHandle' instead" #-}++-- | Deprecated in 0.4.5: use 'Data.Enumerator.Binary.iterHandle' instead++iterHandle :: MonadIO m => IO.Handle+           -> E.Iteratee B.ByteString m ()+iterHandle = EB.iterHandle
+ hs/Data/Enumerator/List.hs view
@@ -0,0 +1,154 @@+++-----------------------------------------------------------------------------+-- |+-- Module: Data.Enumerator.List+-- Copyright: 2010 John Millikin+-- License: MIT+--+-- Maintainer: jmillikin@gmail.com+-- Portability: portable+--+-- This module is intended to be imported qualified:+--+-- @+-- import qualified Data.Enumerator.List as EL+-- @+--+-- Since: 0.4.5+--+-----------------------------------------------------------------------------++module Data.Enumerator.List (++	  head+	, drop+	, dropWhile+	, take+	, takeWhile+	, consume+	, require+	, isolate++	) where+import Data.Enumerator hiding (consume, head, peek, drop, dropWhile)+import Control.Exception (ErrorCall(..))+import Prelude hiding (head, drop, dropWhile, take, takeWhile)+import qualified Data.List as L+++-- | Get the next element from the stream, or 'Nothing' if the stream has+-- ended.+--+-- Since: 0.4.5++head :: Monad m => Iteratee a m (Maybe a)+head = continue loop where+	loop (Chunks []) = head+	loop (Chunks (x:xs)) = yield (Just x) (Chunks xs)+	loop EOF = yield Nothing EOF+++-- | @drop n@ ignores /n/ input elements from the stream.+--+-- Since: 0.4.5++drop :: Monad m => Integer -> Iteratee a m ()+drop n | n <= 0 = return ()+drop n = continue (loop n) where+	loop n' (Chunks xs) = iter where+		len = L.genericLength xs+		iter = if len < n'+			then drop (n' - len)+			else yield () (Chunks (L.genericDrop n' xs))+	loop _ EOF = yield () EOF+++-- | @dropWhile p@ ignores input from the stream until the first element+-- which does not match the predicate.+--+-- Since: 0.4.5++dropWhile :: Monad m => (a -> Bool) -> Iteratee a m ()+dropWhile p = continue loop where+	loop (Chunks xs) = case L.dropWhile p xs of+		[] -> continue loop+		xs' -> yield () (Chunks xs')+	loop EOF = yield () EOF+++-- | @take n@ extracts the next /n/ elements from the stream, as a list.+--+-- Since: 0.4.5++take :: Monad m => Integer -> Iteratee a m [a]+take n | n <= 0 = return []+take n = continue (loop id n) where+	len = L.genericLength+	loop acc n' (Chunks xs)+		| len xs < n' = continue (loop (acc . (xs ++)) (n' - len xs))+		| otherwise   = let+			(xs', extra) = L.genericSplitAt n' xs+			in yield (acc xs') (Chunks extra)+	loop acc _ EOF = yield (acc []) EOF+++-- | @takeWhile p@ extracts input from the stream until the first element+-- which does not match the predicate.+--+-- Since: 0.4.5++takeWhile :: Monad m => (a -> Bool) -> Iteratee a m [a]+takeWhile p = continue (loop id) where+	loop acc (Chunks []) = continue (loop acc)+	loop acc (Chunks xs) = case Prelude.span p xs of+		(_, []) -> continue (loop (acc . (xs ++)))+		(xs', extra) -> yield (acc xs') (Chunks extra)+	loop acc EOF = yield (acc []) EOF+++-- | Read all remaining input elements from the stream, and return as a list.+--+-- Since: 0.4.5++consume :: Monad m => Iteratee a m [a]+consume = continue (loop id) where+	loop acc (Chunks []) = continue (loop acc)+	loop acc (Chunks xs) = continue (loop (acc . (xs ++)))+	loop acc EOF = yield (acc []) EOF+++-- | @require n@ buffers input until at least /n/ elements are available, or+-- throws an error if the stream ends early.+--+-- Since: 0.4.5++require :: Monad m => Integer -> Iteratee a m ()+require n | n <= 0 = return ()+require n = continue (loop id n) where+	len = L.genericLength+	loop acc n' (Chunks xs)+		| len xs < n' = continue (loop (acc . (xs ++)) (n' - len xs))+		| otherwise   = yield () (Chunks (acc xs))+	loop _ _ EOF = throwError (ErrorCall "require: Unexpected EOF")+++-- | @isolate n@ reads at most /n/ elements from the stream, and passes them+-- to its iteratee. If the iteratee finishes early, elements continue to be+-- consumed from the outer stream until /n/ have been consumed.+--+-- Since: 0.4.5++isolate :: Monad m => Integer -> Enumeratee a a m b+isolate n step | n <= 0 = return step+isolate n (Continue k) = continue loop where+	len = L.genericLength+	+	loop (Chunks []) = continue loop+	loop (Chunks xs)+		| len xs <= n = k (Chunks xs) >>== isolate (n - len xs)+		| otherwise = let+			(s1, s2) = L.genericSplitAt n xs+			in k (Chunks s1) >>== (\step -> yield step (Chunks s2))+	loop EOF = k EOF >>== (\step -> yield step EOF)+isolate n step = drop n >> return step
+ hs/Data/Enumerator/List.hs-boot view
@@ -0,0 +1,8 @@++module Data.Enumerator.List where+import {-# SOURCE #-} Data.Enumerator+head :: Monad m => Iteratee a m (Maybe a)+drop :: Monad m => Integer -> Iteratee a m ()+dropWhile :: Monad m => (a -> Bool) -> Iteratee a m ()+takeWhile :: Monad m => (a -> Bool) -> Iteratee a m [a]+consume :: Monad m => Iteratee a m [a]
hs/Data/Enumerator/Text.hs view
@@ -1,3 +1,5 @@++ ----------------------------------------------------------------------------- -- | -- Module: Data.Enumerator.Text@@ -7,123 +9,381 @@ -- Maintainer: jmillikin@gmail.com -- Portability: portable ----- Enumerator-based text IO+-- This module is intended to be imported qualified: --+-- @+-- import qualified Data.Enumerator.Text as ET+-- @+--+-- Since: 0.2+-- -----------------------------------------------------------------------------+ module Data.Enumerator.Text (-	  -- * Enumerators and iteratees++	  -- * Text IO 	  enumHandle 	, enumFile 	, iterHandle++	-- * List analogues+	, Data.Enumerator.Text.head+	, Data.Enumerator.Text.drop+	, Data.Enumerator.Text.dropWhile+	, Data.Enumerator.Text.take+	, Data.Enumerator.Text.takeWhile+	, Data.Enumerator.Text.consume+	, require+	, isolate+ 	  -- * Codecs 	, Codec 	, encode 	, decode+ 	, utf8+ 	, utf16_le 	, utf16_be+ 	, utf32_le 	, utf32_be+ 	, ascii+ 	, iso8859_1+ 	) where-import qualified Data.Enumerator.Text.Compat as TC-import Control.Monad.IO.Class (MonadIO)-import qualified Control.Exception as E+import qualified Prelude+import Prelude hiding (head, drop, takeWhile)+import Data.Enumerator hiding (head, drop) import qualified Data.Text as T-import qualified Data.Text.IO as T++import Data.Enumerator.Util (tryStep)+import qualified Data.Text.IO as TIO++import qualified Control.Exception as Exc+import Control.Monad.IO.Class (MonadIO) import qualified System.IO as IO import System.IO.Error (isEOFError)-import Control.Arrow (first)-import Data.Bits ((.&.))++import qualified Data.Text.Lazy as TL+ import qualified Data.ByteString as B+import Data.Enumerator.Util (tSpanBy, tlSpanBy, reprWord, reprChar)++import Control.Arrow (first)+import Data.Bits ((.&.), (.|.), shiftL)+import Data.Char (ord)+import Data.Word (Word8, Word16) import qualified Data.ByteString.Char8 as B8 import qualified Data.Text.Encoding as TE-import Data.Bits ((.|.), shiftL)-import Data.Word (Word16)-import Prelude as Prelude-import Numeric (showIntAtBase)-import Data.Char (toUpper, intToDigit, ord)-import Data.Word (Word8)++import Data.Maybe (catMaybes)+ import System.IO.Unsafe (unsafePerformIO)-import Data.Enumerator-import Data.Enumerator.Util++ -- | Read lines of text from the handle, and stream them to an 'Iteratee'. -- If an exception occurs during file IO, enumeration will stop and 'Error' -- will be returned. Exceptions from the iteratee are not caught. -- -- The handle should be opened with an appropriate text encoding, and -- in 'IO.ReadMode' or 'IO.ReadWriteMode'.-enumHandle :: MonadIO m => IO.Handle -> Enumerator T.Text m b-enumHandle h = Iteratee . loop where-	loop (Continue k) = withText $ \maybeText -> case maybeText of-		Nothing -> return $ Continue k-		Just text -> runIteratee (k (Chunks [text])) >>= loop+--+-- Since: 0.2++enumHandle :: MonadIO m => IO.Handle+           -> Enumerator T.Text m b+enumHandle h = loop where+	loop (Continue k) = withText $ \maybeText ->+		case maybeText of+			Nothing -> continue k+			Just text -> k (Chunks [text]) >>== loop 	-	loop step = return step-	withText = tryStep $ E.catch-		(Just `fmap` T.hGetLine h)+	loop step = returnI step+	withText = tryStep $ Exc.catch+		(Just `fmap` TIO.hGetLine h) 		(\err -> if isEOFError err 			then return Nothing-			else E.throwIO err)+			else Exc.throwIO err)++ -- | Opens a file path in text mode, and passes the handle to 'enumHandle'. -- The file will be closed when the 'Iteratee' finishes.+--+-- Since: 0.2+ enumFile :: FilePath -> Enumerator T.Text IO b-enumFile path s = Iteratee io where+enumFile path = enum where 	withHandle = tryStep (IO.openFile path IO.ReadMode)-	io = withHandle $ \h -> E.finally-		(runIteratee (enumHandle h s))+	enum step = withHandle $ \h -> Iteratee $ Exc.finally+		(runIteratee (enumHandle h step)) 		(IO.hClose h)++ -- | Read text from a stream and write it to a handle. If an exception -- occurs during file IO, enumeration will stop and 'Error' will be -- returned. -- -- The handle should be opened with an appropriate text encoding, and -- in 'IO.WriteMode' or 'IO.ReadWriteMode'.-iterHandle :: MonadIO m => IO.Handle -> Iteratee T.Text m ()+--+-- Since: 0.2++iterHandle :: MonadIO m => IO.Handle+           -> Iteratee T.Text m () iterHandle h = continue step where 	step EOF = yield () EOF 	step (Chunks []) = continue step-	step (Chunks chunks) = Iteratee io where-		put = mapM_ (T.hPutStr h) chunks-		io = tryStep put (\_ -> return $ Continue step)+	step (Chunks chunks) = let+		put = mapM_ (TIO.hPutStr h) chunks+		in tryStep put (\_ -> continue step)++toChunks :: TL.Text -> Stream T.Text+toChunks = Chunks . TL.toChunks+++-- | Get the next character from the stream, or 'Nothing' if the stream has+-- ended.+--+-- Since: 0.4.5++head :: Monad m => Iteratee T.Text m (Maybe Char)+head = continue loop where+	loop (Chunks xs) = case TL.uncons (TL.fromChunks xs) of+		Just (char, extra) -> yield (Just char) (toChunks extra)+		Nothing -> head+	loop EOF = yield Nothing EOF+++-- | @drop n@ ignores /n/ characters of input from the stream.+--+-- Since: 0.4.5++drop :: Monad m => Integer -> Iteratee T.Text m ()+drop n | n <= 0 = return ()+drop n = continue (loop n) where+	loop n' (Chunks xs) = iter where+		lazy = TL.fromChunks xs+		len = toInteger (TL.length lazy)+		iter = if len < n'+			then drop (n' - len)+			else yield () (toChunks (TL.drop (fromInteger n') lazy))+	loop _ EOF = yield () EOF+++-- | @dropWhile p@ ignores input from the stream until the first character+-- which does not match the predicate.+--+-- Since: 0.4.5++dropWhile :: Monad m => (Char -> Bool) -> Iteratee T.Text m ()+dropWhile p = continue loop where+	loop (Chunks xs) = iter where+		lazy = TL.dropWhile p (TL.fromChunks xs)+		iter = if TL.null lazy+			then continue loop+			else yield () (toChunks lazy)+	loop EOF = yield () EOF+++-- | @take n@ extracts the next /n/ characters from the stream, as a lazy+-- Text.+--+-- Since: 0.4.5++take :: Monad m => Integer -> Iteratee T.Text m TL.Text+take n | n <= 0 = return TL.empty+take n = continue (loop id n) where+	loop acc n' (Chunks xs) = iter where+		lazy = TL.fromChunks xs+		len = toInteger (TL.length lazy)+		+		iter = if len < n'+			then continue (loop (acc . (TL.append lazy)) (n' - len))+			else let+				(xs', extra) = TL.splitAt (fromInteger n') lazy+				in yield (acc xs') (toChunks extra)+	loop acc _ EOF = yield (acc TL.empty) EOF+++-- | @takeWhile p@ extracts input from the stream until the first character+-- which does not match the predicate.+--+-- Since: 0.4.5++takeWhile :: Monad m => (Char -> Bool) -> Iteratee T.Text m TL.Text+takeWhile p = continue (loop id) where+	loop acc (Chunks []) = continue (loop acc)+	loop acc (Chunks xs) = iter where+		lazy = TL.fromChunks xs+		(xs', extra) = tlSpanBy p lazy+		iter = if TL.null extra+			then continue (loop (acc . (TL.append lazy)))+			else yield (acc xs') (toChunks extra)+	loop acc EOF = yield (acc TL.empty) EOF+++-- | Read all remaining input from the stream, and return as a lazy+-- Text.+--+-- Since: 0.4.5++consume :: Monad m => Iteratee T.Text m TL.Text+consume = continue (loop id) where+	loop acc (Chunks []) = continue (loop acc)+	loop acc (Chunks xs) = iter where+		lazy = TL.fromChunks xs+		iter = continue (loop (acc . (TL.append lazy)))+	loop acc EOF = yield (acc TL.empty) EOF+++-- | @require n@ buffers input until at least /n/ characters are available,+-- or throws an error if the stream ends early.+--+-- Since: 0.4.5++require :: Monad m => Integer -> Iteratee T.Text m ()+require n | n <= 0 = return ()+require n = continue (loop id n) where+	loop acc n' (Chunks xs) = iter where+		lazy = TL.fromChunks xs+		len = toInteger (TL.length lazy)+		iter = if len < n'+			then continue (loop (acc . (TL.append lazy)) (n' - len))+			else yield () (toChunks (acc lazy))+	loop _ _ EOF = throwError (Exc.ErrorCall "require: Unexpected EOF")+++-- | @isolate n@ reads at most /n/ characters from the stream, and passes+-- them to its iteratee. If the iteratee finishes early, characters continue+-- to be consumed from the outer stream until /n/ have been consumed.+--+-- Since: 0.4.5++isolate :: Monad m => Integer -> Enumeratee T.Text T.Text m b+isolate n step | n <= 0 = return step+isolate n (Continue k) = continue loop where+	loop (Chunks []) = continue loop+	loop (Chunks xs) = iter where+		lazy = TL.fromChunks xs+		len = toInteger (TL.length lazy)+		+		iter = if len <= n+			then k (Chunks xs) >>== isolate (n - len)+			else let+				(s1, s2) = TL.splitAt (fromInteger n) lazy+				in k (toChunks s1) >>== (\step -> yield step (toChunks s2))+	loop EOF = k EOF >>== (\step -> yield step EOF)+isolate n step = drop n >> return step+ data Codec = Codec 	{ codecName :: T.Text-	, codecEncode :: [T.Text] -> Either E.SomeException [B.ByteString]-	, codecDecode :: B.ByteString -> Either E.SomeException (T.Text, B.ByteString)+	, codecEncode+		:: T.Text+		-> (B.ByteString, Maybe (Exc.SomeException, T.Text))+	, codecDecode+		:: B.ByteString+		-> (T.Text, Either+			(Exc.SomeException, B.ByteString)+			B.ByteString) 	}  instance Show Codec where 	showsPrec d c = showParen (d > 10) $ 		showString "Codec " . shows (codecName c)-encode :: Monad m => Codec -> Enumeratee T.Text B.ByteString m b-encode codec = loop where-	loop = checkDone $ continue . step+++-- | Convert text into bytes, using the provided codec. If the codec is+-- not capable of representing an input character, an error will be thrown.+--+-- Since: 0.2++encode :: Monad m => Codec+       -> Enumeratee T.Text B.ByteString m b+encode codec = checkDone (continue . step) where 	step k EOF = yield (Continue k) EOF-	step k (Chunks []) = continue $ step k-	step k (Chunks xs) = case codecEncode codec xs of-		Left err -> throwError err-		Right byteChunks -> k (Chunks byteChunks) >>== loop-decode :: Monad m => Codec -> Enumeratee B.ByteString T.Text m b-decode codec = loop B.empty where-	dec = codecDecode codec+	step k (Chunks xs) = loop k xs 	-	loop acc = checkDoneEx (Chunks [acc]) (continue . step acc)-	step acc k EOF = yield (Continue k) $ if B.null acc-		then EOF-		else Chunks [acc]-	step acc k (Chunks []) = continue $ step acc k-	step acc k (Chunks xs) = case dec (B.concat (acc:xs)) of-		Left err -> throwError err-		Right (text, extra) -> if T.null text-			then continue $ step extra k-			else k (Chunks [text]) >>== loop extra+	loop k [] = continue (step k)+	loop k (x:xs) = let+		(bytes, extra) = codecEncode codec x+		extraChunks = Chunks $ case extra of+			Nothing -> xs+			Just (_, text) -> text:xs+		+		checkError k' = case extra of+			Nothing -> loop k' xs+			Just (exc, _) -> throwError exc+		+		in if B.null bytes+			then checkError k+			else k (Chunks [bytes]) >>==+				checkDoneEx extraChunks checkError+++-- | Convert bytes into text, using the provided codec. If the codec is+-- not capable of decoding an input byte sequence, an error will be thrown.+--+-- Since: 0.2++decode :: Monad m => Codec+       -> Enumeratee B.ByteString T.Text m b+decode codec = checkDone (continue . step B.empty) where+	step _   k EOF = yield (Continue k) EOF+	step acc k (Chunks xs) = loop acc k xs+	+	loop acc k [] = continue (step acc k)+	loop acc k (x:xs) = let+		(text, extra) = codecDecode codec (B.append acc x)+		extraChunks = Chunks (either snd id extra : xs)+		+		checkError k' = case extra of+			Left (exc, _) -> throwError exc+			Right bytes -> loop bytes k' xs+		+		in if T.null text+			then checkError k+			else k (Chunks [text]) >>==+				checkDoneEx extraChunks checkError++byteSplits :: B.ByteString+           -> [(B.ByteString, B.ByteString)]+byteSplits bytes = loop (B.length bytes) where+	loop 0 = [(B.empty, bytes)]+	loop n = B.splitAt n bytes : loop (n - 1)++splitSlowly :: (B.ByteString -> T.Text)+            -> B.ByteString+            -> (T.Text, Either+            	(Exc.SomeException, B.ByteString)+            	B.ByteString)+splitSlowly dec bytes = valid where+	valid = firstValid (Prelude.map decFirst splits)+	splits = byteSplits bytes+	firstValid = Prelude.head . catMaybes+	tryDec = tryEvaluate . dec+	+	decFirst (a, b) = case tryDec a of+		Left _ -> Nothing+		Right text -> Just (text, case tryDec b of+			Left exc -> Left (exc, b)+			+			-- this case shouldn't occur, since splitSlowly+			-- is only called when parsing failed somewhere+			Right _ -> Right B.empty)+ utf8 :: Codec utf8 = Codec name enc dec where 	name = T.pack "UTF-8"-	enc = Right . Prelude.map TE.encodeUtf8-	dec = unsafeTryDec . splitBytes-	splitBytes bytes = loop 0 where+	enc text = (TE.encodeUtf8 text, Nothing)+	dec bytes = case splitQuickly bytes of+		Just (text, extra) -> (text, Right extra)+		Nothing -> splitSlowly TE.decodeUtf8 bytes++	splitQuickly bytes = loop 0 >>= maybeDecode where+ 		required x0 			| x0 .&. 0x80 == 0x00 = 1 			| x0 .&. 0xE0 == 0xC0 = 2@@ -131,47 +391,71 @@ 			| x0 .&. 0xF8 == 0xF0 = 4 			 			-- Invalid input; let Text figure it out-			| otherwise           = 1+			| otherwise           = 0+ 		maxN = B.length bytes 		-		loop n | n == maxN = (TE.decodeUtf8 bytes, B.empty)+		loop n | n == maxN = Just (TE.decodeUtf8 bytes, B.empty) 		loop n = let-			req = required $ B.index bytes n-			tooLong = first TE.decodeUtf8 $ B.splitAt n bytes+			req = required (B.index bytes n)+			tooLong = first TE.decodeUtf8 (B.splitAt n bytes) 			decodeMore = loop $! n + req-			in if req > maxN then tooLong else decodeMore+			in if req == 0+				then Nothing+				else if n + req > maxN+					then Just tooLong+					else decodeMore+ utf16_le :: Codec utf16_le = Codec name enc dec where 	name = T.pack "UTF-16-LE"-	enc = Right . Prelude.map TE.encodeUtf16LE-	dec = unsafeTryDec . splitBytes-	splitBytes bytes = loop 0 where+	enc text = (TE.encodeUtf16LE text, Nothing)+	dec bytes = case splitQuickly bytes of+		Just (text, extra) -> (text, Right extra)+		Nothing -> splitSlowly TE.decodeUtf16LE bytes++	splitQuickly bytes = maybeDecode (loop 0) where 		maxN = B.length bytes 		-		loop n |  n      == maxN = (TE.decodeUtf16LE bytes, B.empty)+		loop n |  n      == maxN = decodeAll 		       | (n + 1) == maxN = decodeTo n 		loop n = let-			req = utf16Required (B.index bytes 0) (B.index bytes 1)+			req = utf16Required+				(B.index bytes 0)+				(B.index bytes 1) 			decodeMore = loop $! n + req-			in if req > maxN then decodeTo n else decodeMore+			in if n + req > maxN+				then decodeTo n+				else decodeMore 		-		decodeTo n = first TE.decodeUtf16LE $ B.splitAt n bytes+		decodeTo n = first TE.decodeUtf16LE (B.splitAt n bytes)+		decodeAll = (TE.decodeUtf16LE bytes, B.empty)+ utf16_be :: Codec utf16_be = Codec name enc dec where 	name = T.pack "UTF-16-BE"-	enc = Right . Prelude.map TE.encodeUtf16BE-	dec = unsafeTryDec . splitBytes-	splitBytes bytes = loop 0 where+	enc text = (TE.encodeUtf16BE text, Nothing)+	dec bytes = case splitQuickly bytes of+		Just (text, extra) -> (text, Right extra)+		Nothing -> splitSlowly TE.decodeUtf16BE bytes++	splitQuickly bytes = maybeDecode (loop 0) where 		maxN = B.length bytes 		-		loop n |  n      == maxN = (TE.decodeUtf16BE bytes, B.empty)+		loop n |  n      == maxN = decodeAll 		       | (n + 1) == maxN = decodeTo n 		loop n = let-			req = utf16Required (B.index bytes 1) (B.index bytes 0)+			req = utf16Required+				(B.index bytes 1)+				(B.index bytes 0) 			decodeMore = loop $! n + req-			in if req > maxN then decodeTo n else decodeMore+			in if n + req > maxN+				then decodeTo n+				else decodeMore 		-		decodeTo n = first TE.decodeUtf16BE $ B.splitAt n bytes+		decodeTo n = first TE.decodeUtf16BE (B.splitAt n bytes)+		decodeAll = (TE.decodeUtf16BE bytes, B.empty)+ utf16Required :: Word8 -> Word8 -> Int utf16Required x0 x1 = required where 	required = if x >= 0xD800 && x <= 0xDBFF@@ -179,61 +463,85 @@ 		else 2 	x :: Word16 	x = (fromIntegral x1 `shiftL` 8) .|. fromIntegral x0+ utf32_le :: Codec utf32_le = Codec name enc dec where 	name = T.pack "UTF-32-LE"-	enc = Right . Prelude.map TE.encodeUtf32LE-	dec = unsafeTryDec . utf32SplitBytes TE.decodeUtf32LE-	+	enc text = (TE.encodeUtf32LE text, Nothing)+	dec bs = case utf32SplitBytes TE.decodeUtf32LE bs of+		Just (text, extra) -> (text, Right extra)+		Nothing -> splitSlowly TE.decodeUtf32LE bs  utf32_be :: Codec utf32_be = Codec name enc dec where 	name = T.pack "UTF-32-BE"-	enc = Right . Prelude.map TE.encodeUtf32BE-	dec = unsafeTryDec . utf32SplitBytes TE.decodeUtf32BE-utf32SplitBytes :: (B.ByteString -> a) -> B.ByteString -> (a, B.ByteString)-utf32SplitBytes dec bytes = (dec toDecode, extra) where+	enc text = (TE.encodeUtf32BE text, Nothing)+	dec bs = case utf32SplitBytes TE.decodeUtf32BE bs of+		Just (text, extra) -> (text, Right extra)+		Nothing -> splitSlowly TE.decodeUtf32BE bs++utf32SplitBytes :: (B.ByteString -> T.Text)+                -> B.ByteString+                -> Maybe (T.Text, B.ByteString)+utf32SplitBytes dec bytes = split where+	split = maybeDecode (dec toDecode, extra) 	len = B.length bytes 	lenExtra = mod len 4+	 	lenToDecode = len - lenExtra 	(toDecode, extra) = if lenExtra == 0 		then (bytes, B.empty) 		else B.splitAt lenToDecode bytes+ ascii :: Codec-ascii = Codec name (mapEither enc) dec where+ascii = Codec name enc dec where 	name = T.pack "ASCII"-	enc t = case TC.findBy (\c -> ord c > 0x7F) t of-		Nothing -> Right . B8.pack . T.unpack $ t-		Just c -> illegalEnc name c-	dec bytes = case B.find (\w -> w > 0x7F) bytes of-		Nothing -> Right (T.pack (B8.unpack bytes), B.empty)-		Just w -> illegalDec name w+	enc text = (bytes, extra) where+		(safe, unsafe) = tSpanBy (\c -> ord c <= 0x7F) text+		bytes = B8.pack (T.unpack safe)+		extra = if T.null unsafe+			then Nothing+			else Just (illegalEnc name (T.head unsafe), unsafe)+	+	dec bytes = (text, extra) where+		(safe, unsafe) = B.span (<= 0x7F) bytes+		text = T.pack (B8.unpack safe)+		extra = if B.null unsafe+			then Right B.empty+			else Left (illegalDec name (B.head unsafe), unsafe)+ iso8859_1 :: Codec-iso8859_1 = Codec name (mapEither enc) dec where+iso8859_1 = Codec name enc dec where 	name = T.pack "ISO-8859-1"-	enc t = case TC.findBy (\c -> ord c > 0xFF) t of-		Nothing -> Right . B8.pack . T.unpack $ t-		Just c -> illegalEnc name c-	dec bytes = Right (T.pack (B8.unpack bytes), B.empty)-illegalEnc :: T.Text -> Char -> Either E.SomeException a-illegalEnc name c = Left . E.toException . E.ErrorCall $ msg "" where-	len = Prelude.length-	pad str | len str < 4 = replicate (4 - len str) '0' ++ str-	        | otherwise      = str-	hex = "U+" ++ pad (showIntAtBase 16 (toUpper . intToDigit) (ord c) "")-	msg = (s "Codec " . shows name . s " can't encode character " . s hex)-	s = showString-illegalDec :: T.Text -> Word8 -> Either E.SomeException a-illegalDec name w = Left . E.toException . E.ErrorCall $ msg "" where-	len = Prelude.length-	pad str | len str < 2 = replicate (2 - len str) '0' ++ str-	        | otherwise      = str-	hex = "0x" ++ pad (showIntAtBase 16 (toUpper . intToDigit) w "")-	msg = (s "Codec " . shows name . s " can't decode byte " . s hex)-	s = showString-unsafeTryDec :: (a, b) -> Either E.SomeException (a, b)-unsafeTryDec (a, b) = unsafePerformIO $ do-	tried <- E.try $ E.evaluate a-	return $ case tried of-		Left err -> Left err-		Right _ -> Right (a, b)+	enc text = (bytes, extra) where+		(safe, unsafe) = tSpanBy (\c -> ord c <= 0xFF) text+		bytes = B8.pack (T.unpack safe)+		extra = if T.null unsafe+			then Nothing+			else Just (illegalEnc name (T.head unsafe), unsafe)+	+	dec bytes = (T.pack (B8.unpack bytes), Right B.empty)++illegalEnc :: T.Text -> Char -> Exc.SomeException+illegalEnc name c = Exc.toException . Exc.ErrorCall $+	concat [ "Codec "+	       , show name+	       , " can't encode character "+	       , reprChar c+	       ]++illegalDec :: T.Text -> Word8 -> Exc.SomeException+illegalDec name w = Exc.toException . Exc.ErrorCall $+	concat [ "Codec "+	       , show name+	       , " can't decode byte "+	       , reprWord w+	       ]++tryEvaluate :: a -> Either Exc.SomeException a+tryEvaluate = unsafePerformIO . Exc.try . Exc.evaluate++maybeDecode:: (a, b) -> Maybe (a, b)+maybeDecode (a, b) = case tryEvaluate a of+	Left _ -> Nothing+	Right _ -> Just (a, b)
hs/Data/Enumerator/Util.hs view
@@ -1,20 +1,43 @@++{-# LANGUAGE CPP #-} module Data.Enumerator.Util where import Data.Enumerator++import Data.Char (toUpper, intToDigit, ord)+import Data.Word (Word8)+import qualified Data.Text as T+import qualified Data.Text.Lazy as TL+ import Control.Monad.IO.Class (MonadIO, liftIO)-import qualified Control.Exception as E+import qualified Control.Exception as Exc+import Numeric (showIntAtBase) -tryStep :: MonadIO m => IO t -> (t -> m (Step a m b)) -> m (Step a m b)+tryStep :: MonadIO m => IO t -> (t -> Iteratee a m b) -> Iteratee a m b tryStep get io = do-	tried <- liftIO (E.try get)+	tried <- liftIO (Exc.try get) 	case tried of 		Right t -> io t-		Left err -> return $ Error err-{-# INLINE tryStep #-}+		Left err -> throwError (err :: Exc.SomeException) -mapEither :: (a -> Either e b) -> [a] -> Either e [b]-mapEither f = loop [] where-	loop acc [] = Right (reverse acc)-	loop acc (a:as) = case f a of-		Left err -> Left err-		Right b -> loop (b:acc) as-{-# INLINE mapEither #-}+pad0 :: Int -> String -> String+pad0 size str = padded where+	len = Prelude.length str+	padded = if len >= size+		then str+		else Prelude.replicate (size - len) '0' ++ str++reprChar :: Char -> String+reprChar c = "U+" ++ (pad0 4 (showIntAtBase 16 (toUpper . intToDigit) (ord c) ""))++reprWord :: Word8 -> String+reprWord w = "0x" ++ (pad0 2 (showIntAtBase 16 (toUpper . intToDigit) w ""))++tSpanBy  :: (Char -> Bool) -> T.Text -> (T.Text, T.Text)+tlSpanBy :: (Char -> Bool) -> TL.Text -> (TL.Text, TL.Text)+#if MIN_VERSION_text(0,11,0)+tSpanBy = T.span+tlSpanBy = TL.span+#else+tSpanBy = T.spanBy+tlSpanBy = TL.spanBy+#endif
− hs/text-0.10/Data/Enumerator/Text/Compat.hs
@@ -1,6 +0,0 @@-module Data.Enumerator.Text.Compat-	( findBy-	) where-import qualified Data.Text as T-findBy :: (Char -> Bool) -> T.Text -> Maybe Char-findBy = T.findBy
− hs/text-0.11/Data/Enumerator/Text/Compat.hs
@@ -1,6 +0,0 @@-module Data.Enumerator.Text.Compat-	( findBy-	) where-import qualified Data.Text as T-findBy :: (Char -> Bool) -> T.Text -> Maybe Char-findBy = T.find
readme.txt view
@@ -1,10 +1,10 @@ The source code for "enumerator" is literate. To build the library from scratch, install the "anansi" application and then run: -    anansi -o hs/ enumerator.anansi+    anansi -o hs/ src/enumerator.anansi  To generate the woven PDF, install NoWeb and then run: -    anansi -w -l latex-noweb -o enumerator.tex enumerator.anansi+    anansi -w -l latex-noweb -o enumerator.tex src/enumerator.anansi     xelatex enumerator.tex     xelatex enumerator.tex
+ scripts/cabal-dist view
@@ -0,0 +1,57 @@+#!/bin/bash+if [ ! -f 'enumerator.cabal' ]; then+	echo -n "Can't find enumerator.cabal; please run this script as"+	echo -n " ./scripts/cabal-dist from within the enumerator source"+	echo " directory"+	exit 1+fi++XZ=$(which xz)+XELATEX=$(which xelatex)+if [ -d 'cabal-dev' ]; then+	CABAL=$(which cabal-dev)+	PATH="$PATH:$PWD/cabal-dev/bin/"+else+	CABAL=$(which cabal)+fi++ANANSI=$(which anansi)+if [ -z "$ANANSI" ]; then+	echo "Can't find 'anansi' executable; make sure it exists on your "'$PATH'+	exit 1+fi++VERSION=$(awk '/^version:/{print $2}' enumerator.cabal)++echo "Building dist for enumerator-$VERSION using $CABAL"++rm -rf hs dist+anansi --noline -o hs src/enumerator.anansi || exit 1+$CABAL configure || exit 1+$CABAL build || exit 1+$CABAL sdist || exit 1+mv "dist/enumerator-$VERSION.tar.gz" "./enumerator_$VERSION.tar.gz"+if [ -n "$XZ" ]; then+  gzip -dfc "enumerator_$VERSION.tar.gz" > "enumerator_$VERSION.tar"+  xz -f -C sha256 -9 "enumerator_$VERSION.tar"+fi++if [ -n "$XELATEX" ]; then+  rm -f *.{aux,tex,idx,log,out,toc,pdf}+  anansi -w -l latex-noweb -o enumerator.tex src/enumerator.anansi || exit 1+  xelatex enumerator.tex > /dev/null || exit 1+  xelatex enumerator.tex > /dev/null || exit 1++  mv enumerator.pdf "enumerator_$VERSION.pdf"+fi++echo ""+echo "============================================================"+if [ -n "$XELATEX" ]; then+	echo "  woven source        : enumerator_$VERSION.pdf"+fi+echo "  source tarball (gz) : enumerator_$VERSION.tar.gz"+if [ -n "$XZ" ]; then+	echo "  source archive (xz) : enumerator_$VERSION.tar.xz"+fi+echo "============================================================"
+ scripts/run-tests view
@@ -0,0 +1,31 @@+#!/bin/bash+if [ ! -f 'enumerator.cabal' ]; then+	echo -n "Can't find enumerator.cabal; please run this script as"+	echo -n " ./scripts/make-tests from within the enumerator source"+	echo " directory"+	exit 1+fi++CABAL=$(which cabal-dev)+if [ -z "$CABAL" ]; then+	echo -n "Can't find 'cabal-dev'; cowardly refusing to fuck with the"+	echo " global package database"+fi+PATH="$PATH:$PWD/cabal-dev/bin/"++ANANSI=$(which anansi)+if [ -z "$ANANSI" ]; then+	echo "Can't find 'anansi' executable; make sure it exists on your "'$PATH'+	exit 1+fi++rm -rf hs dist+anansi -o hs src/enumerator.anansi || exit 1++$CABAL install || exit 1++pushd tests+$CABAL -s ../cabal-dev install || exit 1+popd++cabal-dev/bin/enumerator_tests
+ src/api-docs.anansi view
@@ -0,0 +1,711 @@+\onecolumn+\section{Haddock API documentation}++This section just repeats literate documentation in Haddock syntax.++:d Data.Enumerator module header+-----------------------------------------------------------------------------+-- |+-- Module: Data.Enumerator+-- Copyright: 2010 John Millikin+-- License: MIT+--+-- Maintainer: jmillikin@gmail.com+-- Portability: portable+--+-- Core enumerator types, and some useful primitives.+--+-- This module is intended to be imported qualified:+--+-- @+-- import qualified Data.Enumerator as E+-- @+--+-----------------------------------------------------------------------------+:++:d Data.Enumerator.List module header+-----------------------------------------------------------------------------+-- |+-- Module: Data.Enumerator.List+-- Copyright: 2010 John Millikin+-- License: MIT+--+-- Maintainer: jmillikin@gmail.com+-- Portability: portable+--+-- This module is intended to be imported qualified:+--+-- @+-- import qualified Data.Enumerator.List as EL+-- @+--+-- Since: 0.4.5+--+-----------------------------------------------------------------------------+:++:d Data.Enumerator.Binary module header+-----------------------------------------------------------------------------+-- |+-- Module: Data.Enumerator.Binary+-- Copyright: 2010 John Millikin+-- License: MIT+--+-- Maintainer: jmillikin@gmail.com+-- Portability: portable+--+-- This module is intended to be imported qualified:+--+-- @+-- import qualified Data.Enumerator.Binary as EB+-- @+--+-- Since: 0.4.5+--+-----------------------------------------------------------------------------+:++:d Data.Enumerator.Text module header+-----------------------------------------------------------------------------+-- |+-- Module: Data.Enumerator.Text+-- Copyright: 2010 John Millikin+-- License: MIT+--+-- Maintainer: jmillikin@gmail.com+-- Portability: portable+--+-- This module is intended to be imported qualified:+--+-- @+-- import qualified Data.Enumerator.Text as ET+-- @+--+-- Since: 0.2+--+-----------------------------------------------------------------------------+:++:d Data.Enumerator.IO module header+-----------------------------------------------------------------------------+-- |+-- Module: Data.Enumerator.IO+-- Copyright: 2010 John Millikin+-- License: MIT+--+-- Maintainer: jmillikin@gmail.com+-- Portability: portable+--+-- Deprecated in 0.4.5: use "Data.Enumerator.Binary" instead+--+-----------------------------------------------------------------------------+:++:d apidoc Data.Enumerator.($$)+-- | @($$) = (==\<\<)@+--+-- This might be easier to read when passing a chain of iteratees to an+-- enumerator.+--+-- Since: 0.1.1+:++:d apidoc Data.Enumerator.(<==<)+-- | @(\<==\<) = flip (>==>)@+--+-- Since: 0.1.1+:++:d apidoc Data.Enumerator.(==<<)+-- | @(==\<\<) = flip (\>\>==)@+:++:d apidoc Data.Enumerator.(>==>)+-- | @(>==>) e1 e2 s = e1 s >>== e2@+--+-- Since: 0.1.1+:++:d apidoc Data.Enumerator.(>>==)+-- | Equivalent to '(>>=)' for @m ('Step' a m b)@; allows 'Iteratee's with+-- different input types to be composed.+:++:d apidoc Data.Enumerator.Continue+-- | The 'Iteratee' is capable of accepting more input. Note that more input+-- is not necessarily required; the 'Iteratee' might be able to generate a+-- value immediately if it receives 'EOF'.+:++:d apidoc Data.Enumerator.Enumeratee+-- | In cases where an enumerator acts as both a source and sink, the resulting+-- type is named an 'Enumeratee'. Enumeratees have two input types,+-- &#x201c;outer a&#x201d; (@aOut@) and &#x201c;inner a&#x201d; (@aIn@).+:++:d apidoc Data.Enumerator.Enumerator+-- | While 'Iteratee's consume data, enumerators generate it. Since+-- @'Iteratee'@ is an alias for @m ('Step' a m b)@, 'Enumerator's can+-- be considered step transformers of type+-- @'Step' a m b -> m ('Step' a m b)@.+--+-- 'Enumerator's typically read from an external source (parser, handle,+-- random generator, etc). They feed chunks into an 'Iteratee' until the+-- source runs out of data (triggering 'EOF') or the iteratee finishes+-- processing ('Yield's a value).+:++:d apidoc Data.Enumerator.Error+-- | The 'Iteratee' encountered an error which prevents it from proceeding+-- further.+:++:d apidoc Data.Enumerator.Iteratee+-- | The primary data type for this library, which consumes+-- input from a 'Stream' until it either generates a value or encounters+-- an error. Rather than requiring all input at once, an iteratee will+-- return 'Continue' when it is capable of processing more data.+--+-- In general, iteratees begin in the 'Continue' state. As each chunk is+-- passed to the continuation, the iteratee returns the next step:+-- 'Continue' for more data, 'Yield' when it's finished, or 'Error' to+-- abort processing.+:++:d apidoc Data.Enumerator.Stream+-- | A 'Stream' is a sequence of chunks generated by an 'Enumerator'.+--+-- @('Chunks' [])@ is used to indicate that a stream is still active, but+-- currently has no available data. Iteratees should ignore empty chunks.+:++:d apidoc Data.Enumerator.Yield+-- | The 'Iteratee' cannot receive any more input, and has generated a+-- result. Included in this value is left-over input, which can be passed to+-- composed 'Iteratee's.+:++:d apidoc Data.Enumerator.break+-- | Deprecated in 0.4.5: use 'Data.Enumerator.List.takeWhile' instead+:++:d apidoc Data.Enumerator.catchError+-- | Runs the iteratee, and calls an exception handler if an 'Error' is+-- returned. By handling errors within the enumerator library, and requiring+-- all errors to be represented by 'Exc.SomeException', libraries with+-- varying error types can be easily composed.+--+-- Since: 0.1.1+:++:d apidoc Data.Enumerator.checkDone+-- | @checkDone = 'checkDoneEx' ('Chunks' [])@+--+-- Use this for enumeratees which do not have an input buffer.+:++:d apidoc Data.Enumerator.checkDoneEx+-- | A common pattern in 'Enumeratee' implementations is to check whether+-- the inner 'Iteratee' has finished, and if so, to return its output.+-- 'checkDone' passes its parameter a continuation if the 'Iteratee'+-- can still consume input, or yields otherwise.+--+-- Since: 0.4.3+:++:d apidoc Data.Enumerator.concatEnums+-- | Compose a list of 'Enumerator's using @'(>>==)'@+:++:d apidoc Data.Enumerator.concatMap+-- | @concatMap f = 'concatMapM' (return . f)@+--+-- Since: 0.4.3+:++:d apidoc Data.Enumerator.concatMapM+-- | @concatMapM f@ applies /f/ to each input element and feeds the+-- resulting outputs to the inner iteratee.+--+-- Since: 0.4.5+:++:d apidoc Data.Enumerator.consume+-- | Deprecated in 0.4.5: use 'Data.Enumerator.List.consume' instead+:++:d apidoc Data.Enumerator.continue+-- | @continue k = 'returnI' ('Continue' k)@+:++:d apidoc Data.Enumerator.drop+-- | Deprecated in 0.4.5: use 'Data.Enumerator.List.drop' instead+:++:d apidoc Data.Enumerator.dropWhile+-- | Deprecated in 0.4.5: use 'Data.Enumerator.List.dropWhile' instead+:++:d apidoc Data.Enumerator.enumEOF+-- | docs TODO+:++:d apidoc Data.Enumerator.enumList+-- | @enumList n xs@ enumerates /xs/ as a stream, passing /n/ inputs per+-- chunk.+--+-- Primarily useful for testing and debugging.+:++:d apidoc Data.Enumerator.filter+-- | @filter p = 'concatMap' (\x -> 'Prelude.filter' p [x])@+--+-- Since: 0.4.5+:++:d apidoc Data.Enumerator.filterM+-- | @filterM p = 'concatMapM' (\x -> 'CM.filterM' p [x])@+--+-- Since: 0.4.5+:++:d apidoc Data.Enumerator.foldl+-- | Run the entire input stream through a pure left fold, yielding when+-- there is no more input.+--+-- Since: 0.4.5+:++:d apidoc Data.Enumerator.foldl'+-- | Run the entire input stream through a pure strict left fold, yielding+-- when there is no more input.+--+-- Since: 0.4.5+:++:d apidoc Data.Enumerator.foldM+-- | Run the entire input stream through a monadic left fold, yielding+-- when there is no more input.+--+-- Since: 0.4.5+:++:d apidoc Data.Enumerator.generateM+-- | Like 'repeatM', except the computation may terminate the stream by+-- returning 'Nothing'.+--+-- Since: 0.4.5+:++:d apidoc Data.Enumerator.head+-- | Deprecated in 0.4.5: use 'Data.Enumerator.List.head' instead+:++:d apidoc Data.Enumerator.isEOF+-- | docs TODO+:++:d apidoc Data.Enumerator.iterate+-- | @iterate f x@ enumerates an infinite stream of repeated applications+-- of /f/ to /x/.+--+-- Analogous to 'Prelude.iterate'.+--+-- Since: 0.4.5+:++:d apidoc Data.Enumerator.iterateM+-- | Similar to 'iterate', except the iteration function is monadic.+--+-- Since: 0.4.5+:++:d apidoc Data.Enumerator.joinE+-- | Flatten an enumerator/enumeratee pair into a single enumerator.+:++:d apidoc Data.Enumerator.joinI+-- | 'joinI' is used to &#x201C;flatten&#x201D; 'Enumeratee's into an+-- 'Iteratee'.+:++:d apidoc Data.Enumerator.last+-- | Get the last element in the stream, or 'Nothing' if the stream+-- has ended.+--+-- Consumes the entire stream.+:++:d apidoc Data.Enumerator.length+-- | Get how many elements remained in the stream.+--+-- Consumes the entire stream.+:++:d apidoc Data.Enumerator.liftFoldL+-- | Deprecated in 0.4.5: use 'Data.Enumerator.foldl' instead+--+-- Since: 0.1.1+:++:d apidoc Data.Enumerator.liftFoldL'+-- | Deprecated in 0.4.5: use 'Data.Enumerator.foldl'' instead+--+-- Since: 0.1.1+:++:d apidoc Data.Enumerator.liftFoldM+-- | Deprecated in 0.4.5: use 'Data.Enumerator.foldM' instead+--+-- Since: 0.1.1+:++:d apidoc Data.Enumerator.liftI+-- | Deprecated in 0.4.5: use 'Data.Enumerator.continue' instead+:++:d apidoc Data.Enumerator.liftTrans+-- | Lift an 'Iteratee' onto a monad transformer, re-wrapping the+-- 'Iteratee'&#x2019;s inner monadic values.+--+-- Since: 0.1.1+:++:d apidoc Data.Enumerator.map+-- | @map f = 'concatMap' (\x -> 'Prelude.map' f [x])@+:++:d apidoc Data.Enumerator.mapM+-- | @mapM f = 'concatMapM' (\x -> 'Prelude.mapM' f [x])@+--+-- Since: 0.4.3+:++:d apidoc Data.Enumerator.peek+-- | Peek at the next element in the stream, or 'Nothing' if the stream+-- has ended.+:++:d apidoc Data.Enumerator.printChunks+-- | Print chunks as they're received from the enumerator, optionally+-- printing empty chunks.+:++:d apidoc Data.Enumerator.repeat+-- | Enumerates an infinite stream of the provided value.+--+-- Analogous to 'Prelude.repeat'.+--+-- Since: 0.4.5+:++:d apidoc Data.Enumerator.repeatM+-- | Enumerates an infinite stream by running the provided computation and+-- passing each result to the iteratee.+--+-- Since: 0.4.5+:++:d apidoc Data.Enumerator.replicate+-- | @replicate n x = 'replicateM' n (return x)@+--+-- Analogous to 'Prelude.replicate'.+--+-- Since: 0.4.5+:++:d apidoc Data.Enumerator.replicateM+-- | @replicateM n m_x@ enumerates a stream of /n/ input elements; each+-- element is generated by running the input computation /m_x/ once.+--+-- Since: 0.4.5+:++:d apidoc Data.Enumerator.returnI+-- | @returnI step = 'Iteratee' (return step)@+:++:d apidoc Data.Enumerator.run+-- | Run an iteratee until it finishes, and return either the final value+-- (if it succeeded) or the error (if it failed).+:++:d apidoc Data.Enumerator.run_+-- | Like 'run', except errors are converted to exceptions and thrown.+-- Primarily useful for small scripts or other simple cases.+--+-- Since: 0.4.1+:++:d apidoc Data.Enumerator.sequence+-- | Feeds outer input elements into the provided iteratee until it yields+-- an inner input, passes that to the inner iteratee, and then loops.+:++:d apidoc Data.Enumerator.span+-- | Deprecated in 0.4.5: use 'Data.Enumerator.List.takeWhile' instead+:++:d apidoc Data.Enumerator.throwError+-- | @throwError exc = 'returnI' ('Error' ('Exc.toException' exc))@+:++:d apidoc Data.Enumerator.yield+-- | @yield x extra = 'returnI' ('Yield' x extra)@+:++:d apidoc Data.Enumerator.Binary.consume+-- | Read all remaining input from the stream, and return as a lazy+-- ByteString.+--+-- Since: 0.4.5+:++:d apidoc Data.Enumerator.Binary.drop+-- | @drop n@ ignores /n/ bytes of input from the stream.+--+-- Since: 0.4.5+:++:d apidoc Data.Enumerator.Binary.dropWhile+-- | @dropWhile p@ ignores input from the stream until the first byte which+-- does not match the predicate.+--+-- Since: 0.4.5+:++:d apidoc Data.Enumerator.Binary.enumFile+-- | Opens a file path in binary mode, and passes the handle to 'enumHandle'.+-- The file will be closed when the 'Iteratee' finishes.+:++:d apidoc Data.Enumerator.Binary.enumHandle+-- | Read bytes (in chunks of the given buffer size) from the handle, and+-- stream them to an 'Iteratee'. If an exception occurs during file IO,+-- enumeration will stop and 'Error' will be returned. Exceptions from the+-- iteratee are not caught.+--+-- This enumerator blocks until at least one byte is available from the+-- handle, and might read less than the maximum buffer size in some+-- cases.+--+-- The handle should be opened with no encoding, and in 'IO.ReadMode' or+-- 'IO.ReadWriteMode'.+:++:d apidoc Data.Enumerator.Binary.head+-- | Get the next byte from the stream, or 'Nothing' if the stream has+-- ended.+--+-- Since: 0.4.5+:++:d apidoc Data.Enumerator.Binary.isolate+-- | @isolate n@ reads at most /n/ bytes from the stream, and passes them+-- to its iteratee. If the iteratee finishes early, bytes continue to be+-- consumed from the outer stream until /n/ have been consumed.+--+-- Since: 0.4.5+:++:d apidoc Data.Enumerator.Binary.iterHandle+-- | Read bytes from a stream and write them to a handle. If an exception+-- occurs during file IO, enumeration will stop and 'Error' will be+-- returned.+--+-- The handle should be opened with no encoding, and in 'IO.WriteMode' or+-- 'IO.ReadWriteMode'.+:++:d apidoc Data.Enumerator.Binary.require+-- | @require n@ buffers input until at least /n/ bytes are available, or+-- throws an error if the stream ends early.+--+-- Since: 0.4.5+:++:d apidoc Data.Enumerator.Binary.take+-- | @take n@ extracts the next /n/ bytes from the stream, as a lazy+-- ByteString.+--+-- Since: 0.4.5+:++:d apidoc Data.Enumerator.Binary.takeWhile+-- | @takeWhile p@ extracts input from the stream until the first byte which+-- does not match the predicate.+--+-- Since: 0.4.5+:++:d apidoc Data.Enumerator.IO.enumFile+-- | Deprecated in 0.4.5: use 'Data.Enumerator.Binary.enumFile' instead+:++:d apidoc Data.Enumerator.IO.enumHandle+-- | Deprecated in 0.4.5: use 'Data.Enumerator.Binary.enumHandle' instead+:++:d apidoc Data.Enumerator.IO.iterHandle+-- | Deprecated in 0.4.5: use 'Data.Enumerator.Binary.iterHandle' instead+:++:d apidoc Data.Enumerator.List.consume+-- | Read all remaining input elements from the stream, and return as a list.+--+-- Since: 0.4.5+:++:d apidoc Data.Enumerator.List.drop+-- | @drop n@ ignores /n/ input elements from the stream.+--+-- Since: 0.4.5+:++:d apidoc Data.Enumerator.List.dropWhile+-- | @dropWhile p@ ignores input from the stream until the first element+-- which does not match the predicate.+--+-- Since: 0.4.5+:++:d apidoc Data.Enumerator.List.head+-- | Get the next element from the stream, or 'Nothing' if the stream has+-- ended.+--+-- Since: 0.4.5+:++:d apidoc Data.Enumerator.List.isolate+-- | @isolate n@ reads at most /n/ elements from the stream, and passes them+-- to its iteratee. If the iteratee finishes early, elements continue to be+-- consumed from the outer stream until /n/ have been consumed.+--+-- Since: 0.4.5+:++:d apidoc Data.Enumerator.List.require+-- | @require n@ buffers input until at least /n/ elements are available, or+-- throws an error if the stream ends early.+--+-- Since: 0.4.5+:++:d apidoc Data.Enumerator.List.take+-- | @take n@ extracts the next /n/ elements from the stream, as a list.+--+-- Since: 0.4.5+:++:d apidoc Data.Enumerator.List.takeWhile+-- | @takeWhile p@ extracts input from the stream until the first element+-- which does not match the predicate.+--+-- Since: 0.4.5+:++:d apidoc Data.Enumerator.Text.Codec+-- | docs TODO+--+-- Since: 0.2+:++:d apidoc Data.Enumerator.Text.consume+-- | Read all remaining input from the stream, and return as a lazy+-- Text.+--+-- Since: 0.4.5+:++:d apidoc Data.Enumerator.Text.decode+-- | Convert bytes into text, using the provided codec. If the codec is+-- not capable of decoding an input byte sequence, an error will be thrown.+--+-- Since: 0.2+:++:d apidoc Data.Enumerator.Text.drop+-- | @drop n@ ignores /n/ characters of input from the stream.+--+-- Since: 0.4.5+:++:d apidoc Data.Enumerator.Text.dropWhile+-- | @dropWhile p@ ignores input from the stream until the first character+-- which does not match the predicate.+--+-- Since: 0.4.5+:++:d apidoc Data.Enumerator.Text.encode+-- | Convert text into bytes, using the provided codec. If the codec is+-- not capable of representing an input character, an error will be thrown.+--+-- Since: 0.2+:++:d apidoc Data.Enumerator.Text.enumFile+-- | Opens a file path in text mode, and passes the handle to 'enumHandle'.+-- The file will be closed when the 'Iteratee' finishes.+--+-- Since: 0.2+:++:d apidoc Data.Enumerator.Text.enumHandle+-- | Read lines of text from the handle, and stream them to an 'Iteratee'.+-- If an exception occurs during file IO, enumeration will stop and 'Error'+-- will be returned. Exceptions from the iteratee are not caught.+--+-- The handle should be opened with an appropriate text encoding, and+-- in 'IO.ReadMode' or 'IO.ReadWriteMode'.+--+-- Since: 0.2+:++:d apidoc Data.Enumerator.Text.head+-- | Get the next character from the stream, or 'Nothing' if the stream has+-- ended.+--+-- Since: 0.4.5+:++:d apidoc Data.Enumerator.Text.isolate+-- | @isolate n@ reads at most /n/ characters from the stream, and passes+-- them to its iteratee. If the iteratee finishes early, characters continue+-- to be consumed from the outer stream until /n/ have been consumed.+--+-- Since: 0.4.5+:++:d apidoc Data.Enumerator.Text.iterHandle+-- | Read text from a stream and write it to a handle. If an exception+-- occurs during file IO, enumeration will stop and 'Error' will be+-- returned.+--+-- The handle should be opened with an appropriate text encoding, and+-- in 'IO.WriteMode' or 'IO.ReadWriteMode'.+--+-- Since: 0.2+:++:d apidoc Data.Enumerator.Text.require+-- | @require n@ buffers input until at least /n/ characters are available,+-- or throws an error if the stream ends early.+--+-- Since: 0.4.5+:++:d apidoc Data.Enumerator.Text.take+-- | @take n@ extracts the next /n/ characters from the stream, as a lazy+-- Text.+--+-- Since: 0.4.5+:++:d apidoc Data.Enumerator.Text.takeWhile+-- | @takeWhile p@ extracts input from the stream until the first character+-- which does not match the predicate.+--+-- Since: 0.4.5+:
+ src/binary.anansi view
@@ -0,0 +1,222 @@+\section{Binary}++:f Data/Enumerator/Binary.hs+|Data.Enumerator.Binary module header|+module Data.Enumerator.Binary (+	|Data.Enumerator.Binary exports|+	) where+import Prelude hiding (head, drop, takeWhile)+import Data.Enumerator hiding (head, drop)+import qualified Data.ByteString as B+|Data.Enumerator.Binary imports|+:++\subsection{IO}++{\tt enumHandle} and {\tt enumFile} are rough analogues of+{\tt hGetContents} and {\tt readFile} from the standard library, except+they operate only in binary mode.++Any exceptions thrown while reading or writing data are caught and reported+using {\tt throwError}, so errors can be handled in pure iteratees.++:d Data.Enumerator.Binary imports+import Data.Enumerator.Util (tryStep)+import qualified Control.Exception as Exc+import Control.Monad.IO.Class (MonadIO)+import qualified System.IO as IO+import System.IO.Error (isEOFError)+:++:f Data/Enumerator/Binary.hs+|apidoc Data.Enumerator.Binary.enumHandle|+enumHandle :: MonadIO m+           => Integer -- ^ Buffer size+           -> IO.Handle+           -> Enumerator B.ByteString m b+enumHandle bufferSize h = loop where+	loop (Continue k) = withBytes $ \bytes ->+		if B.null bytes+			then continue k+			else k (Chunks [bytes]) >>== loop+	+	loop step = returnI step+	+	intSize = fromInteger bufferSize+	withBytes = tryStep $ do+		hasInput <- Exc.catch+			(IO.hWaitForInput h (-1))+			(\err -> if isEOFError err+				then return False+				else Exc.throwIO err)+		if hasInput+			then B.hGetNonBlocking h intSize+			else return B.empty+:++:f Data/Enumerator/Binary.hs+|apidoc Data.Enumerator.Binary.enumFile|+enumFile :: FilePath -> Enumerator B.ByteString IO b+enumFile path = enum where+	withHandle = tryStep (IO.openBinaryFile path IO.ReadMode)+	enum step = withHandle $ \h -> do+		Iteratee $ Exc.finally+			(runIteratee (enumHandle 4096 h step))+			(IO.hClose h)+:++:f Data/Enumerator/Binary.hs+|apidoc Data.Enumerator.Binary.iterHandle|+iterHandle :: MonadIO m => IO.Handle+           -> Iteratee B.ByteString m ()+iterHandle h = continue step where+	step EOF = yield () EOF+	step (Chunks []) = continue step+	step (Chunks bytes) = let+		put = mapM_ (B.hPut h) bytes+		in tryStep put (\_ -> continue step)+:++:d Data.Enumerator.Binary exports+  -- * Binary IO+  enumHandle+, enumFile+, iterHandle+:++\subsection{List analogues}++:d Data.Enumerator.Binary imports+import Data.Word (Word8)+import qualified Data.ByteString.Lazy as BL+:++:f Data/Enumerator/Binary.hs+toChunks :: BL.ByteString -> Stream B.ByteString+toChunks = Chunks . BL.toChunks+:++:f Data/Enumerator/Binary.hs+|apidoc Data.Enumerator.Binary.head|+head :: Monad m => Iteratee B.ByteString m (Maybe Word8)+head = continue loop where+	loop (Chunks xs) = case BL.uncons (BL.fromChunks xs) of+		Just (char, extra) -> yield (Just char) (toChunks extra)+		Nothing -> head+	loop EOF = yield Nothing EOF+:++:f Data/Enumerator/Binary.hs+|apidoc Data.Enumerator.Binary.drop|+drop :: Monad m => Integer -> Iteratee B.ByteString m ()+drop n | n <= 0 = return ()+drop n = continue (loop n) where+	loop n' (Chunks xs) = iter where+		lazy = BL.fromChunks xs+		len = toInteger (BL.length lazy)+		iter = if len < n'+			then drop (n' - len)+			else yield () (toChunks (BL.drop (fromInteger n') lazy))+	loop _ EOF = yield () EOF+:++:f Data/Enumerator/Binary.hs+|apidoc Data.Enumerator.Binary.dropWhile|+dropWhile :: Monad m => (Word8 -> Bool) -> Iteratee B.ByteString m ()+dropWhile p = continue loop where+	loop (Chunks xs) = iter where+		lazy = BL.dropWhile p (BL.fromChunks xs)+		iter = if BL.null lazy+			then continue loop+			else yield () (toChunks lazy)+	loop EOF = yield () EOF+:++:f Data/Enumerator/Binary.hs+|apidoc Data.Enumerator.Binary.take|+take :: Monad m => Integer -> Iteratee B.ByteString m BL.ByteString+take n | n <= 0 = return BL.empty+take n = continue (loop id n) where+	loop acc n' (Chunks xs) = iter where+		lazy = BL.fromChunks xs+		len = toInteger (BL.length lazy)+		+		iter = if len < n'+			then continue (loop (acc . (BL.append lazy)) (n' - len))+			else let+				(xs', extra) = BL.splitAt (fromInteger n') lazy+				in yield (acc xs') (toChunks extra)+	loop acc _ EOF = yield (acc BL.empty) EOF+:++:f Data/Enumerator/Binary.hs+|apidoc Data.Enumerator.Binary.takeWhile|+takeWhile :: Monad m => (Word8 -> Bool) -> Iteratee B.ByteString m BL.ByteString+takeWhile p = continue (loop id) where+	loop acc (Chunks []) = continue (loop acc)+	loop acc (Chunks xs) = iter where+		lazy = BL.fromChunks xs+		(xs', extra) = BL.span p lazy+		iter = if BL.null extra+			then continue (loop (acc . (BL.append lazy)))+			else yield (acc xs') (toChunks extra)+	loop acc EOF = yield (acc BL.empty) EOF+:++:f Data/Enumerator/Binary.hs+|apidoc Data.Enumerator.Binary.consume|+consume :: Monad m => Iteratee B.ByteString m BL.ByteString+consume = continue (loop id) where+	loop acc (Chunks []) = continue (loop acc)+	loop acc (Chunks xs) = iter where+		lazy = BL.fromChunks xs+		iter = continue (loop (acc . (BL.append lazy)))+	loop acc EOF = yield (acc BL.empty) EOF+:++:f Data/Enumerator/Binary.hs+|apidoc Data.Enumerator.Binary.require|+require :: Monad m => Integer -> Iteratee B.ByteString m ()+require n | n <= 0 = return ()+require n = continue (loop id n) where+	loop acc n' (Chunks xs) = iter where+		lazy = BL.fromChunks xs+		len = toInteger (BL.length lazy)+		iter = if len < n'+			then continue (loop (acc . (BL.append lazy)) (n' - len))+			else yield () (toChunks (acc lazy))+	loop _ _ EOF = throwError (Exc.ErrorCall "require: Unexpected EOF")+:++Same caveats as {\tt Data.Enumerator.List.isolate}++:f Data/Enumerator/Binary.hs+|apidoc Data.Enumerator.Binary.isolate|+isolate :: Monad m => Integer -> Enumeratee B.ByteString B.ByteString m b+isolate n step | n <= 0 = return step+isolate n (Continue k) = continue loop where+	loop (Chunks []) = continue loop+	loop (Chunks xs) = iter where+		lazy = BL.fromChunks xs+		len = toInteger (BL.length lazy)+		+		iter = if len <= n+			then k (Chunks xs) >>== isolate (n - len)+			else let+				(s1, s2) = BL.splitAt (fromInteger n) lazy+				in k (toChunks s1) >>== (\step -> yield step (toChunks s2))+	loop EOF = k EOF >>== (\step -> yield step EOF)+isolate n step = drop n >> return step+:++:d Data.Enumerator.Binary exports+-- * List analogues+, Data.Enumerator.Binary.head+, Data.Enumerator.Binary.drop+, Data.Enumerator.Binary.dropWhile+, Data.Enumerator.Binary.take+, Data.Enumerator.Binary.takeWhile+, Data.Enumerator.Binary.consume+, require+, isolate+:
+ src/compat.anansi view
@@ -0,0 +1,237 @@+\section{Compatibility}++Version 0.4.5 of this library introduced some substantial reorganization+and renamings; this section implements compatibility shims, so the API+remains stable.++:d Data.Enumerator exports+-- * Compatibility+:++\subsection{Obsolete functions}++These are functions which seemed like good ideas, or were defined by other+enumerator/iteratee libraries, but turned out to be basically useless. At+least, I've never figured out what they're good for.++:f Data/Enumerator.hs+|apidoc Data.Enumerator.liftTrans|+liftTrans :: (Monad m, MonadTrans t, Monad (t m)) =>+             Iteratee a m b -> Iteratee a (t m) b+liftTrans iter = Iteratee $ do+	step <- lift (runIteratee iter)+	return $ case step of+		Yield x cs -> Yield x cs+		Error err -> Error err+		Continue k -> Continue (liftTrans . k)+:++:f Data/Enumerator.hs+{-# DEPRECATED liftI+     "Use 'Data.Enumerator.continue' instead" #-}+|apidoc Data.Enumerator.liftI|+liftI :: Monad m => (Stream a -> Step a m b)+      -> Iteratee a m b+liftI k = continue (returnI . k)+:++:f Data/Enumerator.hs+|apidoc Data.Enumerator.peek|+peek :: Monad m => Iteratee a m (Maybe a)+peek = continue loop where+	loop (Chunks []) = continue loop+	loop chunk@(Chunks (x:_)) = yield (Just x) chunk+	loop EOF = yield Nothing EOF+:++:f Data/Enumerator.hs+|apidoc Data.Enumerator.last|+last :: Monad m => Iteratee a m (Maybe a)+last = continue (loop Nothing) where+	loop ret (Chunks xs) = continue . loop $ case xs of+		[] -> ret+		_ -> Just (Prelude.last xs)+	loop ret EOF = yield ret EOF+:++:d Data.Enumerator imports+import Data.List (genericLength)+:++:f Data/Enumerator.hs+|apidoc Data.Enumerator.length|+length :: Monad m => Iteratee a m Integer+length = continue (loop 0) where+	len = genericLength+	loop n (Chunks xs) = continue (loop (n + len xs))+	loop n EOF = yield n EOF+:++:d Data.Enumerator exports+-- ** Obsolete functions+, liftTrans+, liftI+, peek+, Data.Enumerator.last+, Data.Enumerator.length+:++\subsection{Aliases}++In previous library versions, several list-based iteratees were defined in+{\tt Data.Enumerator}. They are now defined in {\tt Data.Enumerator.List};+because these functions use core enumerator types, a bit of module+gymnastics is required to get everything compiling properly.++:f Data/Enumerator.hs-boot+module Data.Enumerator where+import qualified Control.Exception as Exc+data Stream a+data Step a m b+	= Continue (Stream a -> Iteratee a m b)+	| Yield b (Stream a)+	| Error Exc.SomeException+newtype Iteratee a m b = Iteratee+	{ runIteratee :: m (Step a m b)+	}+:++:f Data/Enumerator/List.hs-boot+module Data.Enumerator.List where+import {-# SOURCE #-} Data.Enumerator+head :: Monad m => Iteratee a m (Maybe a)+drop :: Monad m => Integer -> Iteratee a m ()+dropWhile :: Monad m => (a -> Bool) -> Iteratee a m ()+takeWhile :: Monad m => (a -> Bool) -> Iteratee a m [a]+consume :: Monad m => Iteratee a m [a]+:++:d Data.Enumerator imports+import {-# SOURCE #-} qualified Data.Enumerator.List as EL+:++These {\tt .hs-boot} files are enough for {\tt Data.Enumerator} to re-export+the list functions under old names, with appropriate deprecation warnings.++:f Data/Enumerator.hs+{-# DEPRECATED head+     "Use 'Data.Enumerator.List.head' instead" #-}+|apidoc Data.Enumerator.head|+head :: Monad m => Iteratee a m (Maybe a)+head = EL.head++{-# DEPRECATED drop+     "Use 'Data.Enumerator.List.drop' instead" #-}+|apidoc Data.Enumerator.drop|+drop :: Monad m => Integer -> Iteratee a m ()+drop = EL.drop++{-# DEPRECATED dropWhile+     "Use 'Data.Enumerator.List.dropWhile' instead" #-}+|apidoc Data.Enumerator.dropWhile|+dropWhile :: Monad m => (a -> Bool) -> Iteratee a m ()+dropWhile = EL.dropWhile++{-# DEPRECATED span+     "Use 'Data.Enumerator.List.takeWhile' instead" #-}+|apidoc Data.Enumerator.span|+span :: Monad m => (a -> Bool) -> Iteratee a m [a]+span = EL.takeWhile++{-# DEPRECATED break+     "Use 'Data.Enumerator.List.takeWhile' instead" #-}+|apidoc Data.Enumerator.break|+break :: Monad m => (a -> Bool) -> Iteratee a m [a]+break p = EL.takeWhile (not . p)++{-# DEPRECATED consume+     "Use 'Data.Enumerator.List.consume' instead" #-}+|apidoc Data.Enumerator.consume|+consume :: Monad m => Iteratee a m [a]+consume = EL.consume+:++:d Data.Enumerator exports+-- ** Deprecated aliases+, Data.Enumerator.head+, Data.Enumerator.drop+, Data.Enumerator.dropWhile+, Data.Enumerator.span+, Data.Enumerator.break+, Data.Enumerator.consume+:++0.4.5 also saw the pure-fold enumerators renamed, to match other functions+based on {\tt Prelude} names.++:f Data/Enumerator.hs+{-# DEPRECATED liftFoldL+     "Use 'Data.Enumerator.foldl' instead" #-}+|apidoc Data.Enumerator.liftFoldL|+liftFoldL :: Monad m => (b -> a -> b) -> b+          -> Iteratee a m b+liftFoldL = Data.Enumerator.foldl++{-# DEPRECATED liftFoldL'+     "Use 'Data.Enumerator.foldl' ' instead" #-}+|apidoc Data.Enumerator.liftFoldL'|+liftFoldL' :: Monad m => (b -> a -> b) -> b+           -> Iteratee a m b+liftFoldL' = Data.Enumerator.foldl'++{-# DEPRECATED liftFoldM+     "Use 'Data.Enumerator.foldM' instead" #-}+|apidoc Data.Enumerator.liftFoldM|+liftFoldM :: Monad m => (b -> a -> m b) -> b+          -> Iteratee a m b+liftFoldM = Data.Enumerator.foldM+:++:d Data.Enumerator exports+, liftFoldL+, liftFoldL'+, liftFoldM+:++\clearpage+Finally, the {\tt Data.Enumerator.IO} module was moved to+{\tt Data.Enumerator.Binary}, and altered to include many more functions+related to binary and {\tt ByteString} processing.++:f Data/Enumerator/IO.hs+|Data.Enumerator.IO module header|+module Data.Enumerator.IO+	{-# DEPRECATED+	     "Use 'Data.Enumerator.Binary' instead" #-}+	( enumHandle+	, enumFile+	, iterHandle+	) where+import qualified Data.Enumerator as E+import qualified Data.Enumerator.Binary as EB+import Control.Monad.IO.Class (MonadIO)+import qualified Data.ByteString as B+import qualified System.IO as IO++{-# DEPRECATED enumHandle+     "Use 'Data.Enumerator.Binary.enumHandle' instead" #-}+|apidoc Data.Enumerator.IO.enumHandle|+enumHandle :: MonadIO m+           => Integer+           -> IO.Handle+           -> E.Enumerator B.ByteString m b+enumHandle = EB.enumHandle++{-# DEPRECATED enumFile+     "Use 'Data.Enumerator.Binary.enumFile' instead" #-}+|apidoc Data.Enumerator.IO.enumFile|+enumFile :: FilePath -> E.Enumerator B.ByteString IO b+enumFile = EB.enumFile++{-# DEPRECATED iterHandle+     "Use 'Data.Enumerator.Binary.iterHandle' instead" #-}+|apidoc Data.Enumerator.IO.iterHandle|+iterHandle :: MonadIO m => IO.Handle+           -> E.Iteratee B.ByteString m ()+iterHandle = EB.iterHandle+:
+ src/enumerator.anansi view
@@ -0,0 +1,119 @@+:# Copyright (C) 2010 John Millikin <jmillikin@gmail.com>+:#+:# See license.txt for details++:option tab-size 2++\documentclass{article}++\usepackage{color}+\usepackage{hyperref}+\usepackage{noweb}+\usepackage{indentfirst}+\usepackage{amsmath}+\usepackage{multicol}++\noweboptions{smallcode}++% Smaller margins+\usepackage[left=1cm,top=1cm,right=1cm]{geometry}++% Remove boxes from hyperlinks+\hypersetup{+    colorlinks,+    linkcolor=blue,+    urlcolor=blue,+}++\newcommand{\io}{{\sc i/o}}++\title{enumerator\_0.4.5}+\author{John Millikin\\+        \href{mailto:"John Millikin" <jmillikin@gmail.com>}{\tt jmillikin@gmail.com}}+\date{January 10, 2011}++\begin{document}++\maketitle++\setlength{\parskip}{5pt plus 1pt}++\begin{multicols}{2}+\section*{Abstract}++Typical buffer-based incremental \io{} is based around a single loop, which+reads data from some source (such as a socket or file), transforms it, and+generates one or more outputs (such as a line count, {\sc http} responses,+or modified file).  Although efficient and safe, these loops are all+single-purpose; it is difficult or impossible to compose buffer-based+processing loops.++Haskell's concept of ``lazy \io{}'' allows pure code to operate on data from+an external source. However, lazy \io{} has several shortcomings. Most notably,+resources such as memory and file handles can be retained for arbitrarily+long periods of time, causing unpredictable performance and error conditions.++Enumerators are an efficient, predictable, and safe alternative to lazy \io{}.+Discovered by Oleg \mbox{Kiselyov}, they allow large datasets to be processed+in near-constant space by pure code. Although somewhat more complex to write,+using enumerators instead of lazy \io{} produces more correct programs.++This library contains an enumerator implementation for Haskell, designed to+be both simple and efficient. Three core types are defined, along with+numerous helper functions:++\begin{itemize}++\item {\it Iteratee\/}: Data sinks, analogous to left folds. Iteratees consume+a sequence of \emph{input} values, and generate a single \emph{output} value.+Many iteratees are designed to perform side effects (such as printing to+{\tt stdout}), so they can also be used as monad transformers.++\item {\it Enumerator\/}: Data sources, which generate input sequences. Typical+enumerators read from a file handle, socket, random number generator, or+other external stream. To operate, enumerators are passed an iteratee, and+provide that iteratee with input until either the iteratee has completed its+computation, or {\sc eof}.++\item {\it Enumeratee\/}: Data transformers, which operate as both enumerators and+iteratees. Enumeratees read from an \emph{outer} enumerator, and provide the+transformed data to an \emph{inner} iteratee.++\end{itemize}++\noindent Homepage: \href{http://john-millikin.com/software/enumerator/}+                         {\small \tt http://john-millikin.com/software/enumerator/}++\setlength{\parskip}{0pt plus 1pt}+\tableofcontents+\setlength{\parskip}{4pt plus 1pt}+\end{multicols}++\newpage+\begin{multicols*}{2}+:include types.anansi++\newpage+:include primitives.anansi+\end{multicols*}++\newpage+:include list.anansi++\newpage+:include binary.anansi++\newpage+:include text.anansi++\newpage+:include util.anansi++\newpage+\begin{multicols*}{2}+:include compat.anansi+\end{multicols*}++:include api-docs.anansi++\end{document}
+ src/list.anansi view
@@ -0,0 +1,155 @@+\section{Lists}++:f Data/Enumerator/List.hs+|Data.Enumerator.List module header|+module Data.Enumerator.List (+	|Data.Enumerator.List exports|+	) where+import Data.Enumerator hiding (consume, head, peek, drop, dropWhile)+import Control.Exception (ErrorCall(..))+import Prelude hiding (head, drop, dropWhile, take, takeWhile)+import qualified Data.List as L+:++:f Data/Enumerator/List.hs+|apidoc Data.Enumerator.List.head|+head :: Monad m => Iteratee a m (Maybe a)+head = continue loop where+	loop (Chunks []) = head+	loop (Chunks (x:xs)) = yield (Just x) (Chunks xs)+	loop EOF = yield Nothing EOF+:++:f Data/Enumerator/List.hs+|apidoc Data.Enumerator.List.drop|+drop :: Monad m => Integer -> Iteratee a m ()+drop n | n <= 0 = return ()+drop n = continue (loop n) where+	loop n' (Chunks xs) = iter where+		len = L.genericLength xs+		iter = if len < n'+			then drop (n' - len)+			else yield () (Chunks (L.genericDrop n' xs))+	loop _ EOF = yield () EOF+:++:f Data/Enumerator/List.hs+|apidoc Data.Enumerator.List.dropWhile|+dropWhile :: Monad m => (a -> Bool) -> Iteratee a m ()+dropWhile p = continue loop where+	loop (Chunks xs) = case L.dropWhile p xs of+		[] -> continue loop+		xs' -> yield () (Chunks xs')+	loop EOF = yield () EOF+:++:f Data/Enumerator/List.hs+|apidoc Data.Enumerator.List.take|+take :: Monad m => Integer -> Iteratee a m [a]+take n | n <= 0 = return []+take n = continue (loop id n) where+	len = L.genericLength+	loop acc n' (Chunks xs)+		| len xs < n' = continue (loop (acc . (xs ++)) (n' - len xs))+		| otherwise   = let+			(xs', extra) = L.genericSplitAt n' xs+			in yield (acc xs') (Chunks extra)+	loop acc _ EOF = yield (acc []) EOF+:++:f Data/Enumerator/List.hs+|apidoc Data.Enumerator.List.takeWhile|+takeWhile :: Monad m => (a -> Bool) -> Iteratee a m [a]+takeWhile p = continue (loop id) where+	loop acc (Chunks []) = continue (loop acc)+	loop acc (Chunks xs) = case Prelude.span p xs of+		(_, []) -> continue (loop (acc . (xs ++)))+		(xs', extra) -> yield (acc xs') (Chunks extra)+	loop acc EOF = yield (acc []) EOF+:++:# NOTE: peeking properly is currently impossible with the current design of+:# 'Stream'. Once it's updated to support EOF with "final data", peek can be+:# re-enabled+:#+:# :d Data/Enumerator/List.hs+:# |apidoc Data.Enumerator.List.peek|+:# peek :: Monad m => Integer -> Iteratee a m [a]+:# peek n | n <= 0 = return []+:# peek n = continue (loop id n) where+:# 	len = L.genericLength+:# 	loop acc n' (Chunks xs)+:# 		| len xs < n' = continue (loop (acc . (xs ++)) (n' - len xs))+:# 		| otherwise   = let+:# 			xs' = L.genericTake n' xs+:# 			in yield (acc xs') (Chunks (acc xs))+:# 	loop acc _ EOF = yield (acc []) EOF+:# :+:# +:# :d Data/Enumerator/List.hs (disabled)+:# |apidoc Data.Enumerator.List.peekWhile|+:# peekWhile :: Monad m => (a -> Bool) -> Iteratee a m [a]+:# peekWhile p = continue (loop id) where+:# 	loop acc (Chunks []) = continue (loop acc)+:# 	loop acc (Chunks xs) = case Prelude.span p xs of+:# 		(_, []) -> continue (loop (acc . (xs ++)))+:# 		(xs', _) -> yield (acc xs') (Chunks (acc xs))+:# 	loop acc EOF = yield (acc []) EOF+:# :++:f Data/Enumerator/List.hs+|apidoc Data.Enumerator.List.consume|+consume :: Monad m => Iteratee a m [a]+consume = continue (loop id) where+	loop acc (Chunks []) = continue (loop acc)+	loop acc (Chunks xs) = continue (loop (acc . (xs ++)))+	loop acc EOF = yield (acc []) EOF+:++:f Data/Enumerator/List.hs+|apidoc Data.Enumerator.List.require|+require :: Monad m => Integer -> Iteratee a m ()+require n | n <= 0 = return ()+require n = continue (loop id n) where+	len = L.genericLength+	loop acc n' (Chunks xs)+		| len xs < n' = continue (loop (acc . (xs ++)) (n' - len xs))+		| otherwise   = yield () (Chunks (acc xs))+	loop _ _ EOF = throwError (ErrorCall "require: Unexpected EOF")+:++Note: {\tt isolate} has some odd behavior regarding extra input in the+inner iteratee. Depending on how large the chunks are, extra input might+be returned in the {\tt Step}, or dropped.++This doesn't matter if {\tt joinI} is used, but might if a user is poking+around inside the {\tt Step}. Eventually, enumeratees will be modified to+avoid exposing its internal iteratee state.++:f Data/Enumerator/List.hs+|apidoc Data.Enumerator.List.isolate|+isolate :: Monad m => Integer -> Enumeratee a a m b+isolate n step | n <= 0 = return step+isolate n (Continue k) = continue loop where+	len = L.genericLength+	+	loop (Chunks []) = continue loop+	loop (Chunks xs)+		| len xs <= n = k (Chunks xs) >>== isolate (n - len xs)+		| otherwise = let+			(s1, s2) = L.genericSplitAt n xs+			in k (Chunks s1) >>== (\step -> yield step (Chunks s2))+	loop EOF = k EOF >>== (\step -> yield step EOF)+isolate n step = drop n >> return step+:++:d Data.Enumerator.List exports+  head+, drop+, dropWhile+, take+, takeWhile+, consume+, require+, isolate+:
+ src/primitives.anansi view
@@ -0,0 +1,316 @@+\section{Primitives}++:d Data.Enumerator exports+-- * Primitives+:++\subsection{Error handling}++Most real-world applications have to deal with error conditions; however,+libraries have various ways of reporting errors. Some throw exceptions,+others use callbacks, and many just use {\tt Either}. Heterogeneous error+handling makes composing code very difficult; therefore, all+enumerator-based code simply uses the standard {\tt Control.Exception}+module and its types.++Instances for the {\tt MonadError} class are provided in auxiliary+libraries, to avoid extraneous dependencies.++:f Data/Enumerator.hs+|apidoc Data.Enumerator.throwError|+throwError :: (Monad m, Exc.Exception e) => e+           -> Iteratee a m b+throwError exc = returnI (Error (Exc.toException exc))+:++Handling errors has a caveat: any input consumed before the error was+thrown can't be recovered. If an iteratee needs to continue parsing after an+error, either buffer the input stream or use a separate framing mechanism.++This limitation means that {\tt catchError} is mostly only useful for+transforming or logging errors, not ignoring them.++:f Data/Enumerator.hs+|apidoc Data.Enumerator.catchError|+catchError :: Monad m => Iteratee a m b+           -> (Exc.SomeException -> Iteratee a m b)+           -> Iteratee a m b+catchError iter h = iter >>== step where+	step (Yield b as) = yield b as+	step (Error err) = h err+	step (Continue k) = continue (\s -> k s >>== step)+:++:d Data.Enumerator exports+-- ** Error handling+, throwError+, catchError+:++\subsection{Iteratees}++Since iteratees are semantically a left-fold, there are many existing+folds that can be lifted to iteratees. The {\tt foldl}, {\tt foldl'}, and+{\tt foldM} functions work like their standard library namesakes, but+construct iteratees instead. These iteratees are not as complex as what can+be created using {\tt Yield} and {\tt Continue}, but cover many common cases.++Each fold consumes input from the stream until {\sc eof}, when it yields its+current accumulator.++:d Data.Enumerator imports+import Data.List (foldl')+:++:f Data/Enumerator.hs+|apidoc Data.Enumerator.foldl|+foldl :: Monad m => (b -> a -> b) -> b+      -> Iteratee a m b+foldl step = continue . loop where+	fold = Prelude.foldl step+	loop acc stream = case stream of+		Chunks [] -> continue (loop acc)+		Chunks xs -> continue (loop (fold acc xs))+		EOF -> yield acc EOF+:++:f Data/Enumerator.hs+|apidoc Data.Enumerator.foldl'|+foldl' :: Monad m => (b -> a -> b) -> b+       -> Iteratee a m b+foldl' step = continue . loop where+	fold = Data.List.foldl' step+	loop acc stream = case stream of+		Chunks [] -> continue (loop acc)+		Chunks xs -> continue (loop (fold acc xs))+		EOF -> yield acc EOF+:++:f Data/Enumerator.hs+|apidoc Data.Enumerator.foldM|+foldM :: Monad m => (b -> a -> m b) -> b+      -> Iteratee a m b+foldM step = continue . loop where+	fold acc = lift . CM.foldM step acc+	+	loop acc stream = case stream of+		Chunks [] -> continue (loop acc)+		Chunks xs -> fold acc xs >>= continue . loop+		EOF -> yield acc EOF+:++:d Data.Enumerator exports+-- ** Iteratees+, Data.Enumerator.foldl+, Data.Enumerator.foldl'+, Data.Enumerator.foldM+:++\subsection{Enumerators}++At their simplest, enumerators just check to see whether their received step+can accept any more input. If so, input is generated somehow, fed to the step,+and its result checked again. Most enumerators are defined using a+worker/wrapper pair, for efficiency and readability.++Here we define a number of enumerators based on functions from+{\tt Data.List}. Each generator has a monadic and non-monadic form, to+demonstrate how side effects might be ordered with respect to the iteratee's+processing.++{\tt iterate} and {\tt iterateM} apply a function repeatedly to the base+input, passing the results through as a stream.++:f Data/Enumerator.hs+|apidoc Data.Enumerator.iterate|+iterate :: Monad m => (a -> a) -> a -> Enumerator a m b+iterate f = loop where+	loop a (Continue k) = k (Chunks [a]) >>== loop (f a)+	loop _ step = returnI step+:++:f Data/Enumerator.hs+|apidoc Data.Enumerator.iterateM|+iterateM :: Monad m => (a -> m a) -> a+         -> Enumerator a m b+iterateM f base = loop (return base) where+	loop m_a (Continue k) = do+		a <- lift m_a+		k (Chunks [a]) >>== loop (f a)+	loop _ step = returnI step+:++{\tt repeat} and {\tt repeatM} create infinite streams, where each input+is a single value.++:f Data/Enumerator.hs+|apidoc Data.Enumerator.repeat|+repeat :: Monad m => a -> Enumerator a m b+repeat a = Data.Enumerator.iterate (const a) a+:++:f Data/Enumerator.hs+|apidoc Data.Enumerator.repeatM|+repeatM :: Monad m => m a -> Enumerator a m b+repeatM m_a step = do+	a <- lift m_a+	iterateM (const m_a) a step+:++{\tt replicate} and {\tt replicateM} create streams containing a given+quantity of the input value.++:f Data/Enumerator.hs+|apidoc Data.Enumerator.replicateM|+replicateM :: Monad m => Integer -> m a+           -> Enumerator a m b+replicateM maxCount getNext = loop maxCount where+	loop 0 step = returnI step+	loop n (Continue k) = do+		next <- lift getNext+		k (Chunks [next]) >>== loop (n - 1)+	loop _ step = returnI step+:++:f Data/Enumerator.hs+|apidoc Data.Enumerator.replicate|+replicate :: Monad m => Integer -> a+          -> Enumerator a m b+replicate maxCount a = replicateM maxCount (return a)+:++{\tt generateM} runs a monadic computation until it returns {\tt Nothing},+which signals the end of enumeration.++Note that when the enumerator is finished, it does not send {\tt EOF} to+the iteratee. Instead, it returns a continuation, so additional enumerators+may add their own input to the stream.++:f Data/Enumerator.hs+|apidoc Data.Enumerator.generateM|+generateM :: Monad m => m (Maybe a)+          -> Enumerator a m b+generateM getNext = loop where+	loop (Continue k) = do+		next <- lift getNext+		case next of+			Nothing -> continue k+			Just x -> k (Chunks [x]) >>== loop+	loop step = returnI step+:++:d Data.Enumerator exports+-- ** Enumerators+, Data.Enumerator.iterate+, iterateM+, Data.Enumerator.repeat+, repeatM+, Data.Enumerator.replicate+, replicateM+, generateM+:++\subsection{Enumeratees}++Enumeratees are conceptually similar to a monadic {\tt concatMap}; each+outer input element is converted to a list of inner inputs, which are passed+to the inner iteratee. Error handling and performance considerations+make most real-life enumeratees more complex, but some don't need the extra+design.++The {\tt checkDone} and {\tt checkDoneEx} functions referenced here are+defined later, with other utilities.++:f Data/Enumerator.hs+|apidoc Data.Enumerator.concatMapM|+concatMapM :: Monad m => (ao -> m [ai])+           -> Enumeratee ao ai m b+concatMapM f = checkDone (continue . step) where+	step k EOF = yield (Continue k) EOF+	step k (Chunks xs) = loop k xs+	+	loop k [] = continue (step k)+	loop k (x:xs) = do+		fx <- lift (f x)+		k (Chunks fx) >>==+			checkDoneEx (Chunks xs) (\k' -> loop k' xs)+:++Once {\tt concatMapM} is defined, similar enumeratees can be easily created+via small wrappers.++:d excluded Prelude imports+concatMap,+:++:f Data/Enumerator.hs+|apidoc Data.Enumerator.concatMap|+concatMap :: Monad m => (ao -> [ai])+          -> Enumeratee ao ai m b+concatMap f = concatMapM (return . f)+:++:f Data/Enumerator.hs+|apidoc Data.Enumerator.map|+map :: Monad m => (ao -> ai)+    -> Enumeratee ao ai m b+map f = concatMap (\x -> Prelude.map f [x])+:++:f Data/Enumerator.hs+|apidoc Data.Enumerator.filter|+filter :: Monad m => (a -> Bool)+       -> Enumeratee a a m b+filter p = concatMap (\x -> Prelude.filter p [x])+:++:f Data/Enumerator.hs+|apidoc Data.Enumerator.mapM|+mapM :: Monad m => (ao -> m ai)+     -> Enumeratee ao ai m b+mapM f = concatMapM (\x -> Prelude.mapM f [x])+:++:f Data/Enumerator.hs+|apidoc Data.Enumerator.filterM|+filterM :: Monad m => (a -> m Bool)+        -> Enumeratee a a m b+filterM p = concatMapM (\x -> CM.filterM p [x])+:++:d Data.Enumerator exports+-- ** Enumeratees+, Data.Enumerator.map+, Data.Enumerator.concatMap+, Data.Enumerator.filter+, Data.Enumerator.mapM+, concatMapM+, Data.Enumerator.filterM+:++\subsection{Debugging}++Debugging enumerator-based code is mostly a question of what inputs are+being passed around. {\tt printChunks} prints out exactly what chunks are+being sent from an enumerator.++:f Data/Enumerator.hs+|apidoc Data.Enumerator.printChunks|+printChunks :: (MonadIO m, Show a)+            => Bool -- ^ Print empty chunks+            -> Iteratee a m ()+printChunks printEmpty = continue loop where+	loop (Chunks xs) = do+		let hide = null xs && not printEmpty+		CM.unless hide (liftIO (print xs))+		continue loop+	+	loop EOF = do+		liftIO (putStrLn "EOF")+		yield () EOF+:++:d Data.Enumerator exports+-- ** Debugging+, printChunks+:
+ src/text.anansi view
@@ -0,0 +1,608 @@+\section{Text}++:f Data/Enumerator/Text.hs+|Data.Enumerator.Text module header|+module Data.Enumerator.Text (+	|Data.Enumerator.Text exports|+	) where+import qualified Prelude+import Prelude hiding (head, drop, takeWhile)+import Data.Enumerator hiding (head, drop)+import qualified Data.Text as T+|Data.Enumerator.Text imports|+:++\subsection{IO}++Reading text is similar to reading bytes, but the enumerators have slightly+different behavior -- instead of reading in fixed-size chunks of data, the+text enumerators read in lines. This matches similar text-based {\sc api}s,+such as Python's {\tt xreadlines()}.++:d Data.Enumerator.Text imports+import Data.Enumerator.Util (tryStep)+import qualified Data.Text.IO as TIO++import qualified Control.Exception as Exc+import Control.Monad.IO.Class (MonadIO)+import qualified System.IO as IO+import System.IO.Error (isEOFError)+:++:f Data/Enumerator/Text.hs+|apidoc Data.Enumerator.Text.enumHandle|+enumHandle :: MonadIO m => IO.Handle+           -> Enumerator T.Text m b+enumHandle h = loop where+	loop (Continue k) = withText $ \maybeText ->+		case maybeText of+			Nothing -> continue k+			Just text -> k (Chunks [text]) >>== loop+	+	loop step = returnI step+	withText = tryStep $ Exc.catch+		(Just `fmap` TIO.hGetLine h)+		(\err -> if isEOFError err+			then return Nothing+			else Exc.throwIO err)+:++:f Data/Enumerator/Text.hs+|apidoc Data.Enumerator.Text.enumFile|+enumFile :: FilePath -> Enumerator T.Text IO b+enumFile path = enum where+	withHandle = tryStep (IO.openFile path IO.ReadMode)+	enum step = withHandle $ \h -> Iteratee $ Exc.finally+		(runIteratee (enumHandle h step))+		(IO.hClose h)+:++:f Data/Enumerator/Text.hs+|apidoc Data.Enumerator.Text.iterHandle|+iterHandle :: MonadIO m => IO.Handle+           -> Iteratee T.Text m ()+iterHandle h = continue step where+	step EOF = yield () EOF+	step (Chunks []) = continue step+	step (Chunks chunks) = let+		put = mapM_ (TIO.hPutStr h) chunks+		in tryStep put (\_ -> continue step)+:++:d Data.Enumerator.Text exports+  -- * Text IO+  enumHandle+, enumFile+, iterHandle+:++\subsection{List analogues}++:d Data.Enumerator.Text imports+import qualified Data.Text.Lazy as TL+:++:f Data/Enumerator/Text.hs+toChunks :: TL.Text -> Stream T.Text+toChunks = Chunks . TL.toChunks+:++:f Data/Enumerator/Text.hs+|apidoc Data.Enumerator.Text.head|+head :: Monad m => Iteratee T.Text m (Maybe Char)+head = continue loop where+	loop (Chunks xs) = case TL.uncons (TL.fromChunks xs) of+		Just (char, extra) -> yield (Just char) (toChunks extra)+		Nothing -> head+	loop EOF = yield Nothing EOF+:++:f Data/Enumerator/Text.hs+|apidoc Data.Enumerator.Text.drop|+drop :: Monad m => Integer -> Iteratee T.Text m ()+drop n | n <= 0 = return ()+drop n = continue (loop n) where+	loop n' (Chunks xs) = iter where+		lazy = TL.fromChunks xs+		len = toInteger (TL.length lazy)+		iter = if len < n'+			then drop (n' - len)+			else yield () (toChunks (TL.drop (fromInteger n') lazy))+	loop _ EOF = yield () EOF+:++:f Data/Enumerator/Text.hs+|apidoc Data.Enumerator.Text.dropWhile|+dropWhile :: Monad m => (Char -> Bool) -> Iteratee T.Text m ()+dropWhile p = continue loop where+	loop (Chunks xs) = iter where+		lazy = TL.dropWhile p (TL.fromChunks xs)+		iter = if TL.null lazy+			then continue loop+			else yield () (toChunks lazy)+	loop EOF = yield () EOF+:++:f Data/Enumerator/Text.hs+|apidoc Data.Enumerator.Text.take|+take :: Monad m => Integer -> Iteratee T.Text m TL.Text+take n | n <= 0 = return TL.empty+take n = continue (loop id n) where+	loop acc n' (Chunks xs) = iter where+		lazy = TL.fromChunks xs+		len = toInteger (TL.length lazy)+		+		iter = if len < n'+			then continue (loop (acc . (TL.append lazy)) (n' - len))+			else let+				(xs', extra) = TL.splitAt (fromInteger n') lazy+				in yield (acc xs') (toChunks extra)+	loop acc _ EOF = yield (acc TL.empty) EOF+:++:f Data/Enumerator/Text.hs+|apidoc Data.Enumerator.Text.takeWhile|+takeWhile :: Monad m => (Char -> Bool) -> Iteratee T.Text m TL.Text+takeWhile p = continue (loop id) where+	loop acc (Chunks []) = continue (loop acc)+	loop acc (Chunks xs) = iter where+		lazy = TL.fromChunks xs+		(xs', extra) = tlSpanBy p lazy+		iter = if TL.null extra+			then continue (loop (acc . (TL.append lazy)))+			else yield (acc xs') (toChunks extra)+	loop acc EOF = yield (acc TL.empty) EOF+:++:f Data/Enumerator/Text.hs+|apidoc Data.Enumerator.Text.consume|+consume :: Monad m => Iteratee T.Text m TL.Text+consume = continue (loop id) where+	loop acc (Chunks []) = continue (loop acc)+	loop acc (Chunks xs) = iter where+		lazy = TL.fromChunks xs+		iter = continue (loop (acc . (TL.append lazy)))+	loop acc EOF = yield (acc TL.empty) EOF+:++:f Data/Enumerator/Text.hs+|apidoc Data.Enumerator.Text.require|+require :: Monad m => Integer -> Iteratee T.Text m ()+require n | n <= 0 = return ()+require n = continue (loop id n) where+	loop acc n' (Chunks xs) = iter where+		lazy = TL.fromChunks xs+		len = toInteger (TL.length lazy)+		iter = if len < n'+			then continue (loop (acc . (TL.append lazy)) (n' - len))+			else yield () (toChunks (acc lazy))+	loop _ _ EOF = throwError (Exc.ErrorCall "require: Unexpected EOF")+:++Same caveats as {\tt Data.Enumerator.List.isolate}++:f Data/Enumerator/Text.hs+|apidoc Data.Enumerator.Text.isolate|+isolate :: Monad m => Integer -> Enumeratee T.Text T.Text m b+isolate n step | n <= 0 = return step+isolate n (Continue k) = continue loop where+	loop (Chunks []) = continue loop+	loop (Chunks xs) = iter where+		lazy = TL.fromChunks xs+		len = toInteger (TL.length lazy)+		+		iter = if len <= n+			then k (Chunks xs) >>== isolate (n - len)+			else let+				(s1, s2) = TL.splitAt (fromInteger n) lazy+				in k (toChunks s1) >>== (\step -> yield step (toChunks s2))+	loop EOF = k EOF >>== (\step -> yield step EOF)+isolate n step = drop n >> return step+:++:d Data.Enumerator.Text exports+-- * List analogues+, Data.Enumerator.Text.head+, Data.Enumerator.Text.drop+, Data.Enumerator.Text.dropWhile+, Data.Enumerator.Text.take+, Data.Enumerator.Text.takeWhile+, Data.Enumerator.Text.consume+, require+, isolate+:++\subsection{Codecs}++Many protocols need the non-blocking input behavior of binary \io{}, but+are defined in terms of unicode characters. The {\tt encode} and+{\tt decode} enumeratees allow text-based protocols to be easily parsed+from a binary input source.++Most common codecs ({\sc utf-8}, {\sc iso-8859-1}, {\sc ascii}) are+supported; more complex codecs can be implemented by bindings to libraries+such as libicu.++All of the codecs here are incremental; that is, they try to read as much+data as possible, but no more. This allows iteratees to read partial data+if the input stream contains invalid data.++:d Data.Enumerator.Text imports+import qualified Data.ByteString as B+import Data.Enumerator.Util (tSpanBy, tlSpanBy, reprWord, reprChar)+:++:d Data.Enumerator.Text exports+  -- * Codecs+, Codec+, encode+, decode+|text codec exports|+:++:f Data/Enumerator/Text.hs+data Codec = Codec+	{ codecName :: T.Text+	, codecEncode+		:: T.Text+		-> (B.ByteString, Maybe (Exc.SomeException, T.Text))+	, codecDecode+		:: B.ByteString+		-> (T.Text, Either+			(Exc.SomeException, B.ByteString)+			B.ByteString)+	}++instance Show Codec where+	showsPrec d c = showParen (d > 10) $+		showString "Codec " . shows (codecName c)+:++:f Data/Enumerator/Text.hs+|apidoc Data.Enumerator.Text.encode|+encode :: Monad m => Codec+       -> Enumeratee T.Text B.ByteString m b+encode codec = checkDone (continue . step) where+	step k EOF = yield (Continue k) EOF+	step k (Chunks xs) = loop k xs+	+	loop k [] = continue (step k)+	loop k (x:xs) = let+		(bytes, extra) = codecEncode codec x+		extraChunks = Chunks $ case extra of+			Nothing -> xs+			Just (_, text) -> text:xs+		+		checkError k' = case extra of+			Nothing -> loop k' xs+			Just (exc, _) -> throwError exc+		+		in if B.null bytes+			then checkError k+			else k (Chunks [bytes]) >>==+				checkDoneEx extraChunks checkError+:++:f Data/Enumerator/Text.hs+|apidoc Data.Enumerator.Text.decode|+decode :: Monad m => Codec+       -> Enumeratee B.ByteString T.Text m b+decode codec = checkDone (continue . step B.empty) where+	step _   k EOF = yield (Continue k) EOF+	step acc k (Chunks xs) = loop acc k xs+	+	loop acc k [] = continue (step acc k)+	loop acc k (x:xs) = let+		(text, extra) = codecDecode codec (B.append acc x)+		extraChunks = Chunks (either snd id extra : xs)+		+		checkError k' = case extra of+			Left (exc, _) -> throwError exc+			Right bytes -> loop bytes k' xs+		+		in if T.null text+			then checkError k+			else k (Chunks [text]) >>==+				checkDoneEx extraChunks checkError+:++Most of the codecs here need to perform at least basic bitbashing, to+calculate how many input bytes will be needed for the next character.++:d Data.Enumerator.Text imports+import Control.Arrow (first)+import Data.Bits ((.&.), (.|.), shiftL)+import Data.Char (ord)+import Data.Word (Word8, Word16)+import qualified Data.ByteString.Char8 as B8+import qualified Data.Text.Encoding as TE+:++The variable-width decoders all follow the same basic pattern. First,+they examine their input to calculate how many bytes the decoder+function should accept. Next they try to decode it -- if the input+is valid, decoding is finished.++If the input is invalid, trying to decode the full input will throw+an exception. When an exception is caught, decoding is passed off to+{\tt splitSlowly} for a more careful parse. The input is reduced until+the decoder can parse something, and the rest of the bytes are stored+for later. An error will only be thrown if the iteratee requires input,+but there are no valid bytes remaining.++:f Data/Enumerator/Text.hs+byteSplits :: B.ByteString+           -> [(B.ByteString, B.ByteString)]+byteSplits bytes = loop (B.length bytes) where+	loop 0 = [(B.empty, bytes)]+	loop n = B.splitAt n bytes : loop (n - 1)+:++:d Data.Enumerator.Text imports+import Data.Maybe (catMaybes)+:++:f Data/Enumerator/Text.hs+splitSlowly :: (B.ByteString -> T.Text)+            -> B.ByteString+            -> (T.Text, Either+            	(Exc.SomeException, B.ByteString)+            	B.ByteString)+splitSlowly dec bytes = valid where+	valid = firstValid (Prelude.map decFirst splits)+	splits = byteSplits bytes+	firstValid = Prelude.head . catMaybes+	tryDec = tryEvaluate . dec+	+	decFirst (a, b) = case tryDec a of+		Left _ -> Nothing+		Right text -> Just (text, case tryDec b of+			Left exc -> Left (exc, b)+			+			-- this case shouldn't occur, since splitSlowly+			-- is only called when parsing failed somewhere+			Right _ -> Right B.empty)+:++\subsubsection{UTF-8}++:d text codec exports+, utf8+:++:f Data/Enumerator/Text.hs+utf8 :: Codec+utf8 = Codec name enc dec where+	name = T.pack "UTF-8"+	enc text = (TE.encodeUtf8 text, Nothing)+	dec bytes = case splitQuickly bytes of+		Just (text, extra) -> (text, Right extra)+		Nothing -> splitSlowly TE.decodeUtf8 bytes+	|utf8 split bytes|+:++:d utf8 split bytes+splitQuickly bytes = loop 0 >>= maybeDecode where+	|utf8 required bytes count|+	maxN = B.length bytes+	+	loop n | n == maxN = Just (TE.decodeUtf8 bytes, B.empty)+	loop n = let+		req = required (B.index bytes n)+		tooLong = first TE.decodeUtf8 (B.splitAt n bytes)+		decodeMore = loop $! n + req+		in if req == 0+			then Nothing+			else if n + req > maxN+				then Just tooLong+				else decodeMore+:++:d utf8 required bytes count+required x0+	| x0 .&. 0x80 == 0x00 = 1+	| x0 .&. 0xE0 == 0xC0 = 2+	| x0 .&. 0xF0 == 0xE0 = 3+	| x0 .&. 0xF8 == 0xF0 = 4+	+	-- Invalid input; let Text figure it out+	| otherwise           = 0+:++\subsubsection{UTF-16}++:d text codec exports+, utf16_le+, utf16_be+:++:f Data/Enumerator/Text.hs+utf16_le :: Codec+utf16_le = Codec name enc dec where+	name = T.pack "UTF-16-LE"+	enc text = (TE.encodeUtf16LE text, Nothing)+	dec bytes = case splitQuickly bytes of+		Just (text, extra) -> (text, Right extra)+		Nothing -> splitSlowly TE.decodeUtf16LE bytes+	|utf16-le split bytes|+:++:f Data/Enumerator/Text.hs+utf16_be :: Codec+utf16_be = Codec name enc dec where+	name = T.pack "UTF-16-BE"+	enc text = (TE.encodeUtf16BE text, Nothing)+	dec bytes = case splitQuickly bytes of+		Just (text, extra) -> (text, Right extra)+		Nothing -> splitSlowly TE.decodeUtf16BE bytes+	|utf16-be split bytes|+:++:d utf16-le split bytes+splitQuickly bytes = maybeDecode (loop 0) where+	maxN = B.length bytes+	+	loop n |  n      == maxN = decodeAll+	       | (n + 1) == maxN = decodeTo n+	loop n = let+		req = utf16Required+			(B.index bytes 0)+			(B.index bytes 1)+		decodeMore = loop $! n + req+		in if n + req > maxN+			then decodeTo n+			else decodeMore+	+	decodeTo n = first TE.decodeUtf16LE (B.splitAt n bytes)+	decodeAll = (TE.decodeUtf16LE bytes, B.empty)+:++:d utf16-be split bytes+splitQuickly bytes = maybeDecode (loop 0) where+	maxN = B.length bytes+	+	loop n |  n      == maxN = decodeAll+	       | (n + 1) == maxN = decodeTo n+	loop n = let+		req = utf16Required+			(B.index bytes 1)+			(B.index bytes 0)+		decodeMore = loop $! n + req+		in if n + req > maxN+			then decodeTo n+			else decodeMore+	+	decodeTo n = first TE.decodeUtf16BE (B.splitAt n bytes)+	decodeAll = (TE.decodeUtf16BE bytes, B.empty)+:++:f Data/Enumerator/Text.hs+utf16Required :: Word8 -> Word8 -> Int+utf16Required x0 x1 = required where+	required = if x >= 0xD800 && x <= 0xDBFF+		then 4+		else 2+	x :: Word16+	x = (fromIntegral x1 `shiftL` 8) .|. fromIntegral x0+:++\subsubsection{UTF-32}++:d text codec exports+, utf32_le+, utf32_be+:++:f Data/Enumerator/Text.hs+utf32_le :: Codec+utf32_le = Codec name enc dec where+	name = T.pack "UTF-32-LE"+	enc text = (TE.encodeUtf32LE text, Nothing)+	dec bs = case utf32SplitBytes TE.decodeUtf32LE bs of+		Just (text, extra) -> (text, Right extra)+		Nothing -> splitSlowly TE.decodeUtf32LE bs++utf32_be :: Codec+utf32_be = Codec name enc dec where+	name = T.pack "UTF-32-BE"+	enc text = (TE.encodeUtf32BE text, Nothing)+	dec bs = case utf32SplitBytes TE.decodeUtf32BE bs of+		Just (text, extra) -> (text, Right extra)+		Nothing -> splitSlowly TE.decodeUtf32BE bs+:++:f Data/Enumerator/Text.hs+utf32SplitBytes :: (B.ByteString -> T.Text)+                -> B.ByteString+                -> Maybe (T.Text, B.ByteString)+utf32SplitBytes dec bytes = split where+	split = maybeDecode (dec toDecode, extra)+	len = B.length bytes+	lenExtra = mod len 4+	+	lenToDecode = len - lenExtra+	(toDecode, extra) = if lenExtra == 0+		then (bytes, B.empty)+		else B.splitAt lenToDecode bytes+:++\subsubsection{ASCII}++:d text codec exports+, ascii+:++:f Data/Enumerator/Text.hs+ascii :: Codec+ascii = Codec name enc dec where+	name = T.pack "ASCII"+	enc text = (bytes, extra) where+		(safe, unsafe) = tSpanBy (\c -> ord c <= 0x7F) text+		bytes = B8.pack (T.unpack safe)+		extra = if T.null unsafe+			then Nothing+			else Just (illegalEnc name (T.head unsafe), unsafe)+	+	dec bytes = (text, extra) where+		(safe, unsafe) = B.span (<= 0x7F) bytes+		text = T.pack (B8.unpack safe)+		extra = if B.null unsafe+			then Right B.empty+			else Left (illegalDec name (B.head unsafe), unsafe)+:++\subsubsection{ISO 8859-1}++:d text codec exports+, iso8859_1+:++:f Data/Enumerator/Text.hs+iso8859_1 :: Codec+iso8859_1 = Codec name enc dec where+	name = T.pack "ISO-8859-1"+	enc text = (bytes, extra) where+		(safe, unsafe) = tSpanBy (\c -> ord c <= 0xFF) text+		bytes = B8.pack (T.unpack safe)+		extra = if T.null unsafe+			then Nothing+			else Just (illegalEnc name (T.head unsafe), unsafe)+	+	dec bytes = (T.pack (B8.unpack bytes), Right B.empty)+:++\subsection{Encoding Utilities}++:f Data/Enumerator/Text.hs+illegalEnc :: T.Text -> Char -> Exc.SomeException+illegalEnc name c = Exc.toException . Exc.ErrorCall $+	concat [ "Codec "+	       , show name+	       , " can't encode character "+	       , reprChar c+	       ]+:++:f Data/Enumerator/Text.hs+illegalDec :: T.Text -> Word8 -> Exc.SomeException+illegalDec name w = Exc.toException . Exc.ErrorCall $+	concat [ "Codec "+	       , show name+	       , " can't decode byte "+	       , reprWord w+	       ]+:++:d Data.Enumerator.Text imports+import System.IO.Unsafe (unsafePerformIO)+:++:f Data/Enumerator/Text.hs+tryEvaluate :: a -> Either Exc.SomeException a+tryEvaluate = unsafePerformIO . Exc.try . Exc.evaluate++maybeDecode:: (a, b) -> Maybe (a, b)+maybeDecode (a, b) = case tryEvaluate a of+	Left _ -> Nothing+	Right _ -> Just (a, b)+:
+ src/types.anansi view
@@ -0,0 +1,337 @@+\section{Core types}++Most of this library's types and functions are exported from the+{\tt Data.Enumerator} module.++:f Data/Enumerator.hs+|Data.Enumerator module header|+module Data.Enumerator (+	|Data.Enumerator exports|+	) where+|Data.Enumerator imports|+:++\noindent A few utility functions share names with functions from the Prelude, so+those are removed from the default namespace.++:d Data.Enumerator imports+import qualified Prelude as Prelude+import Prelude hiding (+	|excluded Prelude imports|+	)+:++:d Data.Enumerator exports+-- * Core+-- ** Types+  Stream (..)+, Iteratee (..)+, Step (..)+, Enumerator+, Enumeratee+:++\subsection{Input streams}++A {\tt Stream} is a sequence of chunks generated by an enumerator or+enumeratee. Chunks might be composite values, such as a string, or atomic,+such as a parser event. Allowing a stream to support multiple chunks+slightly complicates iteratee and enumeratee implementation, but greatly+simplifies handling of leftover inputs.++{\tt (Chunks [])} is a legal value, used when a stream is still active but+no data is currently available. Iteratees and enumeratees often special-case+empty chunks for performance reasons, though they're not required to.++:f Data/Enumerator.hs+|apidoc Data.Enumerator.Stream|+data Stream a+	= Chunks [a]+	| EOF+	deriving (Show, Eq)++instance Monad Stream where+	return = Chunks . return+	Chunks xs >>= f = mconcat (fmap f xs)+	EOF >>= _ = EOF++instance Functor Stream where+	fmap f (Chunks xs) = Chunks (fmap f xs)+	fmap _ EOF = EOF++instance A.Applicative Stream where+	pure = return+	(<*>) = CM.ap+:++The {\tt Monoid} instance deserves some special attention, because it has+the unexpected behavior that {\tt mappend EOF (Chunks []) == EOF}. Although+it's reasonable that appending chunks to an {\sc eof} stream should provide+a valid stream, such behavior would violate the monoid laws.++:d Data.Enumerator imports+import Data.Monoid (Monoid, mempty, mappend, mconcat)+:++:f Data/Enumerator.hs+instance Monoid (Stream a) where+	mempty = Chunks mempty+	mappend (Chunks xs) (Chunks ys) = Chunks (xs ++ ys)+	mappend _ _ = EOF+:++\subsection{Iteratees}++The primary data type for this library is {\tt Iteratee}, which consumes+input until it either generates a value or encounters an error. Rather+than requiring all input at once, an iteratee will return {\tt Continue}+when it is capable of processing more data.++In general, iteratees begin in the {\tt Continue} state. As each chunk is+passed to the continuation, the iteratee may return the next step, which is+one of:++\begin{itemize}+\item {\tt Continue}: The iteratee is capable of accepting more input. Note+that more input is not required; the iteratee might be able to generate a+value immediately if the stream ends.++\item {\tt Yield}: The iteratee has received enough input to generate a+result. Included in this value is left-over input, which can be passed to+the next iteratee.++\item {\tt Error}: The iteratee encountered an error which prevents it from+proceeding further.+\end{itemize}++:d Data.Enumerator imports+import qualified Control.Exception as Exc+:++:f Data/Enumerator.hs+data Step a m b+	|apidoc Data.Enumerator.Continue|+	= Continue (Stream a -> Iteratee a m b)+	+	|apidoc Data.Enumerator.Yield|+	| Yield b (Stream a)+	+	|apidoc Data.Enumerator.Error|+	| Error Exc.SomeException++|apidoc Data.Enumerator.Iteratee|+newtype Iteratee a m b = Iteratee+	{ runIteratee :: m (Step a m b)+	}+:++Users often need to construct iteratees which only yield or continue,+so we define some helper functions to save typing:++:f Data/Enumerator.hs+|apidoc Data.Enumerator.returnI|+returnI :: Monad m => Step a m b -> Iteratee a m b+returnI step = Iteratee (return step)++|apidoc Data.Enumerator.yield|+yield :: Monad m => b -> Stream a -> Iteratee a m b+yield x extra = returnI (Yield x extra)++|apidoc Data.Enumerator.continue|+continue :: Monad m => (Stream a -> Iteratee a m b)+         -> Iteratee a m b+continue k = returnI (Continue k)+:++:d Data.Enumerator exports+, returnI+, yield+, continue+:++\subsection{Enumerators}++Enumerators typically read from an external source (parser, handle, random+number generator, etc). They feed chunks into an iteratee until the source+runs out of data (triggering {\tt EOF}) or the iteratee finishes processing+(yields a value).++Since {\tt Iteratee} is an alias for {\tt m (Step a m b)}, enumerators can+also be considered step transformers of type+{\tt Step a m b -> m (Step a m b)}.++:f Data/Enumerator.hs+|apidoc Data.Enumerator.Enumerator|+type Enumerator a m b = Step a m b -> Iteratee a m b+:++Although enumerators can be encoded as a simple step transformer with the+type {\tt Step a m b -> Step a m b}, encoding as a computation allows easier+reasoning about the order of side effects. Consider the case of enumerating+two files:++:d enumerator example+let iterFoo = enumFile "foo.txt" iterWhatever+let iterBar = enumFile "bar.txt" iterFoo+:++It's impossible to determine, merely by looking at these lines, which file+will be opened first. In fact, depending on the implementation of+{\tt enumFile}, both files might be open at the same time. If enumerators+return monadic values, the order of events is more clear:++:d enumerator example+iterFoo <- enumFile "foo.txt" iterWhatever+iterBar <- enumFile "bar.txt" iterFoo+:++\subsection{Enumeratees}++In cases where an enumerator acts as both a source and sink, the resulting+type is named an {\tt Enumeratee}. Enumeratees have two input types,+``outer a'' ({\tt ao}) and ``inner a'' ({\tt ai}).++Enumeratees are encoded as an iteratee stack. The outer iteratee reads from+a stream of \emph{ao} values, transforms them into \emph{ai}, and passes them+to an inner iteratee. This model allows a single outer input to generate many+inner inputs, and vice-versa.++:f Data/Enumerator.hs+|apidoc Data.Enumerator.Enumeratee|+type Enumeratee ao ai m b = Step ai m b+          -> Iteratee ao m (Step ai m b)+:++\subsection{Operators}++Because {\tt Iteratee a m b} is semantically equivalent to+{\tt m (Step a m b)}, several of the monadic combinators ({\tt (>>=)},+{\tt (>=>)}, etc) are useful to save typing when constructing enumerators+and enumeratees. {\tt (>>==)} corresponds to {\tt (>>=)}, {\tt (>==>)} to+{\tt (>=>)}, and so on.++For compatibility, {\tt (==<<)} is aliased to {\tt (\$\$)}.++:f Data/Enumerator.hs+infixl 1 >>==++|apidoc Data.Enumerator.(>>==)|+(>>==) :: Monad m+       => Iteratee a m b+       -> (Step a m b -> Iteratee a' m b')+       -> Iteratee a' m b'+i >>== f = Iteratee (runIteratee i >>= runIteratee . f)+:++:f Data/Enumerator.hs+infixr 1 ==<<++|apidoc Data.Enumerator.(==<<)|+(==<<) :: Monad m+       => (Step a m b -> Iteratee a' m b')+       -> Iteratee a m b+       -> Iteratee a' m b'+(==<<) = flip (>>==)+:++:f Data/Enumerator.hs+infixr 0 $$++|apidoc Data.Enumerator.($$)|+($$) :: Monad m+     => (Step a m b -> Iteratee a' m b')+     -> Iteratee a m b+     -> Iteratee a' m b'+($$) = (==<<)+:++:f Data/Enumerator.hs+infixr 1 >==>++|apidoc Data.Enumerator.(>==>)|+(>==>) :: Monad m+       => Enumerator a m b+       -> (Step a m b -> Iteratee a' m b')+       -> Step a m b+       -> Iteratee a' m b'+(>==>) e1 e2 s = e1 s >>== e2+:++:f Data/Enumerator.hs+infixr 1 <==<++|apidoc Data.Enumerator.(<==<)|+(<==<) :: Monad m+       => (Step a m b -> Iteratee a' m b')+       -> Enumerator a m b+       -> Step a m b+       -> Iteratee a' m b'+(<==<) = flip (>==>)+:++:d Data.Enumerator exports+-- ** Operators+, (>>==)+, (==<<)+, ($$)+, (>==>)+, (<==<)+:++\subsection{Iteratees as Monads}++Iteratees are monads; by sequencing iteratees, very complex processing may+be applied to arbitrary input streams. Iteratees are also applicative+functors and monad transformers.++:f Data/Enumerator.hs+instance Monad m => Monad (Iteratee a m) where+	return x = yield x (Chunks [])+	+	m >>= f = Iteratee $ runIteratee m >>=+		\r1 -> case r1 of+			Continue k -> return (Continue ((>>= f) . k))+			Error err -> return (Error err)+			Yield x (Chunks []) -> runIteratee (f x)+			Yield x extra -> runIteratee (f x) >>=+				\r2 -> case r2 of+					Continue k -> runIteratee (k extra)+					Error err -> return (Error err)+					Yield x' _ -> return (Yield x' extra)+:++Most iteratees are used to wrap \io{} operations, so it's sensible to define+instances for typeclasses from {\tt transformers}.++:d Data.Enumerator imports+import Control.Monad.Trans.Class (MonadTrans, lift)+import Control.Monad.IO.Class (MonadIO, liftIO)+:++:f Data/Enumerator.hs+instance MonadTrans (Iteratee a) where+	lift m = Iteratee (m >>= runIteratee . return)++instance MonadIO m => MonadIO (Iteratee a m) where+	liftIO = lift . liftIO+:++It's probably possible to define {\tt Functor} and {\tt Applicative}+instances for {\tt Iteratee} without a {\tt Monad} constraint, but I haven't+bothered, since every useful operation requires {\tt m} to be a Monad anyway.++:d Data.Enumerator imports+import qualified Control.Applicative as A+import qualified Control.Monad as CM+:++:f Data/Enumerator.hs+instance Monad m => Functor (Iteratee a m) where+	fmap = CM.liftM+:++:f Data/Enumerator.hs+instance Monad m => A.Applicative (Iteratee a m) where+	pure = return+	(<*>) = CM.ap+:
+ src/util.anansi view
@@ -0,0 +1,250 @@+\section{Misc. utilities}++A few special-case utilities that are used by similar libraries, or were+present in previous versions of {\tt enumerator}, or otherwise don't have a+good place to go.++:d Data.Enumerator exports+-- * Misc. utilities+:++\subsection{Enumeratees}++Sequencing a fixed set of enumerators is easy, but for more complex+cases, it's useful to have a small utility wrapper.++:f Data/Enumerator.hs+|apidoc Data.Enumerator.concatEnums|+concatEnums :: Monad m => [Enumerator a m b]+            -> Enumerator a m b+concatEnums = Prelude.foldl (>==>) returnI+:++:d Data.Enumerator exports+, concatEnums+:++{\tt joinI} is used to ``flatten'' enumeratees, to transform them into an+{\tt Iteratee}.++:f Data/Enumerator.hs+|apidoc Data.Enumerator.joinI|+joinI :: Monad m => Iteratee a m (Step a' m b)+      -> Iteratee a m b+joinI outer = outer >>= check where+	check (Continue k) = k EOF >>== \s -> case s of+		Continue _ -> error "joinI: divergent iteratee"+		_ -> check s+	check (Yield x _) = return x+	check (Error e) = throwError e+:++{\tt joinE} is similar, except it flattens an enumerator/enumeratee pair+into a single enumerator.++:f Data/Enumerator.hs+|apidoc Data.Enumerator.joinE|+joinE :: Monad m+      => Enumerator ao m (Step ai m b)+      -> Enumeratee ao ai m b+      -> Enumerator ai m b+joinE enum enee s = Iteratee $ do+	step <- runIteratee (enumEOF $$ enum $$ enee s)+	case step of+		Error err -> return (Error err)+		Yield x _ -> return x+		Continue _ -> error "joinE: divergent iteratee"+:++{\tt sequence} repeatedly runs its parameter to transform the stream.++:f Data/Enumerator.hs+|apidoc Data.Enumerator.sequence|+sequence :: Monad m => Iteratee ao m ai+         -> Enumeratee ao ai m b+sequence i = loop where+	loop = checkDone check+	check k = isEOF >>= \f -> if f+		then yield (Continue k) EOF+		else step k+	step k = i >>= \v -> k (Chunks [v]) >>== loop+:++Another small, useful enumerator separates an input list into chunks, and+sends them to the iteratee. This is useful for testing iteratees in pure+code.++:d Data.Enumerator imports+import Data.List (genericSplitAt)+:++:f Data/Enumerator.hs+|apidoc Data.Enumerator.enumList|+enumList :: Monad m => Integer -> [a] -> Enumerator a m b+enumList n = loop where+	loop xs (Continue k) | not (null xs) = let+		(s1, s2) = genericSplitAt n xs+		in k (Chunks s1) >>== loop s2+	loop _ step = returnI step+:++:d Data.Enumerator exports+, joinI+, joinE+, Data.Enumerator.sequence+, enumList+:++\subsection{Running iteratees}++To simplify running iteratees, {\tt run} sends {\tt EOF} and then examines+the result. It is not possible for the result to be {\tt Continue}, because+{\tt enumEOF} calls {\tt error} for divergent iteratees.++:f Data/Enumerator.hs+|apidoc Data.Enumerator.run|+run :: Monad m => Iteratee a m b+    -> m (Either Exc.SomeException b)+run i = do+	mStep <- runIteratee $ enumEOF ==<< i+	case mStep of+		Error err -> return $ Left err+		Yield x _ -> return $ Right x+		Continue _ -> error "run: divergent iteratee"+:++:f Data/Enumerator.hs+|apidoc Data.Enumerator.enumEOF|+enumEOF :: Monad m => Enumerator a m b+enumEOF (Yield x _) = yield x EOF+enumEOF (Error err) = throwError err+enumEOF (Continue k) = k EOF >>== check where+	check (Continue _) = error "enumEOF: divergent iteratee"+	check s = enumEOF s+:++{\tt run\_} is even more simplified; it's used in simple scripts, where the+user doesn't care about error handling.++:f Data/Enumerator.hs+|apidoc Data.Enumerator.run_|+run_ :: Monad m => Iteratee a m b -> m b+run_ i = run i >>= either Exc.throw return+:++:d Data.Enumerator exports+, enumEOF+, run+, run_+:++\subsection{{\tt checkDone} and {\tt checkDoneEx}}++A common pattern in {\tt Enumeratee} implementations is to check whether+the inner {\tt Iteratee} has finished, and if so, to return its output.+{\tt checkDone} passes its parameter a continuation if the {\tt Iteratee}+can still consume input, or yields otherwise.++Oleg's version of {\tt checkDone} has a problem---when the enumeratee has+some sort of input buffer, but the underlying iteratee enters {\tt Yield},+it will discard the output buffer. {\tt checkDoneEx} corrects this; for+backwards compatibility, {\tt checkDone} remains.++:f Data/Enumerator.hs+|apidoc Data.Enumerator.checkDoneEx|+checkDoneEx :: Monad m =>+	Stream a' ->+	((Stream a -> Iteratee a m b) -> Iteratee a' m (Step a m b)) ->+	Enumeratee a' a m b+checkDoneEx _     f (Continue k) = f k+checkDoneEx extra _ step         = yield step extra++|apidoc Data.Enumerator.checkDone|+checkDone :: Monad m =>+	((Stream a -> Iteratee a m b) -> Iteratee a' m (Step a m b)) ->+	Enumeratee a' a m b+checkDone = checkDoneEx (Chunks [])+:++:d Data.Enumerator exports+, checkDone+, checkDoneEx+:++:f Data/Enumerator.hs+|apidoc Data.Enumerator.isEOF|+isEOF :: Monad m => Iteratee a m Bool+isEOF = continue $ \s -> case s of+	EOF -> yield True s+	_ -> yield False s+:++:d Data.Enumerator exports+, isEOF+:++{\tt Data.Enumerator.Util} is a hidden module for functions used by several+public modules, but not logically part of the {\tt enumerator} API.++:f Data/Enumerator/Util.hs+{-# LANGUAGE CPP #-}+module Data.Enumerator.Util where+import Data.Enumerator++import Data.Char (toUpper, intToDigit, ord)+import Data.Word (Word8)+import qualified Data.Text as T+import qualified Data.Text.Lazy as TL++import Control.Monad.IO.Class (MonadIO, liftIO)+import qualified Control.Exception as Exc+import Numeric (showIntAtBase)+:++:f Data/Enumerator/Util.hs+tryStep :: MonadIO m => IO t -> (t -> Iteratee a m b) -> Iteratee a m b+tryStep get io = do+	tried <- liftIO (Exc.try get)+	case tried of+		Right t -> io t+		Left err -> throwError (err :: Exc.SomeException)+:++:f Data/Enumerator/Util.hs+pad0 :: Int -> String -> String+pad0 size str = padded where+	len = Prelude.length str+	padded = if len >= size+		then str+		else Prelude.replicate (size - len) '0' ++ str+:++:f Data/Enumerator/Util.hs+reprChar :: Char -> String+reprChar c = "U+" ++ (pad0 4 (showIntAtBase 16 (toUpper . intToDigit) (ord c) ""))+:++:f Data/Enumerator/Util.hs+reprWord :: Word8 -> String+reprWord w = "0x" ++ (pad0 2 (showIntAtBase 16 (toUpper . intToDigit) w ""))+:++{\tt text-0.11} changed some function names to appease a few bikeshedding+idiots in -cafe; to support it, a bit of compatibility code is needed.++I had a choice between using the preprocessor, or a separate module plus+some Cabal magic. It turns out that {\tt cabal sdist} doesn't properly+handle multiple source directories selected by flags, so the preprocessor+is used for now.++:f Data/Enumerator/Util.hs+tSpanBy  :: (Char -> Bool) -> T.Text -> (T.Text, T.Text)+tlSpanBy :: (Char -> Bool) -> TL.Text -> (TL.Text, TL.Text)+#if MIN_VERSION_text(0,11,0)+tSpanBy = T.span+tlSpanBy = TL.span+#else+tSpanBy = T.spanBy+tlSpanBy = TL.spanBy+#endif+:
+ tests/Properties.hs view
@@ -0,0 +1,816 @@+-- Copyright (C) 2010 John Millikin <jmillikin@gmail.com>+--+-- See license.txt for details+module Main (tests, main) where++import Data.Enumerator (($$))+import qualified Data.Enumerator as E+import qualified Data.Enumerator.Binary as EB+import qualified Data.Enumerator.Text as ET+import qualified Data.Enumerator.List as EL++import qualified Data.ByteString as B+import qualified Data.ByteString.Lazy as BL+import qualified Data.ByteString.Char8 as B8+import qualified Data.Text as T+import qualified Data.Text.Lazy as TL+import qualified Data.Text.Encoding as TE++import Test.QuickCheck hiding ((.&.))+import Test.QuickCheck.Poly+import qualified Test.Framework as F+import Test.Framework.Providers.QuickCheck2 (testProperty)++import Control.Applicative+import Control.Exception+import Control.Monad+import Data.Bits+import Data.Char (chr)+import Data.Monoid+import Data.Functor.Identity+import Data.String++tests :: [F.Test]+tests =+	[ test_StreamInstances+	, test_Primitives+	, test_Text+	, test_ListAnalogues+	, test_Other+	]++main :: IO ()+main = F.defaultMain tests++-- Stream instances {{{++test_StreamInstances :: F.Test+test_StreamInstances = F.testGroup "Stream Instances"+	[ test_StreamMonoid+	, test_StreamFunctor+	, test_StreamApplicative+	, test_StreamMonad+	]++test_StreamMonoid :: F.Test+test_StreamMonoid = F.testGroup "Monoid Stream" props where+	props = [ testProperty "law 1" prop_law1+	        , testProperty "law 2" prop_law2+	        , testProperty "law 3" prop_law3+	        , testProperty "law 4" prop_law4+	        ]+	+	prop_law1 :: E.Stream A -> Bool+	prop_law1 x = mappend mempty x == x+	+	prop_law2 :: E.Stream A -> Bool+	prop_law2 x = mappend x mempty == x+	+	prop_law3 :: E.Stream A -> E.Stream A -> E.Stream A -> Bool+	prop_law3 x y z = mappend x (mappend y z) == mappend (mappend x y) z+	+	prop_law4 :: [E.Stream A] -> Bool+	prop_law4 xs = mconcat xs == foldr mappend mempty xs++test_StreamFunctor :: F.Test+test_StreamFunctor = F.testGroup "Functor Stream" props where+	props = [ testProperty "law 1" prop_law1+	        , testProperty "law 2" prop_law2+	        ]+	+	prop_law1 :: E.Stream A -> Bool+	prop_law1 x = fmap id x == id x+	+	prop_law2 :: E.Stream A -> Blind (B -> C) -> Blind (A -> B) -> Bool+	prop_law2 x (Blind f) (Blind g) = fmap (f . g) x == (fmap f . fmap g) x++test_StreamApplicative :: F.Test+test_StreamApplicative = F.testGroup "Applicative Stream" props where+	props = [ testProperty "law 1" prop_law1+	        , testProperty "law 2" prop_law2+	        , testProperty "law 3" prop_law3+	        , testProperty "law 4" prop_law4+	        , testProperty "law 5" prop_law5+	        ]+	+	prop_law1 :: E.Stream A -> Bool+	prop_law1 v = (pure id <*> v) == v+	+	prop_law2 :: Blind (E.Stream (B -> C)) -> Blind (E.Stream (A -> B)) -> E.Stream A -> Bool+	prop_law2 (Blind u) (Blind v) w = (pure (.) <*> u <*> v <*> w) == (u <*> (v <*> w))+	+	prop_law3 :: Blind (A -> B) -> A -> Bool+	prop_law3 (Blind f) x = (pure f <*> pure x) == (pure (f x) `asTypeOf` E.Chunks [B 0])+	+	prop_law4 :: Blind (E.Stream (A -> B)) -> A -> Bool+	prop_law4 (Blind u) y = (u <*> pure y) == (pure ($ y) <*> u)+	+	prop_law5 :: Blind (A -> B) -> E.Stream A -> Bool+	prop_law5 (Blind f) x = (fmap f x) == (pure f <*> x)++test_StreamMonad :: F.Test+test_StreamMonad = F.testGroup "Monad Stream" props where+	props = [ testProperty "law 1" prop_law1+	        , testProperty "law 2" prop_law2+	        , testProperty "law 3" prop_law3+	        ]+	+	prop_law1 :: A -> Blind (A -> E.Stream B) -> Bool+	prop_law1 a (Blind f) = (return a >>= f) == f a+	+	prop_law2 :: E.Stream A -> Bool+	prop_law2 m = (m >>= return) == m+	+	prop_law3 :: E.Stream A -> Blind (A -> E.Stream B) -> Blind (B -> E.Stream C) -> Bool+	prop_law3 m (Blind f) (Blind g) = ((m >>= f) >>= g) == (m >>= (\x -> f x >>= g))++-- }}}++-- Generic properties {{{++test_Enumeratee :: String -> E.Enumeratee A A Identity (Maybe A) -> F.Test+test_Enumeratee name enee = F.testGroup name props where+	props = [ testProperty "incremental" prop_incremental+	        , testProperty "nest errors" prop_nest_errors+	        ]+	+	prop_incremental (Positive n) (NonEmpty xs) = let+		result = runIdentity (E.run_ iter)+		expected = (Just (head xs), tail xs)+		+		iter = E.enumList n xs $$ do+			a <- E.joinI (enee $$ EL.head)+			b <- EL.consume+			return (a, b)+		+		in result == expected+	+	prop_nest_errors (Positive n) (NonEmpty xs) = let+		result = runIdentity (E.run_ iter)+		+		iter = E.enumList n xs $$ do+			a <- enee $$ E.throwError (ErrorCall "")+			EL.consume+		+		in result == xs++-- }}}++-- Primitives {{{++test_Primitives :: F.Test+test_Primitives = F.testGroup "Primitives"+	[ test_Map+	, test_ConcatMap+	, test_MapM+	, test_ConcatMapM+	, test_Filter+	, test_FilterM+	]++test_Map :: F.Test+test_Map = test_Enumeratee "map" (E.map id)++test_ConcatMap :: F.Test+test_ConcatMap = test_Enumeratee "concatMap" (E.concatMap (:[]))++test_MapM :: F.Test+test_MapM = test_Enumeratee "mapM" (E.mapM return)++test_ConcatMapM :: F.Test+test_ConcatMapM = test_Enumeratee "concatMapM" (E.concatMapM (\x -> return [x]))++test_Filter :: F.Test+test_Filter = test_Enumeratee "filter" (E.filter (\_ -> True))++test_FilterM :: F.Test+test_FilterM = test_Enumeratee "filterM" (E.filterM (\_ -> return True))++-- }}}++-- Text encoding / decoding {{{++test_Text :: F.Test+test_Text = F.testGroup "Text"+	[ test_Encoding+	, test_Decoding+	]++test_Encoding :: F.Test+test_Encoding = F.testGroup "Encoding"+	[ test_Encode_ASCII+	, test_Encode_ISO8859+	]++test_Encode_ASCII :: F.Test+test_Encode_ASCII = F.testGroup "ASCII" props where+	props = [ testProperty "works" (forAll genASCII prop_works)+	        , testProperty "error" prop_error+	        , testProperty "lazy" prop_lazy+	        ]+	+	encode iter input =+		runIdentity . E.run $+		E.enumList 1 input $$+		E.joinI (ET.encode ET.ascii $$ iter)+	+	prop_works bytes = result == map B.singleton words where+		Right result = encode EL.consume (map T.singleton chars)+		+		chars = B8.unpack bytes+		words = B.unpack bytes+	+	prop_error = isLeft (encode EL.consume input)  where+		isLeft = either (const True) (const False)+		input = [T.pack "\x61\xFF"]+	+	prop_lazy = either (const False) (== expected) result where+		result = encode EL.head input+		input = [T.pack "\x61\xFF"]+		expected = Just (B.singleton 0x61)++test_Encode_ISO8859 :: F.Test+test_Encode_ISO8859 = F.testGroup "ISO-8859-1" props where+	props = [ testProperty "works" (forAll genISO8859 prop_works)+	        , testProperty "error" prop_error+	        , testProperty "lazy" prop_lazy+	        ]+	+	encode iter input =+		runIdentity . E.run $+		E.enumList 1 input $$+		E.joinI (ET.encode ET.iso8859_1 $$ iter)+	+	prop_works bytes = result == map B.singleton words where+		Right result = encode EL.consume (map T.singleton chars)+		+		chars = B8.unpack bytes+		words = B.unpack bytes+	+	prop_error = isLeft (encode EL.consume input)  where+		isLeft = either (const True) (const False)+		input = [T.pack "\x61\xFF5E"]+	+	prop_lazy = either (const False) (== expected) result where+		result = encode EL.head input+		input = [T.pack "\x61\xFF5E"]+		expected = Just (B.singleton 0x61)++test_Decoding :: F.Test+test_Decoding = F.testGroup "Decoding"+	[ test_Decode_ASCII+	, test_Decode_UTF8+	, test_Decode_UTF16_BE+	, test_Decode_UTF16_LE+	, test_Decode_UTF32_BE+	, test_Decode_UTF32_LE+	]++test_Decode_ASCII :: F.Test+test_Decode_ASCII = F.testGroup "ASCII" props where+	props = [ testProperty "works" (forAll genASCII prop_works)+	        , testProperty "error" prop_error+	        , testProperty "lazy" prop_lazy+	        ]+	+	decode iter input =+		runIdentity . E.run $+		E.enumList 1 input $$+		E.joinI (ET.decode ET.ascii $$ iter)+	+	prop_works text = result == map T.singleton chars where+		Right result = decode EL.consume (map B.singleton bytes)+		+		bytes = B.unpack (TE.encodeUtf8 text)+		chars = T.unpack text+	+	prop_error = isLeft (decode EL.consume input)  where+		isLeft = either (const True) (const False)+		input = [B.pack [0xFF]]+	+	prop_lazy = either (const False) (== expected) result where+		result = decode EL.head input+		input = [B.pack [0x61, 0xFF]]+		expected = Just (T.pack "a")++test_Decode_UTF8 :: F.Test+test_Decode_UTF8 = F.testGroup "UTF-8" props where+	props = [ testProperty "works" prop_works+	        , testProperty "error" prop_error+	        , testProperty "lazy" prop_lazy+	        , testProperty "incremental" prop_incremental+	        ]+	+	decode iter input =+		runIdentity . E.run $+		E.enumList 1 input $$+		E.joinI (ET.decode ET.utf8 $$ iter)+	+	prop_works text = result == map T.singleton chars where+		Right result = decode EL.consume (map B.singleton bytes)+		+		bytes = B.unpack (TE.encodeUtf8 text)+		chars = T.unpack text+	+	prop_error = isLeft (decode EL.consume input)  where+		isLeft = either (const True) (const False)+		input = [B.pack [0x61, 0x80]]+	+	prop_lazy = either (const False) (== expected) result where+		result = decode EL.head input+		input = [B.pack [0x61, 0x80]]+		expected = Just (T.pack "a")+	+	prop_incremental = either (const False) (== expected) result where+		result = decode EL.head input+		input = [B.pack [0x61, 0xC2, 0xC2]]+		expected = Just (T.pack "a")++test_Decode_UTF16_BE :: F.Test+test_Decode_UTF16_BE = F.testGroup "UTF-16-BE" props where+	props = [ testProperty "works" prop_works+	        , testProperty "lazy" prop_lazy+	        , testProperty "error" prop_error+	        , testProperty "incremental" prop_incremental+	        ]+	+	decode iter input =+		runIdentity . E.run $+		E.enumList 1 input $$+		E.joinI (ET.decode ET.utf16_be $$ iter)+	+	prop_works text = result == map T.singleton chars where+		Right result = decode EL.consume (map B.singleton bytes)+		+		bytes = B.unpack (TE.encodeUtf16BE text)+		chars = T.unpack text+	+	prop_lazy = either (const False) (== expected) result where+		result = decode EL.head input+		input = [B.pack [0x00, 0x61, 0xDD, 0x1E]]+		expected = Just (T.pack "a")+	+	prop_error = isLeft (decode EL.consume input)  where+		isLeft = either (const True) (const False)+		input = [B.pack [0x00, 0x61, 0xDD, 0x1E]]+	+	prop_incremental = either (const False) (== expected) result where+		result = decode EL.head input+		input = [B.pack [0x00, 0x61, 0xD8, 0x34, 0xD8, 0xD8]]+		expected = Just (T.pack "a")++test_Decode_UTF16_LE :: F.Test+test_Decode_UTF16_LE = F.testGroup "UTF-16-LE" props where+	props = [ testProperty "works" prop_works+	        , testProperty "lazy" prop_lazy+	        , testProperty "error" prop_error+	        , testProperty "incremental" prop_incremental+	        ]+	+	decode iter input =+		runIdentity . E.run $+		E.enumList 1 input $$+		E.joinI (ET.decode ET.utf16_le $$ iter)+	+	prop_works text = result == map T.singleton chars where+		Right result = decode EL.consume (map B.singleton bytes)+		+		bytes = B.unpack (TE.encodeUtf16LE text)+		chars = T.unpack text+	+	prop_lazy = either (const False) (== expected) result where+		result = decode EL.head input+		input = [B.pack [0x61, 0x00, 0x1E, 0xDD]]+		expected = Just (T.pack "a")+	+	prop_error = isLeft (decode EL.consume input)  where+		isLeft = either (const True) (const False)+		input = [B.pack [0x61, 0x00, 0x1E, 0xDD]]+	+	prop_incremental = either (const False) (== expected) result where+		result = decode EL.head input+		input = [B.pack [0x61, 0x00, 0x34, 0xD8, 0xD8, 0xD8]]+		expected = Just (T.pack "a")++test_Decode_UTF32_BE :: F.Test+test_Decode_UTF32_BE = F.testGroup "UTF-32-BE" props where+	props = [ testProperty "works" prop_works+	        , testProperty "lazy" prop_lazy+	        , testProperty "error" prop_error+	        ]+	+	decode iter input =+		runIdentity . E.run $+		E.enumList 1 input $$+		E.joinI (ET.decode ET.utf32_be $$ iter)+	+	prop_works text = result == map T.singleton chars where+		Right result = decode EL.consume (map B.singleton bytes)+		+		bytes = B.unpack (TE.encodeUtf32BE text)+		chars = T.unpack text+	+	prop_lazy = either (const False) (== expected) result where+		result = decode EL.head input+		input = [B.pack [0x00, 0x00, 0x00, 0x61, 0xFF, 0xFF]]+		expected = Just (T.pack "a")+	+	prop_error = isLeft (decode EL.consume input)  where+		isLeft = either (const True) (const False)+		input = [B.pack [0xFF, 0xFF, 0xFF, 0xFF]]++test_Decode_UTF32_LE :: F.Test+test_Decode_UTF32_LE = F.testGroup "UTF-32-LE" props where+	props = [ testProperty "works" prop_works+	        , testProperty "lazy" prop_lazy+	        , testProperty "error" prop_error+	        ]+	+	decode iter input =+		runIdentity . E.run $+		E.enumList 1 input $$+		E.joinI (ET.decode ET.utf32_le $$ iter)+	+	prop_works text = result == map T.singleton chars where+		Right result = decode EL.consume (map B.singleton bytes)+		+		bytes = B.unpack (TE.encodeUtf32LE text)+		chars = T.unpack text+	+	prop_lazy = either (const False) (== expected) result where+		result = decode EL.head input+		input = [B.pack [0x61, 0x00, 0x00, 0x00, 0xFF, 0xFF]]+		expected = Just (T.pack "a")+	+	prop_error = isLeft (decode EL.consume input)  where+		isLeft = either (const True) (const False)+		input = [B.pack [0xFF, 0xFF, 0xFF, 0xFF]]++-- }}}++-- List analogues {{{++test_ListAnalogues :: F.Test+test_ListAnalogues = F.testGroup "list analogues"+	[ test_ListConsume+	, test_ListHead+	, test_ListDrop+	, test_ListTake+	-- , test_ListPeek+	, test_ListRequire+	, test_ListIsolate+	+	, test_BinaryConsume+	, test_BinaryHead+	, test_BinaryDrop+	, test_BinaryTake+	, test_BinaryRequire+	, test_BinaryIsolate+	+	, test_TextConsume+	, test_TextHead+	, test_TextDrop+	, test_TextTake+	, test_TextRequire+	, test_TextIsolate+	]++test_ListConsume :: F.Test+test_ListConsume = testProperty "List.consume" prop where+	prop :: [A] -> Bool+	prop xs = result == xs where+		result = runIdentity (E.run_ iter)+		iter = E.enumList 1 xs $$ EL.consume++test_ListHead :: F.Test+test_ListHead = testProperty "List.head" prop where+	prop :: [A] -> Bool+	prop xs = result == expected where+		result = runIdentity (E.run_ iter)+		expected = case xs of+			[] -> (Nothing, [])+			(x:xs') -> (Just x, xs')+		+		iter = E.enumList 1 xs $$ do+			x <- EL.head+			extra <- EL.consume+			return (x, extra)++test_ListDrop :: F.Test+test_ListDrop = testProperty "List.drop" prop where+	prop :: Positive Integer -> [A] -> Bool+	prop (Positive n) xs = result == expected where+		result = runIdentity (E.run_ iter)+		expected = drop (fromInteger n) xs+		+		iter = E.enumList 1 xs $$ do+			EL.drop n+			EL.consume++test_ListTake :: F.Test+test_ListTake = testProperty "List.take" prop where+	prop :: Positive Integer -> [A] -> Bool+	prop (Positive n) xs = result == expected where+		result = runIdentity (E.run_ iter)+		expected = splitAt (fromInteger n) xs+		+		iter = E.enumList 1 xs $$ do+			xs <- EL.take n+			extra <- EL.consume+			return (xs, extra)++{-+test_ListPeek :: F.Test+test_ListPeek = testProperty "List.peek" prop where+	prop :: Positive Integer -> [A] -> Bool+	prop (Positive n) xs = result == expected where+		result = runIdentity (E.run_ iter)+		expected = (take (fromInteger n) xs, xs)+		+		iter = E.enumList 1 xs $$ do+			xs <- EL.peek n+			extra <- EL.consume+			return (xs, extra)+-}++test_ListRequire :: F.Test+test_ListRequire = testProperty "List.require" prop where+	prop :: Positive Integer -> [A] -> Bool+	prop (Positive n) xs = result == expected where+		result = case runIdentity (E.run iter) of+			Left exc -> Left (show exc)+			Right x -> Right x+		expected = if n > toInteger (length xs)+			then Left "require: Unexpected EOF"+			else Right xs+		+		iter = E.enumList 1 xs $$ do+			EL.require n+			EL.consume++test_ListIsolate :: F.Test+test_ListIsolate = testProperty "List.isolate" prop where+	prop :: Positive Integer -> [A] -> Bool+	prop (Positive n) xs = result == expected where+		result = runIdentity (E.run_ iter)+		expected = case xs of+			[] -> (Nothing, [])+			(x:[]) -> (Just x, [])+			(x:_:xs') -> (Just x, xs')+		+		iter = E.enumList 1 xs $$ do+			x <- E.joinI (EL.isolate 2 $$ EL.head)+			extra <- EL.consume+			return (x, extra)++test_BinaryConsume :: F.Test+test_BinaryConsume = testProperty "Binary.consume" prop where+	prop ts = result == BL.fromChunks ts where+		result = runIdentity (E.run_ iter)+		iter = E.enumList 1 ts $$ EB.consume++test_BinaryHead :: F.Test+test_BinaryHead = testProperty "Binary.head" prop where+	prop ts = result == expected where+		result = runIdentity (E.run_ iter)+		expected = case BL.uncons (BL.fromChunks ts) of+			Nothing -> (Nothing, BL.empty)+			Just (x, extra) -> (Just x, extra)+		+		iter = E.enumList 1 ts $$ do+			x <- EB.head+			extra <- EB.consume+			return (x, extra)++test_BinaryDrop :: F.Test+test_BinaryDrop = testProperty "Binary.drop" prop where+	prop :: Positive Integer -> [B.ByteString] -> Bool+	prop (Positive n) ts = result == expected where+		result = runIdentity (E.run_ iter)+		expected = BL.drop (fromInteger n) (BL.fromChunks ts)+		+		iter = E.enumList 1 ts $$ do+			EB.drop n+			EB.consume++test_BinaryTake :: F.Test+test_BinaryTake = testProperty "Binary.take" prop where+	prop :: Positive Integer -> [B.ByteString] -> Bool+	prop (Positive n) ts = result == expected where+		result = runIdentity (E.run_ iter)+		expected = BL.splitAt (fromInteger n) (BL.fromChunks ts)+		+		iter = E.enumList 1 ts $$ do+			xs <- EB.take n+			extra <- EB.consume+			return (xs, extra)++test_BinaryRequire :: F.Test+test_BinaryRequire = testProperty "Binary.require" prop where+	prop :: Positive Integer -> [B.ByteString] -> Bool+	prop (Positive n) ts = result == expected where+		result = case runIdentity (E.run iter) of+			Left exc -> Left (show exc)+			Right x -> Right x+		lazy = BL.fromChunks ts+		expected = if n > toInteger (BL.length lazy)+			then Left "require: Unexpected EOF"+			else Right lazy+		+		iter = E.enumList 1 ts $$ do+			EB.require n+			EB.consume++test_BinaryIsolate :: F.Test+test_BinaryIsolate = testProperty "Binary.isolate" prop where+	prop :: Positive Integer -> [B.ByteString] -> Bool+	prop (Positive n) ts = result == expected where+		result = runIdentity (E.run_ iter)+		expected = case BL.unpack (BL.fromChunks ts) of+			[] -> (Nothing, BL.empty)+			(x:[]) -> (Just x, BL.empty)+			(x:_:xs') -> (Just x, BL.pack xs')+		+		iter = E.enumList 1 ts $$ do+			x <- E.joinI (EB.isolate 2 $$ EB.head)+			extra <- EB.consume+			return (x, extra)++test_TextConsume :: F.Test+test_TextConsume = testProperty "Text.consume" prop where+	prop ts = result == TL.fromChunks ts where+		result = runIdentity (E.run_ iter)+		iter = E.enumList 1 ts $$ ET.consume++test_TextHead :: F.Test+test_TextHead = testProperty "Text.head" prop where+	prop ts = result == expected where+		result = runIdentity (E.run_ iter)+		expected = case TL.uncons (TL.fromChunks ts) of+			Nothing -> (Nothing, TL.empty)+			Just (x, extra) -> (Just x, extra)+		+		iter = E.enumList 1 ts $$ do+			x <- ET.head+			extra <- ET.consume+			return (x, extra)++test_TextDrop :: F.Test+test_TextDrop = testProperty "Text.drop" prop where+	prop :: Positive Integer -> [T.Text] -> Bool+	prop (Positive n) ts = result == expected where+		result = runIdentity (E.run_ iter)+		expected = TL.drop (fromInteger n) (TL.fromChunks ts)+		+		iter = E.enumList 1 ts $$ do+			ET.drop n+			ET.consume++test_TextTake :: F.Test+test_TextTake = testProperty "Text.take" prop where+	prop :: Positive Integer -> [T.Text] -> Bool+	prop (Positive n) ts = result == expected where+		result = runIdentity (E.run_ iter)+		expected = TL.splitAt (fromInteger n) (TL.fromChunks ts)+		+		iter = E.enumList 1 ts $$ do+			xs <- ET.take n+			extra <- ET.consume+			return (xs, extra)++test_TextRequire :: F.Test+test_TextRequire = testProperty "Text.require" prop where+	prop :: Positive Integer -> [T.Text] -> Bool+	prop (Positive n) ts = result == expected where+		result = case runIdentity (E.run iter) of+			Left exc -> Left (show exc)+			Right x -> Right x+		lazy = TL.fromChunks ts+		expected = if n > toInteger (TL.length lazy)+			then Left "require: Unexpected EOF"+			else Right lazy+		+		iter = E.enumList 1 ts $$ do+			ET.require n+			ET.consume++test_TextIsolate :: F.Test+test_TextIsolate = testProperty "Text.isolate" prop where+	prop :: Positive Integer -> [T.Text] -> Bool+	prop (Positive n) ts = result == expected where+		result = runIdentity (E.run_ iter)+		expected = case TL.unpack (TL.fromChunks ts) of+			[] -> (Nothing, TL.empty)+			(x:[]) -> (Just x, TL.empty)+			(x:_:xs') -> (Just x, TL.pack xs')+		+		iter = E.enumList 1 ts $$ do+			x <- E.joinI (ET.isolate 2 $$ ET.head)+			extra <- ET.consume+			return (x, extra)++-- }}}++-- Specific functions++test_Other :: F.Test+test_Other = F.testGroup "Other"+	[ test_Sequence+	, test_joinE+	]++test_Sequence :: F.Test+test_Sequence = testProperty "sequence" prop where+	prop :: Positive Integer -> [A] -> Bool+	prop (Positive n) xs = result == expected where+		result = runIdentity (E.run_ iter)+		expected = map Just xs+		+		iter = E.enumList n xs $$ E.joinI (E.sequence EL.head $$ EL.consume)++test_joinE :: F.Test+test_joinE = testProperty "joinE" prop where+	prop :: [Integer] -> Bool+	prop xs = result == expected where+		result = runIdentity (E.run_ iter)+		expected = map (* 10) xs+		+		iter = (E.joinE (E.enumList 1 xs) (E.map (* 10))) $$ EL.consume++-- misc++genASCII :: IsString a => Gen a+genASCII = fmap fromString string where+	string = sized $ \n -> do+		k <- choose (0,n)+		sequence [ char | _ <- [1..k] ]+	+	char = chr `fmap` choose (0,0x7F)++genISO8859 :: IsString a => Gen a+genISO8859 = fmap fromString string where+	string = sized $ \n -> do+		k <- choose (0,n)+		sequence [ char | _ <- [1..k] ]+	+	char = chr `fmap` choose (0,0xFF)++genUnicode :: IsString a => Gen a+genUnicode = fmap fromString string where+	string = sized $ \n -> do+		k <- choose (0,n)+		sequence [ char | _ <- [1..k] ]+	+	excluding :: [a -> Bool] -> Gen a -> Gen a+	excluding bad gen = loop where+		loop = do+			x <- gen+			if or (map ($ x) bad)+				then loop+				else return x+	+	reserved = [lowSurrogate, highSurrogate, noncharacter]+	lowSurrogate c = c >= 0xDC00 && c <= 0xDFFF+	highSurrogate c = c >= 0xD800 && c <= 0xDBFF+	noncharacter c = masked == 0xFFFE || masked == 0xFFFF where+		masked = c .&. 0xFFFF+	+	ascii = choose (0,0x7F)+	plane0 = choose (0xF0, 0xFFFF)+	plane1 = oneof [ choose (0x10000, 0x10FFF)+	               , choose (0x11000, 0x11FFF)+	               , choose (0x12000, 0x12FFF)+	               , choose (0x13000, 0x13FFF)+	               , choose (0x1D000, 0x1DFFF)+	               , choose (0x1F000, 0x1FFFF)+	               ]+	plane2 = oneof [ choose (0x20000, 0x20FFF)+	               , choose (0x21000, 0x21FFF)+	               , choose (0x22000, 0x22FFF)+	               , choose (0x23000, 0x23FFF)+	               , choose (0x24000, 0x24FFF)+	               , choose (0x25000, 0x25FFF)+	               , choose (0x26000, 0x26FFF)+	               , choose (0x27000, 0x27FFF)+	               , choose (0x28000, 0x28FFF)+	               , choose (0x29000, 0x29FFF)+	               , choose (0x2A000, 0x2AFFF)+	               , choose (0x2B000, 0x2BFFF)+	               , choose (0x2F000, 0x2FFFF)+	               ]+	plane14 = choose (0xE0000, 0xE0FFF)+	planes = [ascii, plane0, plane1, plane2, plane14]+	+	char = chr `fmap` excluding reserved (oneof planes)++instance Arbitrary a => Arbitrary (E.Stream a) where+	arbitrary = frequency+		[ (10, return E.EOF)+		, (90, fmap E.Chunks arbitrary)+		]++instance Arbitrary T.Text where+	arbitrary = genUnicode++instance Arbitrary B.ByteString where+	arbitrary = genUnicode
+ tests/enumerator-tests.cabal view
@@ -0,0 +1,17 @@+name: enumerator-tests+version: 0+build-type: Simple+cabal-version: >= 1.6++executable enumerator_tests+  main-is: Properties.hs++  build-depends:+      base > 3 && < 5+    , transformers+    , bytestring+    , text+    , enumerator+    , QuickCheck == 2.4.*+    , test-framework >= 0.2 && < 0.4+    , test-framework-quickcheck2 == 0.2.9