packages feed

enumerator 0.1.1 → 0.2

raw patch · 7 files changed

+348/−156 lines, 7 filesdep +extensible-exceptionsdep +textdep ~basePVP ok

version bump matches the API change (PVP)

Dependencies added: extensible-exceptions, text

Dependency ranges changed: base

API changes (from Hackage documentation)

+ Data.Enumerator.Text: ascii :: Codec
+ Data.Enumerator.Text: data Codec
+ Data.Enumerator.Text: decode :: (Monad m) => Codec -> Enumeratee SomeException ByteString Text m b
+ Data.Enumerator.Text: encode :: (Monad m) => Codec -> Enumeratee SomeException Text ByteString m b
+ Data.Enumerator.Text: enumFile :: FilePath -> Enumerator SomeException Text IO b
+ Data.Enumerator.Text: enumHandle :: Handle -> Enumerator SomeException Text IO b
+ Data.Enumerator.Text: instance Show Codec
+ Data.Enumerator.Text: iso8859_1 :: Codec
+ Data.Enumerator.Text: iterFile :: FilePath -> Iteratee SomeException Text IO ()
+ Data.Enumerator.Text: iterHandle :: Handle -> Iteratee SomeException Text IO ()
+ Data.Enumerator.Text: utf16_be :: Codec
+ Data.Enumerator.Text: utf16_le :: Codec
+ Data.Enumerator.Text: utf32_be :: Codec
+ Data.Enumerator.Text: utf32_le :: Codec
+ Data.Enumerator.Text: utf8 :: Codec

Files

Examples/wc.hs view
@@ -9,17 +9,15 @@ ----------------------------------------------------------------------------- module Main (main) where import Data.Enumerator-import Data.Enumerator.IO+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-import qualified Data.Text.Encoding as T  -- support imports import Control.Exception as E import Data.List-import Data.Bits ((.&.))-import Data.Word (Word8) import Control.Monad (unless, forM_) import System.IO import System.Console.GetOpt@@ -53,24 +51,14 @@ -- -- Since it's possible for the input file's encoding to be non-UTF8, a bit -- of error handling is also required.+--+-- 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 E.SomeException B.ByteString m Integer-iterChars = continue (step (B.empty, 0)) where-	step accT chunk = case chunk of-		EOF -> let (extra, acc) = accT in yield acc (Chunks [extra])-		(Chunks xs) -> case foldl' foldStep (Just accT) xs of-			Just accT' -> continue $ step $! accT'-			Nothing -> throwError (E.SomeException (E.ErrorCall "Invalid UTF-8"))-	-	-- The 'decodeUtf8' function is complicated, and defined later, but-	-- all it does is decode as much input as possible, then return any-	-- remaining bytes.-	-	foldStep Nothing _ = Nothing-	foldStep (Just (extra, acc)) bytes = case decodeUtf8 (B.append extra bytes) of-		Just (text, extra') -> Just (extra', acc + toInteger (T.length text))-		Nothing -> Nothing-+iterChars = joinI (ET.decode ET.utf8 $$ count) where+	count = liftFoldL' (\acc t -> acc + toInteger (T.length t)) 0  main :: IO () main = do@@ -88,7 +76,7 @@ 		putStr $ filename ++ ": " 		 		-- see cat.hs for commented implementation of 'Data.Enumerator.IO.enumFile'-		eitherStat <- run (enumFile filename $$ iter)+		eitherStat <- run (EIO.enumFile filename $$ iter) 		putStrLn $ case eitherStat of 			Left err -> "ERROR: " ++ show err 			Right stat -> show stat@@ -121,86 +109,3 @@ 		exitFailure 	 	return (Prelude.head options, files)---- Incremental UTF-8 validator / decoder--decodeUtf8 :: B.ByteString -> Maybe (T.Text, B.ByteString)-decodeUtf8 allBytes = loop B.empty allBytes where-	-	loop acc bytes | B.null bytes = Just (T.decodeUtf8 acc, bytes)-	loop acc bytes = do-		let (x0, x1, x2, x3) = indexes bytes-		req <- required x0-		if req > B.length bytes-			then Just (T.decodeUtf8 acc, bytes)-			else loop (B.append acc (B.take req bytes)) (B.drop req bytes)-	-	required x0-		| x0 .&. 0x80 == 0x00 = Just 1-		| x0 .&. 0xE0 == 0xC0 = Just 2-		| x0 .&. 0xF0 == 0xE0 = Just 3-		| x0 .&. 0xF8 == 0xF0 = Just 4-		| otherwise           = Nothing-	-	indexes bytes = (x0, x1, x2, x3) where-		x0 = B.index bytes 0-		x1 = B.index bytes 1-		x2 = B.index bytes 2-		x3 = B.index bytes 3---- UTF8 decoding gunk; mostly copied from Data.Text--between :: Word8                -- ^ byte to check-        -> Word8                -- ^ lower bound-        -> Word8                -- ^ upper bound-        -> Bool-between x y z = x >= y && x <= z-{-# INLINE between #-}--validate1    :: Word8 -> Bool-validate1 x1 = between x1 0x00 0x7F-{-# INLINE validate1 #-}--validate2       :: Word8 -> Word8 -> Bool-validate2 x1 x2 = between x1 0xC2 0xDF && between x2 0x80 0xBF-{-# INLINE validate2 #-}--validate3          :: Word8 -> Word8 -> Word8 -> Bool-{-# INLINE validate3 #-}-validate3 x1 x2 x3 = validate3_1 ||-                     validate3_2 ||-                     validate3_3 ||-                     validate3_4-  where-    validate3_1 = (x1 == 0xE0) &&-                  between x2 0xA0 0xBF &&-                  between x3 0x80 0xBF-    validate3_2 = between x1 0xE1 0xEC &&-                  between x2 0x80 0xBF &&-                  between x3 0x80 0xBF-    validate3_3 = x1 == 0xED &&-                  between x2 0x80 0x9F &&-                  between x3 0x80 0xBF-    validate3_4 = between x1 0xEE 0xEF &&-                  between x2 0x80 0xBF &&-                  between x3 0x80 0xBF--validate4             :: Word8 -> Word8 -> Word8 -> Word8 -> Bool-{-# INLINE validate4 #-}-validate4 x1 x2 x3 x4 = validate4_1 ||-                        validate4_2 ||-                        validate4_3-  where -    validate4_1 = x1 == 0xF0 &&-                  between x2 0x90 0xBF &&-                  between x3 0x80 0xBF &&-                  between x4 0x80 0xBF-    validate4_2 = between x1 0xF1 0xF3 &&-                  between x2 0x80 0xBF &&-                  between x3 0x80 0xBF &&-                  between x4 0x80 0xBF-    validate4_3 = x1 == 0xF4 &&-                  between x2 0x80 0x8F &&-                  between x3 0x80 0xBF &&-                  between x4 0x80 0xBF-
enumerator.cabal view
@@ -1,5 +1,5 @@ name: enumerator-version: 0.1.1+version: 0.2 synopsis: Implementation of Oleg Kiselyov's left-fold enumerators license: MIT license-file: license.txt@@ -19,6 +19,7 @@  extra-source-files:   Examples/*.hs+  readme.txt  source-repository head   type: darcs@@ -29,10 +30,22 @@   hs-source-dirs: hs    build-depends:-      base >=3 && < 5-    , transformers >= 0.2 && < 0.3+      transformers >= 0.2 && < 0.3     , bytestring >= 0.9 && < 0.10+    , text >= 0.7 && < 0.8 +  if impl(ghc >= 6.10)+    build-depends:+        base >=4 && < 5+  else+    build-depends:+        base >=3 && < 4+      , extensible-exceptions+   exposed-modules:     Data.Enumerator     Data.Enumerator.IO+    Data.Enumerator.Text++  other-modules:+    Data.Enumerator.Util
hs/Data/Enumerator.hs view
@@ -33,12 +33,14 @@ 	, (>==>) 	, (<==<) 	  -- ** Iteratees+	, run 	, consume 	, isEOF 	, liftTrans 	, liftFoldL 	, liftFoldL' 	, liftFoldM+	, printChunks 	  -- ** Enumerators 	, enumEOF 	, enumList@@ -61,9 +63,6 @@ 	, Data.Enumerator.dropWhile 	, span 	, Data.Enumerator.break-	  -- * Utility functions-	, run-	, printChunks 	) where import Data.List (genericDrop, genericLength, genericSplitAt) import Data.Monoid (Monoid, mempty, mappend, mconcat)@@ -215,6 +214,8 @@ 	case step of 		Error err -> runIteratee (h err) 		_ -> return step+infixl 1 >>==+ -- | Equivalent to (>>=), but allows 'Iteratee's with different input types -- to be composed. (>>==) :: Monad m =>@@ -223,6 +224,7 @@ 	Iteratee e a' m b' i >>== f = Iteratee $ runIteratee i >>= runIteratee . f {-# INLINE (>>==) #-}+infixr 1 ==<<  -- | @(==\<\<) = flip (\>\>==)@ (==<<):: Monad m =>@@ -231,6 +233,7 @@ 	Iteratee e a' m b' (==<<) = flip (>>==) {-# INLINE (==<<) #-}+infixr 0 $$  -- | @($$) = (==\<\<)@ --@@ -242,6 +245,8 @@ 	Iteratee e a' m b' ($$) = (==<<) {-# INLINE ($$) #-}+infixr 1 >==>+ -- | @(>==>) e1 e2 s = e1 s >>== e2@ (>==>) :: Monad m => 	Enumerator e a m b ->@@ -250,6 +255,7 @@ 	Iteratee e a' m b' (>==>) e1 e2 s = e1 s >>== e2 {-# INLINE (>==>) #-}+infixr 1 <==<  -- | @(\<==\<) = flip (>==>)@ (<==<) :: Monad m =>@@ -293,6 +299,22 @@ 		Chunks [] -> continue $ step acc 		Chunks xs -> Iteratee $ liftM (Continue . step) (foldM f acc xs) 		EOF -> yield acc EOF+-- | 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 e a m b -> m (Either e 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"+-- | Print chunks as they're received from the enumerator, optionally+-- printing empty chunks.+printChunks :: (MIO.MonadIO m, Show a) => Bool -> Iteratee e 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.@@ -394,19 +416,3 @@ -- | @break p = 'span' (not . p)@ break :: Monad m => (a -> Bool) -> Iteratee e a m [a] break p = span $ not . p--- | 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 e a m b -> m (Either e 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"--- | Print chunks as they're received from the enumerator, optionally--- printing empty chunks.-printChunks :: (MIO.MonadIO m, Show a) => Bool -> Iteratee e 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
hs/Data/Enumerator/IO.hs view
@@ -17,7 +17,7 @@ 	, iterHandle 	) where import Data.Enumerator-import Control.Monad.IO.Class (liftIO)+import Data.Enumerator.Util import qualified Control.Exception as E import qualified Data.ByteString as B import qualified System.IO as IO@@ -25,49 +25,51 @@ -- 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. enumHandle :: Integer -- ^ Buffer size            -> IO.Handle            -> Enumerator E.SomeException B.ByteString IO b enumHandle bufferSize h = Iteratee . loop where-	size' = fromInteger bufferSize-	loop (Continue k) = read' k+	loop (Continue k) = withBytes $ \bytes -> if B.null bytes+		then return $ Continue k+		else runIteratee (k (Chunks [bytes])) >>= loop+	 	loop step = return step-	read' k = do-		eitherB <- E.try $ B.hGet h size'-		case eitherB of-			Left err -> return $ Error err-			Right bytes | B.null bytes -> return $ Continue k-			Right bytes -> do-				step <- runIteratee (k (Chunks [bytes]))-				loop step+	+	intSize = fromInteger bufferSize+	withBytes = tryStep $ do+		hasInput <- E.catch+			(IO.hWaitForInput h (-1))+			(\(E.SomeException _) -> return False) +		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 E.SomeException B.ByteString IO b-enumFile path s = Iteratee $ do-	eitherH <- E.try $ IO.openBinaryFile path IO.ReadMode-	case eitherH of-		Left err -> return $ Error err-		Right h -> E.finally-			(runIteratee (enumHandle 4096 h s))-			(IO.hClose h)+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. iterHandle :: IO.Handle -> Iteratee E.SomeException B.ByteString IO () iterHandle h = continue step where 	step EOF = yield () EOF-	step (Chunks bytes) = do-		eitherErr <- liftIO . E.try $ mapM_ (B.hPut h) bytes-		case eitherErr of-			Left err -> throwError err-			_ -> continue step+	step (Chunks []) = continue step+	step (Chunks bytes) = Iteratee io where+		put = mapM_ (B.hPut h) bytes+		io = tryStep put (\_ -> return $ Continue step) -- | Opens a file path in binary mode, and passes the handle to 'iterHandle'. -- The file will be closed when the 'Iteratee' finishes. iterFile :: FilePath -> Iteratee E.SomeException B.ByteString IO ()-iterFile path = Iteratee $ do-	eitherH <- E.try $ IO.openBinaryFile path IO.WriteMode-	case eitherH of-		Left err -> return $ Error err-		Right h -> E.finally-			(runIteratee (iterHandle h))-			(IO.hClose h)+iterFile path = Iteratee io where+	withHandle = tryStep (IO.openBinaryFile path IO.WriteMode)+	io = withHandle $ \h -> E.finally+		(runIteratee (iterHandle h))+		(IO.hClose h)
+ hs/Data/Enumerator/Text.hs view
@@ -0,0 +1,235 @@+-----------------------------------------------------------------------------+-- |+-- Module: Data.Enumerator.Text+-- Copyright: 2010 John Millikin+-- License: MIT+--+-- Maintainer: jmillikin@gmail.com+-- Portability: portable+--+-- Enumerator-based text IO+--+-----------------------------------------------------------------------------+module Data.Enumerator.Text (+	  -- * Enumerators and iteratees+	  enumHandle+	, enumFile+	, iterHandle+	, iterFile+	  -- * Codecs+	, Codec+	, encode+	, decode+	, utf8+	, utf16_le+	, utf16_be+	, utf32_le+	, utf32_be+	, ascii+	, iso8859_1+	) where+import qualified Control.Exception as E+import qualified Data.Text as T+import qualified Data.Text.IO as T+import qualified System.IO as IO+import Control.Arrow (first)+import Data.Bits ((.&.))+import qualified Data.ByteString as B+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 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.+enumHandle :: IO.Handle -> Enumerator E.SomeException T.Text IO b+enumHandle h = Iteratee . loop where+	loop (Continue k) = withText $ \text -> if T.null text+		then return $ Continue k+		else runIteratee (k (Chunks [text])) >>= loop+	+	loop step = return step+	withText = tryStep (T.hGetLine h)+-- | Opens a file path in text mode, and passes the handle to 'enumHandle'.+-- The file will be closed when the 'Iteratee' finishes.+enumFile :: FilePath -> Enumerator E.SomeException T.Text IO b+enumFile path s = Iteratee io where+	withHandle = tryStep (IO.openFile path IO.ReadMode)+	io = withHandle $ \h -> E.finally+		(runIteratee (enumHandle h s))+		(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.+iterHandle :: IO.Handle -> Iteratee E.SomeException T.Text IO ()+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)+-- | Opens a file path in text mode, and passes the handle to 'iterHandle'.+-- The file will be closed when the 'Iteratee' finishes.+iterFile :: FilePath -> Iteratee E.SomeException T.Text IO ()+iterFile path = Iteratee io where+	withHandle = tryStep (IO.openFile path IO.WriteMode)+	io = withHandle $ \h -> E.finally+		(runIteratee (iterHandle h))+		(IO.hClose h)+data Codec = Codec+	{ codecName :: T.Text+	, codecEncode :: [T.Text] -> Either E.SomeException [B.ByteString]+	, codecDecode :: B.ByteString -> Either E.SomeException (T.Text, B.ByteString)+	}++instance Show Codec where+	showsPrec d c = showParen (d > 10) $+		showString "Codec " . shows (codecName c)+encode :: Monad m => Codec -> Enumeratee E.SomeException T.Text B.ByteString m b+encode codec = loop where+	loop = checkDone $ continue . step+	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 E.SomeException B.ByteString T.Text m b+decode codec = loop B.empty where+	dec = codecDecode codec+	+	loop acc = checkDone $ 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+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+		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           = 1+		maxN = B.length bytes+		+		loop n | n == maxN = (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 > maxN then 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+		maxN = B.length bytes+		+		loop n |  n      == maxN = (TE.decodeUtf16LE bytes, B.empty)+		       | (n + 1) == maxN = decodeTo n+		loop n = let+			req = utf16Required (B.index bytes 0) (B.index bytes 1)+			decodeMore = loop $! n + req+			in if req > maxN then decodeTo n else decodeMore+		+		decodeTo n = first TE.decodeUtf16LE $ B.splitAt n bytes+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+		maxN = B.length bytes+		+		loop n |  n      == maxN = (TE.decodeUtf16BE bytes, B.empty)+		       | (n + 1) == maxN = decodeTo n+		loop n = let+			req = utf16Required (B.index bytes 1) (B.index bytes 0)+			decodeMore = loop $! n + req+			in if req > maxN then decodeTo n else decodeMore+		+		decodeTo n = first TE.decodeUtf16BE $ B.splitAt n bytes+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+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+	++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+	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+	name = T.pack "ASCII"+	enc t = case T.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+iso8859_1 :: Codec+iso8859_1 = Codec name (mapEither enc) dec where+	name = T.pack "ISO-8859-1"+	enc t = case T.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.SomeException . 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.SomeException . 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)
+ hs/Data/Enumerator/Util.hs view
@@ -0,0 +1,21 @@+module Data.Enumerator.Util where+import Data.Enumerator+import qualified Control.Exception as E++tryStep :: IO t+       -> (t -> IO (Step E.SomeException a IO b))+       -> IO (Step E.SomeException a IO b)+tryStep get io = do+	tried <- E.try get+	case tried of+		Right t -> io t+		Left err -> return $ Error err+{-# INLINE tryStep #-}++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 #-}
+ readme.txt view
@@ -0,0 +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++To generate the woven PDF, install NoWeb and then run:++    anansi -w -l latex-noweb -o enumerator.tex enumerator.anansi+    xelatex enumerator.tex+    xelatex enumerator.tex