hexpat-iteratee 0.1 → 0.2
raw patch · 9 files changed
+312/−175 lines, 9 filesdep ~ListPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: List
API changes (from Hackage documentation)
- Text.XML.Expat.Chunked: End :: QueueValue a
- Text.XML.Expat.Chunked: Pending :: QueueValue a
- Text.XML.Expat.Chunked: Queue :: (Seq (Maybe a)) -> Queue a
- Text.XML.Expat.Chunked: Result :: a -> Result m a
- Text.XML.Expat.Chunked: Stack :: [a] -> Stack a
- Text.XML.Expat.Chunked: Value :: a -> QueueValue a
- Text.XML.Expat.Chunked: Yield :: (HandlerT m a) -> Result m a
- Text.XML.Expat.Chunked: data QueueValue a
- Text.XML.Expat.Chunked: data Result m a
- Text.XML.Expat.Chunked: emptyQueue :: Queue a
- Text.XML.Expat.Chunked: emptyStack :: Stack a
- Text.XML.Expat.Chunked: newtype Queue a
- Text.XML.Expat.Chunked: newtype Stack a
- Text.XML.Expat.Chunked: queueEnd :: Queue a -> Queue a
- Text.XML.Expat.Chunked: queueIndex :: Queue a -> Int -> QueueValue a
- Text.XML.Expat.Chunked: queuePush :: a -> Queue a -> Queue a
- Text.XML.Expat.Chunked: stackPop :: Stack a -> Stack a
- Text.XML.Expat.Chunked: stackPush :: a -> Stack a -> Stack a
- Text.XML.Expat.Chunked: stackTop :: Stack a -> a
- Text.XML.Expat.Chunked: yield :: (Monad m) => HandlerT m ()
- Text.XML.Expat.Chunked.Iterator: Iterator :: m (ListItem (Iterator m) a) -> Iterator m a
- Text.XML.Expat.Chunked.Iterator: instance (Eq (m (ListItem (Iterator m) a))) => Eq (Iterator m a)
- Text.XML.Expat.Chunked.Iterator: instance (Monad m) => Applicative (Iterator m)
- Text.XML.Expat.Chunked.Iterator: instance (Monad m) => Functor (Iterator m)
- Text.XML.Expat.Chunked.Iterator: instance (Monad m) => List (Iterator m)
- Text.XML.Expat.Chunked.Iterator: instance (Monad m) => Monad (Iterator m)
- Text.XML.Expat.Chunked.Iterator: instance (Monad m) => MonadPlus (Iterator m)
- Text.XML.Expat.Chunked.Iterator: instance (Monad m) => Monoid (Iterator m a)
- Text.XML.Expat.Chunked.Iterator: instance (MonadError e m) => MonadError e (Iterator m)
- Text.XML.Expat.Chunked.Iterator: instance (MonadIO m) => MonadIO (Iterator m)
- Text.XML.Expat.Chunked.Iterator: instance (MonadReader s m) => MonadReader s (Iterator m)
- Text.XML.Expat.Chunked.Iterator: instance (MonadState s m) => MonadState s (Iterator m)
- Text.XML.Expat.Chunked.Iterator: instance (Ord (m (ListItem (Iterator m) a))) => Ord (Iterator m a)
- Text.XML.Expat.Chunked.Iterator: instance (Read (m (ListItem (Iterator m) a))) => Read (Iterator m a)
- Text.XML.Expat.Chunked.Iterator: instance (Show (m (ListItem (Iterator m) a))) => Show (Iterator m a)
- Text.XML.Expat.Chunked.Iterator: instance MonadTrans Iterator
- Text.XML.Expat.Chunked.Iterator: newtype Iterator m a
- Text.XML.Expat.Chunked.Iterator: runIterator :: Iterator m a -> m (ListItem (Iterator m) a)
+ Text.XML.Expat.Chunked: ASCII :: Encoding
+ Text.XML.Expat.Chunked: Element :: !tag -> ![(tag, text)] -> c (NodeG c tag text) -> NodeG tag text
+ Text.XML.Expat.Chunked: ISO88591 :: Encoding
+ Text.XML.Expat.Chunked: ParserOptions :: Maybe Encoding -> Maybe (tag -> Maybe text) -> ParserOptions tag text
+ Text.XML.Expat.Chunked: Text :: !text -> NodeG tag text
+ Text.XML.Expat.Chunked: UTF16 :: Encoding
+ Text.XML.Expat.Chunked: UTF8 :: Encoding
+ Text.XML.Expat.Chunked: XMLParseError :: String -> XMLParseLocation -> XMLParseError
+ Text.XML.Expat.Chunked: XMLParseLocation :: Int64 -> Int64 -> Int64 -> Int64 -> XMLParseLocation
+ Text.XML.Expat.Chunked: data Encoding :: *
+ Text.XML.Expat.Chunked: data NodeG c :: (* -> *) tag text :: (* -> *) -> * -> * -> *
+ Text.XML.Expat.Chunked: data ParserOptions tag text :: * -> * -> *
+ Text.XML.Expat.Chunked: data XMLParseError :: *
+ Text.XML.Expat.Chunked: data XMLParseLocation :: *
+ Text.XML.Expat.Chunked: defaultParserOptions :: ParserOptions tag text
+ Text.XML.Expat.Chunked: eAttributes :: NodeG tag text -> ![(tag, text)]
+ Text.XML.Expat.Chunked: eChildren :: NodeG tag text -> c (NodeG c tag text)
+ Text.XML.Expat.Chunked: eName :: NodeG tag text -> !tag
+ Text.XML.Expat.Chunked: entityDecoder :: ParserOptions tag text -> Maybe (tag -> Maybe text)
+ Text.XML.Expat.Chunked: parserEncoding :: ParserOptions tag text -> Maybe Encoding
+ Text.XML.Expat.Chunked: xmlByteCount :: XMLParseLocation -> Int64
+ Text.XML.Expat.Chunked: xmlByteIndex :: XMLParseLocation -> Int64
+ Text.XML.Expat.Chunked: xmlColumnNumber :: XMLParseLocation -> Int64
+ Text.XML.Expat.Chunked: xmlLineNumber :: XMLParseLocation -> Int64
- Text.XML.Expat.Chunked: parse :: (MonadIO m, GenericXMLString tag, GenericXMLString text) => ParserOptions tag text -> (Maybe (CNode m tag text) -> HandlerT m a) -> IterateeG WrappedByteString Word8 m (Either ErrMsg a)
+ Text.XML.Expat.Chunked: parse :: (MonadIO m, GenericXMLString tag, GenericXMLString text) => ParserOptions tag text -> (CNode m tag text -> HandlerT m a) -> IterateeG WrappedByteString Word8 m (Either ErrMsg a)
- Text.XML.Expat.Chunked: type CNode m tag text = NodeG (Iterator (HandlerT m)) tag text
+ Text.XML.Expat.Chunked: type CNode m tag text = NodeG (ListT (HandlerT m)) tag text
Files
- Text/XML/Expat/Chunked.hs +88/−68
- Text/XML/Expat/Chunked/Iterator.hs +0/−101
- hexpat-iteratee.cabal +25/−6
- test/broken.xml +0/−0
- test/sudoku.xml +27/−0
- test/test1.hs +30/−0
- test/test2.hs +25/−0
- test/test3.hs +87/−0
- test/test4.hs +30/−0
Text/XML/Expat/Chunked.hs view
@@ -1,8 +1,28 @@ {-# LANGUAGE ScopedTypeVariables #-}-module Text.XML.Expat.Chunked where+module Text.XML.Expat.Chunked (+ -- * Tree structure+ CNode,+ NodeG(..), -import Text.XML.Expat.Chunked.Iterator+ -- * Generic node manipulation+ module Text.XML.Expat.NodeClass,++ -- * Generic manipulation of the child list+ module Data.List.Class,++ -- * Parse to tree+ ParserOptions(..),+ defaultParserOptions,+ Encoding(..),+ Text.XML.Expat.Chunked.parse,+ HandlerT(..),+ XMLParseError(..),+ XMLParseLocation(..)+ ) where++import Control.Monad.ListT import qualified Text.XML.Expat.IO as IO+import Text.XML.Expat.NodeClass import Text.XML.Expat.SAX import Text.XML.Expat.Tree @@ -10,61 +30,54 @@ import Control.Monad.Trans import qualified Data.ByteString as B import Data.IORef-import Data.Iteratee hiding (head)-import qualified Data.Iteratee as It+import Data.Iteratee hiding (head, peek) import Data.Iteratee.WrappedByteString import Data.List.Class-import Data.Sequence (Seq, (|>), index)-import qualified Data.Sequence as Seq import Data.Word ------- Queue ------------------------------------------------------------------+------ Types ------------------------------------------------------------------ -newtype Queue a = Queue (Seq (Maybe a))+-- | A tree representation that uses a monadic list as its child list type.+type CNode m tag text = NodeG (ListT (HandlerT m)) tag text -emptyQueue :: Queue a-emptyQueue = Queue (Seq.empty) -queuePush :: a -> Queue a -> Queue a-queuePush value (Queue s) = Queue (s |> Just value)--queueEnd :: Queue a -> Queue a-queueEnd (Queue s) = Queue (s |> Nothing)+------ Queue ------------------------------------------------------------------ -data QueueValue a- = Value a+-- Mutable queue implemented as a linked list+data QueueCell a+ = Value a (QueueHead a) | End | Pending -queueIndex :: Queue a -> Int -> QueueValue a-queueIndex q@(Queue s) i- | i >= Seq.length s = Pending- | otherwise = case s `index` i of- Just value -> Value value- Nothing -> End--------- Stack --------------------------------------------------------------------newtype Stack a = Stack [a]+newtype QueueHead a = QueueHead (IORef (QueueCell a))+newtype QueueTail a = QueueTail (IORef (IORef (QueueCell a))) -emptyStack :: Stack a-emptyStack = Stack []+newQueue :: IO (QueueHead a, QueueTail a)+newQueue = do+ end <- newIORef Pending+ endRef <- newIORef end+ return (QueueHead end, QueueTail endRef) -stackTop :: Stack a -> a-stackTop (Stack s) = head s+peek :: QueueHead a -> IO (QueueCell a)+peek (QueueHead hRef) = readIORef hRef -stackPop :: Stack a -> Stack a-stackPop (Stack s) = Stack (tail s)+push :: QueueTail a -> a -> IO ()+push (QueueTail qtRef) val = do+ tl <- readIORef qtRef+ newTl <- newIORef Pending+ writeIORef tl (Value val (QueueHead newTl))+ writeIORef qtRef newTl -stackPush :: a -> Stack a -> Stack a-stackPush it (Stack s) = Stack (it:s)+pushEnd :: QueueTail a -> IO ()+pushEnd (QueueTail qtRef) = do+ tl <- readIORef qtRef+ writeIORef tl End ------ Handler ---------------------------------------------------------------- -data Result m a = Yield (HandlerT m a) | Result a+data Result m a = Yield (HandlerT m a) | Result a | HandlerErr String data HandlerT m a = HandlerT { runHandlerT :: m (Result m a)@@ -78,9 +91,10 @@ f >>= g = HandlerT $ do res1 <- runHandlerT f case res1 of- Yield c -> return $ Yield (c >>= g)- Result a -> runHandlerT (g a)- fail err = HandlerT $ fail err+ Yield c -> return $ Yield (c >>= g)+ Result a -> runHandlerT (g a)+ HandlerErr err -> return $ HandlerErr err + fail err = HandlerT $ return $ HandlerErr err instance MonadTrans HandlerT where lift m = HandlerT $ do@@ -97,49 +111,52 @@ ------------------------------------------------------------------------------- -type CNode m tag text = NodeG (Iterator (HandlerT m)) tag text-+-- | An iteratee that parses the input document, passing a representation of it+-- to the specified handler monad. The monad runs lazily using co-routines, so+-- if it requests a part of the tree that hasn't been parsed yet, it will+-- be suspended, and continued when it's available.+--+-- This implementation does /not/ use Haskell's lazy I/O. parse :: forall m a tag text . ( MonadIO m, GenericXMLString tag, GenericXMLString text ) => ParserOptions tag text- -> (Maybe (CNode m tag text) -> HandlerT m a)+ -> (CNode m tag text -> HandlerT m a) -> IterateeG WrappedByteString Word8 m (Either ErrMsg a) parse opts code = IterateeG $ \str -> do let enc = parserEncoding opts parser <- liftIO $ IO.newParser enc - rootRef <- liftIO $ newIORef emptyQueue- stackRef <- liftIO $ newIORef $ rootRef `stackPush` emptyStack+ (rootHd, rootTl) <- liftIO $ newQueue+ stackRef <- liftIO $ newIORef [rootTl] liftIO $ IO.setStartElementHandler parser $ \cName cAttrs -> do- name <- mkText cName+ name <- textFromCString cName attrs <- forM cAttrs $ \(cAttrName,cAttrValue) -> do- attrName <- mkText cAttrName- attrValue <- mkText cAttrValue+ attrName <- textFromCString cAttrName+ attrValue <- textFromCString cAttrValue return (attrName, attrValue) --loc <- getParseLocation parser stack <- readIORef stackRef- newRef <- newIORef emptyQueue- let elt = Element name attrs (Iterator $ iter newRef 0)- modifyIORef (stackTop stack) (elt `queuePush`)- modifyIORef stackRef (newRef `stackPush`)+ (hd, tl) <- newQueue+ push (head stack) $ Element name attrs (ListT $ iter hd)+ writeIORef stackRef (tl:stack) return True liftIO $ IO.setEndElementHandler parser $ \_ -> do- --name <- mkText cName+ --name <- textFromCString cName --loc <- getParseLocation parser stack <- readIORef stackRef- modifyIORef (stackTop stack) queueEnd- writeIORef stackRef (stackPop stack)+ pushEnd (head stack)+ writeIORef stackRef (tail stack) return True liftIO $ IO.setCharacterDataHandler parser $ \cText -> do txt <- gxFromCStringLen cText --loc <- getParseLocation parser stack <- readIORef stackRef- modifyIORef (stackTop stack) (Text txt `queuePush`)+ push (head stack) $ Text txt return True let nextIter :: HandlerT m a@@ -165,21 +182,24 @@ case mErr of Just err -> Done (Left $ Err $ show err) str Nothing -> Done (Right a) str+ HandlerErr handlerErr -> do+ case mErr of+ Just parseErr -> Done (Left $ Err $ show parseErr) str+ Nothing -> Done (Left $ Err $ show handlerErr) str let process = do- elt <- iter rootRef 0+ elt <- iter rootHd case elt of- Nil -> code Nothing- Cons node _ -> code (Just node)+ Nil -> fail "no root node"+ Cons node _ -> code node runIter (nextIter process) str where- iter :: IORef (Queue (CNode m tag text)) -> Int -> HandlerT m (ListItem (Iterator (HandlerT m)) (CNode m tag text))- iter ref i = do- queue <- liftIO (readIORef ref)- case queue `queueIndex` i of -- to do: make it throw away heads we don't have references to- Pending -> yield >> iter ref i- End -> return $ Nil- Value a -> return $- let i' = i + 1- in Cons a (Iterator $ iter ref i')+ iter :: QueueHead (CNode m tag text) -> HandlerT m (ListItem (ListT (HandlerT m)) (CNode m tag text))+ iter hd = do+ cell <- liftIO $ peek hd+ case cell of+ Pending -> yield >> iter hd+ End -> return $ Nil+ Value a hd' -> return $ Cons a (ListT $ iter hd')+
− Text/XML/Expat/Chunked/Iterator.hs
@@ -1,101 +0,0 @@-{-# LANGUAGE TypeFamilies, FlexibleContexts, UndecidableInstances,- StandaloneDeriving, FlexibleInstances, MultiParamTypeClasses #-}-module Text.XML.Expat.Chunked.Iterator (Iterator (..)) where--import Data.List.Class (List(..), ListItem(..), foldrL)--import Control.Applicative (Applicative(..))-import Control.Monad (MonadPlus(..), ap, liftM)--- import Control.Monad.Cont.Class (MonadCont(..))-import Control.Monad.Error.Class (MonadError(..))-import Control.Monad.Reader.Class (MonadReader(..))-import Control.Monad.State.Class (MonadState(..))-import Control.Monad.Trans (MonadTrans(..), MonadIO(..))-import Data.Monoid (Monoid(..))---newtype Iterator m a = Iterator { runIterator :: m (ListItem (Iterator m) a) }--deriving instance (Eq (m (ListItem (Iterator m) a))) => Eq (Iterator m a)-deriving instance (Ord (m (ListItem (Iterator m) a))) => Ord (Iterator m a)-deriving instance (Read (m (ListItem (Iterator m) a))) => Read (Iterator m a)-deriving instance (Show (m (ListItem (Iterator m) a))) => Show (Iterator m a)--{--instance (Monad m, MonadPlus (Iterator m)) => List (Iterator m) where- type ItemM (Iterator m) = m- runList = runIterator- joinL m = Iterator $ do- it <- m- runIterator it--}---- for mappend, fmap, bind-foldrL' :: List l => (a -> l b -> l b) -> l b -> l a -> l b-foldrL' consFunc nilFunc =- joinL . foldrL step (return nilFunc)- where- step x = return . consFunc x . joinL---- like generic cons except using that one--- would cause an infinite loop-cons :: Monad m => a -> Iterator m a -> Iterator m a-cons x = Iterator . return . Cons x--instance Monad m => Monoid (Iterator m a) where- mempty = Iterator $ return Nil- mappend = flip (foldrL' cons)--instance Monad m => Functor (Iterator m) where- fmap func = foldrL' (cons . func) mempty--instance Monad m => Monad (Iterator m) where- return = Iterator . return . (`Cons` mempty)- a >>= b = foldrL' mappend mempty (fmap b a)--instance Monad m => Applicative (Iterator m) where- pure = return- (<*>) = ap--instance Monad m => MonadPlus (Iterator m) where- mzero = mempty- mplus = mappend--instance MonadTrans Iterator where- lift = Iterator . liftM (`Cons` mempty)--instance Monad m => List (Iterator m) where- type ItemM (Iterator m) = m- runList = runIterator- joinL = Iterator . (>>= runList)---- YUCK:--- I can't believe I'm doing this,--- for compatability with mtl's Iterator.--- I hate the O(N^2) code length auto-lifts. DRY!!--instance MonadIO m => MonadIO (Iterator m) where- liftIO = lift . liftIO--{---- TODO: understand and verify this instance :)-instance MonadCont m => MonadCont (Iterator m) where- callCC f =- Iterator $ callCC thing- where- thing c = runIterator . f $ Iterator . c . (`Cons` mempty)--}--instance MonadError e m => MonadError e (Iterator m) where- throwError = lift . throwError- catchError m = Iterator . catchError (runList m) . (runList .)--instance MonadReader s m => MonadReader s (Iterator m) where- ask = lift ask- local f = Iterator . local f . runList--instance MonadState s m => MonadState s (Iterator m) where- get = lift get- put = lift . put--
hexpat-iteratee.cabal view
@@ -1,9 +1,22 @@ Cabal-Version: >= 1.6 Name: hexpat-iteratee-Version: 0.1-Synopsis: chunked XML parsing using iteratees+Version: 0.2+Synopsis: Chunked XML parsing using iteratees Description:- Provides chunked XML parsing using iteratees+ This package provides chunked XML parsing using iteratees. It is especially suited+ to implementing XML-based socket protocols, but is useful wherever lazy parsing is+ needed on production systems where you can't tolerate the problems that come with+ Haskell's lazy I\/O.+ .+ The XML is presented as a lazy tree, and is processed by a handler implemented using+ a special HandlerT monad. This monad is suspended whenever it tries to read a part+ of the tree that hasn't been parsed yet, and continued as soon as it is available.+ The resulting code looks and functions very much as if you were using lazy I\/O,+ only without the associated problems. The handler monad may have effects.+ .+ Background: Haskell's lazy I\/O can be problematic in some applications because it+ doesn't handle I\/O errors properly, and you can't predict when it will clean up its+ resources, which could result in file handles running out. Category: XML License: BSD3 License-File: LICENSE@@ -14,7 +27,14 @@ (c) 2010 Stephen Blackheath <http://blacksapphire.com/antispam/> Homepage: http://haskell.org/haskellwiki/Hexpat/ Build-Type: Simple-Stability: prototype+Stability: pre-alpha+Extra-Source-Files:+ test/test1.hs+ test/test2.hs+ test/test3.hs+ test/test4.hs+ test/sudoku.xml+ test/broken.xml source-repository head type: darcs location: http://code.haskell.org/hexpat-iteratee/@@ -29,8 +49,7 @@ extensible-exceptions >= 0.1 && < 0.2, iteratee >= 0.3, hexpat >= 0.13,- List >= 0.2+ List >= 0.3 Exposed-Modules: Text.XML.Expat.Chunked- Text.XML.Expat.Chunked.Iterator ghc-options: -Wall -fno-warn-name-shadowing -fno-warn-unused-matches -fno-warn-missing-signatures -fno-warn-orphans
+ test/broken.xml view
+ test/sudoku.xml view
@@ -0,0 +1,27 @@+<?xml version="1.0"?>+<gconf>+ <entry name="bg_color" mtime="1258334129" type="string">+ <stringvalue>black</stringvalue>+ </entry>+ <entry name="show_toolbar" mtime="1241553324" type="int" value="1"/>+ <entry name="generate_puzzles_in_background" mtime="1241553324" type="int" value="1"/>+ <entry name="show_impossible_implications" mtime="1241553324" type="int" value="0"/>+ <entry name="highlight" mtime="1241553450" type="int" value="0"/>+ <entry name="bg_custom_color" mtime="1241553323" type="string">+ <stringvalue></stringvalue>+ </entry>+ <entry name="width" mtime="1258334132" type="int" value="700"/>+ <entry name="difficulty" mtime="1241553323" type="float" value="0"/>+ <entry name="bg_black" mtime="1241553323" type="int" value="1"/>+ <entry name="show_tracker" mtime="1241553323" type="bool" value="false"/>+ <entry name="font_zoom" mtime="1241553323" type="int" value="0"/>+ <entry name="auto_save_interval" mtime="1241553323" type="int" value="60"/>+ <entry name="zoom_on_resize" mtime="1241553323" type="int" value="1"/>+ <entry name="always_show_hints" mtime="1241553323" type="int" value="0"/>+ <entry name="minimum_number_of_new_puzzles" mtime="1241553323" type="int" value="90"/>+ <entry name="player" mtime="1241553323" type="string">+ <stringvalue>blackh</stringvalue>+ </entry>+ <entry name="height" mtime="1258334132" type="int" value="675"/>+ <entry name="group_size" mtime="1241553323" type="int" value="9"/>+</gconf>
+ test/test1.hs view
@@ -0,0 +1,30 @@+import Text.XML.Expat.Chunked+import Text.XML.Expat.Format+import Text.XML.Expat.Tree hiding (parse)++import Control.Monad+import Control.Monad.Trans+import qualified Data.ByteString as B+import Data.Iteratee+import Data.List.Class+import Data.Text (Text)+import qualified Data.Text as T+import System.IO+++main :: IO ()+main = do+ eErr <- fileDriver (parse defaultParserOptions dump) "sudoku.xml"+ case eErr of+ Left err -> putStrLn $ "failed: "++show err+ Right () -> putStrLn ""++dump :: CNode IO Text Text -> HandlerT IO ()+dump doc = do+ let txt = formatG doc+ execute $ forL txt $ liftIO . B.hPutStr stdout+ where+ mapL :: List c => (a -> ItemM c b) -> c a -> c b+ mapL f = joinM . liftM f+ forL = flip mapL+
+ test/test2.hs view
@@ -0,0 +1,25 @@+import Text.XML.Expat.Chunked+import Text.XML.Expat.Format+import Text.XML.Expat.Tree hiding (parse)++import Control.Monad+import Control.Monad.Trans+import qualified Data.ByteString as B+import Data.Iteratee+import Data.List.Class+import Data.Text (Text)+import qualified Data.Text as T+import System.IO+++main :: IO ()+main = do+ eErr <- fileDriver (parse defaultParserOptions dump) "sudoku.xml"+ case eErr of+ Left err -> putStrLn $ "failed: "++show err+ Right () -> putStrLn ""++dump :: CNode IO Text Text -> HandlerT IO ()+dump doc = do+ fail "die!"+
+ test/test3.hs view
@@ -0,0 +1,87 @@+{-# LANGUAGE ScopedTypeVariables #-}+import Text.XML.Expat.Chunked+import Text.XML.Expat.Format+import Text.XML.Expat.Tree hiding (parse)++import Control.Exception+import Control.Monad+import Control.Monad.Trans+import qualified Data.ByteString as B+import Data.Iteratee+import Data.Iteratee.Base.StreamChunk (ReadableChunk (..))+import Data.List.Class+import Data.Text (Text)+import qualified Data.Text as T+import System.IO+import Foreign.Ptr+import Foreign.ForeignPtr+import Foreign.Storable+++main :: IO ()+main = do+ eErr <- myFileDriver (parse defaultParserOptions dump) "sudoku.xml"+ case eErr of+ Left err -> putStrLn $ "failed: "++show err+ Right () -> putStrLn ""++dump :: CNode IO Text Text -> HandlerT IO ()+dump doc = do+ let txt = formatG doc+ forIter txt $ liftIO . B.hPutStr stdout+ return ()++forIter :: (List l) => l a -> (a -> ItemM l b) -> ItemM l [b]+forIter iter body = fi iter []+ where+ fi iter acc = do+ elt <- runList iter+ case elt of+ Cons value iter' -> do+ b <- body value+ fi iter' (b:acc)+ Nil -> return (reverse acc)++-- ------------------------------------------------------------------------+-- Binary Random IO enumerators++myBufSize = 16++-- |The enumerator of a file Handle. This version enumerates+-- over the entire contents of a file, in order, unless stopped by+-- the iteratee. In particular, seeking is not supported.+myEnumHandle :: forall s el m a.(ReadableChunk s el, MonadIO m) =>+ Handle ->+ EnumeratorGM s el m a+myEnumHandle h i =+ liftIO (mallocForeignPtrBytes (fromIntegral buffer_size)) >>= loop i+ where+ buffer_size = myBufSize - mod myBufSize (sizeOf (undefined :: el))+ loop iter fp = do+ s <- liftIO . withForeignPtr fp $ \p -> do+ n <- try $ hGetBuf h p buffer_size :: IO (Either SomeException Int)+ case n of+ Left _ -> return $ Left "IO error"+ Right 0 -> return $ Right Nothing+ Right n' -> liftM (Right . Just) $ readFromPtr p (fromIntegral n')+ liftIO $ putStr "|" -- ###+ checkres fp iter s+ checkres fp iter = either (flip enumErr iter)+ (maybe (return iter)+ (check fp <=< runIter iter . Chunk))+ check _p (Done x _) = return . return $ x+ check p (Cont i' Nothing) = loop i' p+ check _p (Cont _ (Just e)) = return $ throwErr e++-- |Process a file using the given IterateeGM. This function wraps+-- enumHandle as a convenience.+myFileDriver :: (MonadIO m, ReadableChunk s el) =>+ IterateeG s el m a ->+ FilePath ->+ m a+myFileDriver iter filepath = do+ h <- liftIO $ openBinaryFile filepath ReadMode+ result <- myEnumHandle h iter >>= run+ liftIO $ hClose h+ return result+
+ test/test4.hs view
@@ -0,0 +1,30 @@+import Text.XML.Expat.Chunked+import Text.XML.Expat.Format+import Text.XML.Expat.Tree hiding (parse)++import Control.Monad+import Control.Monad.Trans+import qualified Data.ByteString as B+import Data.Iteratee+import Data.List.Class+import Data.Text (Text)+import qualified Data.Text as T+import System.IO+++main :: IO ()+main = do+ eErr <- fileDriver (parse defaultParserOptions dump) "broken.xml"+ case eErr of+ Left err -> putStrLn $ "failed: "++show err+ Right () -> putStrLn ""++dump :: CNode IO Text Text -> HandlerT IO ()+dump doc = do+ let txt = formatG doc+ execute $ forL txt $ liftIO . B.hPutStr stdout+ where+ mapL :: List c => (a -> ItemM c b) -> c a -> c b+ mapL f = joinM . liftM f+ forL = flip mapL+