expat-enumerator (empty) → 0.1
raw patch · 4 files changed
+194/−0 lines, 4 filesdep +basedep +bytestringdep +enumeratorsetup-changed
Dependencies added: base, bytestring, enumerator, hexpat, text, transformers, xml-types
Files
- Setup.hs +2/−0
- Text/XML/Expat/Enumerator.hs +141/−0
- expat-enumerator.cabal +29/−0
- license.txt +22/−0
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ Text/XML/Expat/Enumerator.hs view
@@ -0,0 +1,141 @@+{-# LANGUAGE DeriveDataTypeable #-}++-----------------------------------------------------------------------------+-- |+-- Module: Text.XML.Expat.Enumerator+-- Copyright: 2010 John Millikin+-- License: MIT+--+-- Maintainer: jmillikin@gmail.com+-- Portability: portable+--+-----------------------------------------------------------------------------+module Text.XML.Expat.Enumerator+ ( Expat.Encoding (..)+ , ParseError (..)+ , parseBytesIO+ , parseTextIO+ ) where+import qualified Data.ByteString as B+import qualified Data.Enumerator as E+import qualified Data.Enumerator.Text as ET+import Data.Enumerator ((>>==), ($$))+import qualified Data.Text as T+import qualified Data.Text.Encoding as TE+import qualified Data.Text.Lazy as TL+import qualified Data.XML.Types as X+import qualified Text.XML.Expat.Internal.IO as Expat++import qualified Control.Exception as Exc+import Control.Monad (forM)+import Control.Monad.IO.Class (MonadIO, liftIO)+import Data.Typeable (Typeable)+import qualified Data.IORef as IO+import Foreign.C (CString, CStringLen)++-- | A replacement for 'Expat.XMLParseError', defined so it can be given+-- a non-orphan 'Exc.Exception' instance.+data ParseError = ParseError+ { parseErrorMessage :: T.Text+ , parseErrorLocation :: Expat.XMLParseLocation+ }+ deriving (Show, Typeable)++instance Exc.Exception ParseError++parseTextIO :: MonadIO m => E.Enumeratee T.Text X.Event m b+parseTextIO s = E.joinI (ET.encode ET.utf8 $$ parseBytesIO (Just Expat.UTF8) s)++parseBytesIO :: MonadIO m+ => Maybe Expat.Encoding -- ^ If present, will override Expat's+ -- encoding detection.+ -> E.Enumeratee B.ByteString X.Event m b+parseBytesIO enc s = do+ p <- liftIO $ Expat.newParser enc+ ref <- liftIO $ setCallbacks p+ eneeParser p ref s++setCallbacks :: Expat.Parser -> IO (IO.IORef [X.Event])+setCallbacks p = do+ eventRef <- IO.newIORef []+ let addEvent e = IO.modifyIORef eventRef (e:) >> return True+ let toName local = X.Name local Nothing Nothing+ + Expat.setStartElementHandler p $ \_ cName cAttrs -> do+ local <- peekUTF8 cName+ attrs <- forM cAttrs $ \(cAttrName, cAttrText) -> do+ attrLocal <- peekUTF8 cAttrName+ attrText <- peekUTF8 cAttrText+ let content = X.ContentText attrText+ return $ X.Attribute (toName attrLocal) [content]+ addEvent (X.EventBeginElement (toName local) attrs)+ + Expat.setEndElementHandler p $ \_ cName -> do+ local <- peekUTF8 cName+ addEvent (X.EventEndElement (toName local))+ + Expat.setCharacterDataHandler p $ \_ cstr -> do+ text <- peekUTF8Len cstr+ addEvent (X.EventContent (X.ContentText text))+ + Expat.setProcessingInstructionHandler p $ \_ ctgt cdst -> do+ tgt <- peekUTF8 ctgt+ dst <- peekUTF8 cdst+ addEvent (X.EventInstruction (X.Instruction tgt dst))+ + Expat.setCommentHandler p $ \_ cstr -> do+ text <- peekUTF8 cstr+ addEvent (X.EventComment text)+ + return eventRef++eneeParser :: MonadIO m+ => Expat.Parser+ -> IO.IORef [X.Event]+ -> E.Enumeratee B.ByteString X.Event m b+eneeParser p eventRef = E.checkDone (E.continue . step) where+ step k E.EOF = checkEvents k+ (\ptr -> Expat.parseChunk ptr B.empty True)+ (\k' -> E.yield (E.Continue k') E.EOF)+ + step k (E.Chunks []) = E.continue (step k)+ step k (E.Chunks xs) = checkEvents k+ (\ptr -> parseChunks ptr xs)+ (\k' -> E.continue (step k'))+ + parseChunks _ [] = error "Text.XML.Expat.Enumerator: parseChunks []"+ parseChunks ptr (x:xs) = do+ maybeErr <- Expat.parseChunk ptr x False+ case maybeErr of+ Just err -> return (Just err)+ Nothing -> if null xs+ then return Nothing+ else parseChunks ptr xs+ + checkEvents k runParse next = do+ (events, maybeErr) <- liftIO (getEvents runParse)+ let checkError k' = case maybeErr of+ Nothing -> next k'+ Just err -> throwError err+ if null events+ then checkError k+ else k (E.Chunks events) >>== E.checkDone checkError+ + getEvents runParse = liftIO $ do+ IO.writeIORef eventRef []+ err <- Expat.withParser p runParse+ events <- IO.readIORef eventRef+ return (reverse events, err)+ + throwError err = E.throwError (ParseError (T.pack msg) loc) where+ Expat.XMLParseError msg loc = err++peekUTF8 :: CString -> IO TL.Text+peekUTF8 cstr = do+ bytes <- B.packCString cstr+ return (TL.fromChunks [TE.decodeUtf8 bytes])++peekUTF8Len :: CStringLen -> IO TL.Text+peekUTF8Len cstr = do+ bytes <- B.packCStringLen cstr+ return (TL.fromChunks [TE.decodeUtf8 bytes])
+ expat-enumerator.cabal view
@@ -0,0 +1,29 @@+name: expat-enumerator+version: 0.1+synopsis: Enumerator-based API for Expat+license: MIT+license-file: license.txt+author: John Millikin <jmillikin@gmail.com>+maintainer: jmillikin@gmail.com+copyright: Copyright (c) John Millikin 2010+build-type: Simple+cabal-version: >=1.6+category: Enumerator, Text, XML, Parsing+stability: experimental+bug-reports: mailto:jmillikin@gmail.com+tested-with: GHC==6.12.1++library+ ghc-options: -Wall -fno-warn-unused-do-bind++ build-depends:+ base >= 4 && < 5+ , bytestring >= 0.9 && < 0.10+ , text >= 0.7 && < 0.8+ , enumerator >= 0.4 && < 0.5+ , transformers >= 0.2 && < 0.3+ , hexpat >= 0.19 && < 0.20+ , xml-types >= 0.1.1 && < 0.2++ exposed-modules:+ Text.XML.Expat.Enumerator
+ license.txt view
@@ -0,0 +1,22 @@+Copyright (c) 2010 John Millikin++Permission is hereby granted, free of charge, to any person+obtaining a copy of this software and associated documentation+files (the "Software"), to deal in the Software without+restriction, including without limitation the rights to use,+copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the+Software is furnished to do so, subject to the following+conditions:++The above copyright notice and this permission notice shall be+included in all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR+OTHER DEALINGS IN THE SOFTWARE.