packages feed

hexpat 0.18.3 → 0.19

raw patch · 12 files changed

+1263/−93 lines, 12 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Text.XML.Expat.Annotated: CharacterData :: text -> SAXEvent tag text
- Text.XML.Expat.Annotated: EndElement :: tag -> SAXEvent tag text
- Text.XML.Expat.Annotated: FailDocument :: XMLParseError -> SAXEvent tag text
- Text.XML.Expat.Annotated: StartElement :: tag -> [(tag, text)] -> SAXEvent tag text
- Text.XML.Expat.Annotated: data SAXEvent tag text
- Text.XML.Expat.Tree: CharacterData :: text -> SAXEvent tag text
- Text.XML.Expat.Tree: EndElement :: tag -> SAXEvent tag text
- Text.XML.Expat.Tree: FailDocument :: XMLParseError -> SAXEvent tag text
- Text.XML.Expat.Tree: StartElement :: tag -> [(tag, text)] -> SAXEvent tag text
- Text.XML.Expat.Tree: data SAXEvent tag text
+ Text.XML.Expat.Annotated: instance (Functor c, List c) => MkElementClass (NodeG ()) c
+ Text.XML.Expat.Extended: ASCII :: Encoding
+ Text.XML.Expat.Extended: CData :: !text -> NodeG a c tag text
+ Text.XML.Expat.Extended: Document :: Maybe (XMLDeclaration text) -> Maybe (DocumentTypeDeclaration c tag text) -> c (Misc text) -> NodeG a c tag text -> DocumentG a c tag text
+ Text.XML.Expat.Extended: Element :: !tag -> ![(tag, text)] -> c (NodeG a c tag text) -> a -> NodeG a c tag text
+ Text.XML.Expat.Extended: ISO88591 :: Encoding
+ Text.XML.Expat.Extended: Misc :: (Misc text) -> NodeG a c tag text
+ Text.XML.Expat.Extended: ParseOptions :: Maybe Encoding -> Maybe (tag -> Maybe text) -> ParseOptions tag text
+ Text.XML.Expat.Extended: Text :: !text -> NodeG a c tag text
+ Text.XML.Expat.Extended: UTF16 :: Encoding
+ Text.XML.Expat.Extended: UTF8 :: Encoding
+ Text.XML.Expat.Extended: XMLParseError :: String -> XMLParseLocation -> XMLParseError
+ Text.XML.Expat.Extended: XMLParseException :: XMLParseError -> XMLParseException
+ Text.XML.Expat.Extended: XMLParseLocation :: Int64 -> Int64 -> Int64 -> Int64 -> XMLParseLocation
+ Text.XML.Expat.Extended: class (Monoid s, Eq s) => GenericXMLString s
+ Text.XML.Expat.Extended: dDocumentTypeDeclaration :: DocumentG a c tag text -> Maybe (DocumentTypeDeclaration c tag text)
+ Text.XML.Expat.Extended: dRoot :: DocumentG a c tag text -> NodeG a c tag text
+ Text.XML.Expat.Extended: dTopLevelMiscs :: DocumentG a c tag text -> c (Misc text)
+ Text.XML.Expat.Extended: dXMLDeclaration :: DocumentG a c tag text -> Maybe (XMLDeclaration text)
+ Text.XML.Expat.Extended: data DocumentG a c tag text
+ Text.XML.Expat.Extended: data Encoding
+ Text.XML.Expat.Extended: data NodeG a c tag text
+ Text.XML.Expat.Extended: data ParseOptions tag text
+ Text.XML.Expat.Extended: data XMLParseError
+ Text.XML.Expat.Extended: data XMLParseException
+ Text.XML.Expat.Extended: data XMLParseLocation
+ Text.XML.Expat.Extended: defaultParseOptions :: ParseOptions tag text
+ Text.XML.Expat.Extended: eAnn :: NodeG a c tag text -> a
+ Text.XML.Expat.Extended: eAttributes :: NodeG a c tag text -> ![(tag, text)]
+ Text.XML.Expat.Extended: eChildren :: NodeG a c tag text -> c (NodeG a c tag text)
+ Text.XML.Expat.Extended: eName :: NodeG a c tag text -> !tag
+ Text.XML.Expat.Extended: entityDecoder :: ParseOptions tag text -> Maybe (tag -> Maybe text)
+ Text.XML.Expat.Extended: gxBreakOn :: (GenericXMLString s) => Char -> s -> (s, s)
+ Text.XML.Expat.Extended: gxFromCStringLen :: (GenericXMLString s) => CStringLen -> IO s
+ Text.XML.Expat.Extended: gxFromChar :: (GenericXMLString s) => Char -> s
+ Text.XML.Expat.Extended: gxFromString :: (GenericXMLString s) => String -> s
+ Text.XML.Expat.Extended: gxHead :: (GenericXMLString s) => s -> Char
+ Text.XML.Expat.Extended: gxNullString :: (GenericXMLString s) => s -> Bool
+ Text.XML.Expat.Extended: gxTail :: (GenericXMLString s) => s -> s
+ Text.XML.Expat.Extended: gxToByteString :: (GenericXMLString s) => s -> ByteString
+ Text.XML.Expat.Extended: gxToString :: (GenericXMLString s) => s -> String
+ Text.XML.Expat.Extended: instance (Eq tag, Eq text, Eq a) => Eq (DocumentG a [] tag text)
+ Text.XML.Expat.Extended: instance (Eq tag, Eq text, Eq a) => Eq (NodeG a [] tag text)
+ Text.XML.Expat.Extended: instance (Functor c, List c) => DocumentClass (DocumentG ann) c
+ Text.XML.Expat.Extended: instance (Functor c, List c) => MkElementClass (NodeG ()) c
+ Text.XML.Expat.Extended: instance (Functor c, List c) => MkElementClass (NodeG (Maybe a)) c
+ Text.XML.Expat.Extended: instance (Functor c, List c) => NodeClass (NodeG a) c
+ Text.XML.Expat.Extended: instance (NFData tag, NFData text, NFData a) => NFData (NodeG a [] tag text)
+ Text.XML.Expat.Extended: instance (Show tag, Show text, Show a) => Show (DocumentG a [] tag text)
+ Text.XML.Expat.Extended: instance (Show tag, Show text, Show a) => Show (NodeG a [] tag text)
+ Text.XML.Expat.Extended: mapAnnotation :: (a -> b) -> Node a tag text -> Node b tag text
+ Text.XML.Expat.Extended: mapDocumentAnnotation :: (a -> b) -> Document a tag text -> Document b tag text
+ Text.XML.Expat.Extended: modifyAnnotation :: (a -> a) -> Node a tag text -> Node a tag text
+ Text.XML.Expat.Extended: overrideEncoding :: ParseOptions tag text -> Maybe Encoding
+ Text.XML.Expat.Extended: parse :: (GenericXMLString tag, GenericXMLString text) => ParseOptions tag text -> ByteString -> (LDocument tag text, Maybe XMLParseError)
+ Text.XML.Expat.Extended: parse' :: (GenericXMLString tag, GenericXMLString text) => ParseOptions tag text -> ByteString -> Either XMLParseError (LDocument tag text)
+ Text.XML.Expat.Extended: parseThrowing :: (GenericXMLString tag, GenericXMLString text) => ParseOptions tag text -> ByteString -> LDocument tag text
+ Text.XML.Expat.Extended: saxToTree :: (GenericXMLString tag, Monoid text) => [(SAXEvent tag text, a)] -> (Document a tag text, Maybe XMLParseError)
+ Text.XML.Expat.Extended: type Document a tag text = DocumentG a [] tag text
+ Text.XML.Expat.Extended: type LDocument tag text = Document XMLParseLocation tag text
+ Text.XML.Expat.Extended: type LNode tag text = Node XMLParseLocation tag text
+ Text.XML.Expat.Extended: type NDocument a text = Document a (NName text) text
+ Text.XML.Expat.Extended: type NLDocument text = Document XMLParseLocation (NName text) text
+ Text.XML.Expat.Extended: type NLNode text = LNode (NName text) text
+ Text.XML.Expat.Extended: type NNode a text = Node a (NName text) text
+ Text.XML.Expat.Extended: type Node a tag text = NodeG a [] tag text
+ Text.XML.Expat.Extended: type QDocument a text = Document a (QName text) text
+ Text.XML.Expat.Extended: type QLDocument text = Document XMLParseLocation (QName text) text
+ Text.XML.Expat.Extended: type QLNode text = LNode (QName text) text
+ Text.XML.Expat.Extended: type QNode a text = Node a (QName text) text
+ Text.XML.Expat.Extended: type UDocument a text = Document a text text
+ Text.XML.Expat.Extended: type ULDocument text = Document XMLParseLocation text text
+ Text.XML.Expat.Extended: type ULNode text = LNode text text
+ Text.XML.Expat.Extended: type UNode a text = Node a text text
+ Text.XML.Expat.Extended: xmlByteCount :: XMLParseLocation -> Int64
+ Text.XML.Expat.Extended: xmlByteIndex :: XMLParseLocation -> Int64
+ Text.XML.Expat.Extended: xmlColumnNumber :: XMLParseLocation -> Int64
+ Text.XML.Expat.Extended: xmlLineNumber :: XMLParseLocation -> Int64
+ Text.XML.Expat.Format: documentToSAX :: (GenericXMLString tag, GenericXMLString text, Monoid text, DocumentClass d c) => d c tag text -> c (SAXEvent tag text)
+ Text.XML.Expat.Format: formatDocument :: (DocumentClass d [], GenericXMLString tag, GenericXMLString text) => d [] tag text -> ByteString
+ Text.XML.Expat.Format: formatDocument' :: (DocumentClass d [], GenericXMLString tag, GenericXMLString text) => d [] tag text -> ByteString
+ Text.XML.Expat.Format: formatDocumentG :: (DocumentClass d c, GenericXMLString tag, GenericXMLString text) => d c tag text -> c ByteString
+ Text.XML.Expat.Internal.DocumentClass: Comment :: !text -> Misc text
+ Text.XML.Expat.Internal.DocumentClass: DocumentTypeDeclaration :: DocumentTypeDeclaration c tag text
+ Text.XML.Expat.Internal.DocumentClass: ProcessingInstruction :: !text -> !text -> Misc text
+ Text.XML.Expat.Internal.DocumentClass: XMLDeclaration :: text -> (Maybe text) -> (Maybe Bool) -> XMLDeclaration text
+ Text.XML.Expat.Internal.DocumentClass: class (Functor c, List c, NodeClass (NodeType d) c) => DocumentClass d c
+ Text.XML.Expat.Internal.DocumentClass: data DocumentTypeDeclaration c tag text
+ Text.XML.Expat.Internal.DocumentClass: data Misc text
+ Text.XML.Expat.Internal.DocumentClass: data XMLDeclaration text
+ Text.XML.Expat.Internal.DocumentClass: getDocumentTypeDeclaration :: (DocumentClass d c) => d c tag text -> Maybe (DocumentTypeDeclaration c tag text)
+ Text.XML.Expat.Internal.DocumentClass: getRoot :: (DocumentClass d c) => d c tag text -> NodeType d c tag text
+ Text.XML.Expat.Internal.DocumentClass: getTopLevelMiscs :: (DocumentClass d c) => d c tag text -> c (Misc text)
+ Text.XML.Expat.Internal.DocumentClass: getXMLDeclaration :: (DocumentClass d c) => d c tag text -> Maybe (XMLDeclaration text)
+ Text.XML.Expat.Internal.DocumentClass: instance (Eq text) => Eq (Misc text)
+ Text.XML.Expat.Internal.DocumentClass: instance (Eq text) => Eq (XMLDeclaration text)
+ Text.XML.Expat.Internal.DocumentClass: instance (NFData text) => NFData (Misc text)
+ Text.XML.Expat.Internal.DocumentClass: instance (Show text) => Show (Misc text)
+ Text.XML.Expat.Internal.DocumentClass: instance (Show text) => Show (XMLDeclaration text)
+ Text.XML.Expat.Internal.DocumentClass: instance Eq (DocumentTypeDeclaration c tag text)
+ Text.XML.Expat.Internal.DocumentClass: instance Show (DocumentTypeDeclaration c tag text)
+ Text.XML.Expat.Internal.DocumentClass: mkDocument :: (DocumentClass d c) => Maybe (XMLDeclaration text) -> Maybe (DocumentTypeDeclaration c tag text) -> c (Misc text) -> NodeType d c tag text -> d c tag text
+ Text.XML.Expat.Internal.DocumentClass: mkPlainDocument :: (DocumentClass d c) => NodeType d c tag text -> d c tag text
+ Text.XML.Expat.Internal.DocumentClass: modifyDocumentTypeDeclaration :: (DocumentClass d c) => (Maybe (DocumentTypeDeclaration c tag text) -> Maybe (DocumentTypeDeclaration c tag text)) -> d c tag text -> d c tag text
+ Text.XML.Expat.Internal.DocumentClass: modifyRoot :: (DocumentClass d c) => (NodeType d c tag text -> NodeType d c tag text) -> d c tag text -> d c tag text
+ Text.XML.Expat.Internal.DocumentClass: modifyTopLevelMiscs :: (DocumentClass d c) => (c (Misc text) -> c (Misc text)) -> d c tag text -> d c tag text
+ Text.XML.Expat.Internal.DocumentClass: modifyXMLDeclaration :: (DocumentClass d c) => (Maybe (XMLDeclaration text) -> Maybe (XMLDeclaration text)) -> d c tag text -> d c tag text
+ Text.XML.Expat.Internal.IO: setCommentHandler :: Parser -> CommentHandler -> IO ()
+ Text.XML.Expat.Internal.IO: setEndCDataHandler :: Parser -> EndCDataHandler -> IO ()
+ Text.XML.Expat.Internal.IO: setProcessingInstructionHandler :: Parser -> ProcessingInstructionHandler -> IO ()
+ Text.XML.Expat.Internal.IO: setStartCDataHandler :: Parser -> StartCDataHandler -> IO ()
+ Text.XML.Expat.Internal.IO: setXMLDeclarationHandler :: Parser -> XMLDeclarationHandler -> IO ()
+ Text.XML.Expat.Internal.IO: type CommentHandler = ParserPtr -> CString -> IO Bool
+ Text.XML.Expat.Internal.IO: type EndCDataHandler = ParserPtr -> IO Bool
+ Text.XML.Expat.Internal.IO: type ProcessingInstructionHandler = ParserPtr -> CString -> CString -> IO Bool
+ Text.XML.Expat.Internal.IO: type StartCDataHandler = ParserPtr -> IO Bool
+ Text.XML.Expat.Internal.IO: type XMLDeclarationHandler = ParserPtr -> CString -> CString -> CInt -> IO Bool
+ Text.XML.Expat.Internal.NodeClass: getTarget :: (NodeClass n c, Monoid text) => n c tag text -> text
+ Text.XML.Expat.Internal.NodeClass: hasTarget :: (NodeClass n c, Eq text) => text -> n c tag text -> Bool
+ Text.XML.Expat.Internal.NodeClass: isCData :: (NodeClass n c) => n c tag text -> Bool
+ Text.XML.Expat.Internal.NodeClass: isComment :: (NodeClass n c) => n c tag text -> Bool
+ Text.XML.Expat.Internal.NodeClass: isProcessingInstruction :: (NodeClass n c) => n c tag text -> Bool
+ Text.XML.Expat.SAX: Comment :: text -> SAXEvent tag text
+ Text.XML.Expat.SAX: EndCData :: SAXEvent tag text
+ Text.XML.Expat.SAX: ProcessingInstruction :: text -> text -> SAXEvent tag text
+ Text.XML.Expat.SAX: StartCData :: SAXEvent tag text
+ Text.XML.Expat.SAX: XMLDeclaration :: text -> (Maybe text) -> (Maybe Bool) -> SAXEvent tag text

Files

Text/XML/Expat/Annotated.hs view
@@ -43,8 +43,7 @@   parseThrowing,   XMLParseException(..), -  -- * SAX-style parse-  SAXEvent(..),+  -- * Convert from SAX   saxToTree,    -- * Abstraction of string types@@ -111,7 +110,7 @@ -- -- Note that some functions in the @Text.XML.Expat.Cursor@ module need to create -- new nodes through the 'MkElementClass' type class. Normally this can only be done--- if @a@ is a Maybe type (so it can provide the Nothing value for the annotation+-- if @a@ is a Maybe type or () (so it can provide the Nothing value for the annotation -- on newly created nodes).  Or, you can write your own 'MkElementClass' instance. -- Apart from that, there is no requirement for @a@ to be a Maybe type. data NodeG a c tag text =@@ -135,8 +134,12 @@ type Node a tag text = NodeG a [] tag text  instance (Show tag, Show text, Show a) => Show (NodeG a [] tag text) where-    show (Element na at ch an) = "Element "++show na++" "++show at++" "++show ch++" "++show an-    show (Text t) = "Text "++show t+    showsPrec d (Element na at ch an) = showParen (d > 10) $+        ("Element "++) . showsPrec 11 na . (" "++) .+                         showsPrec 11 at . (" "++) .+                         showsPrec 11 ch . (" "++) .+                         showsPrec 11 an+    showsPrec d (Text t) = showParen (d > 10) $ ("Text "++) . showsPrec 11 t  instance (Eq tag, Eq text, Eq a) => Eq (NodeG a [] tag text) where     Element na1 at1 ch1 an1 == Element na2 at2 ch2 an2 =@@ -164,13 +167,20 @@          isText (Text _) = True     isText _        = False-    ++    isCData _ = False+    isProcessingInstruction _ = False+    isComment _ = False+     isNamed _  (Text _) = False     isNamed nm (Element nm' _ _ _) = nm == nm'      getName (Text _)             = mempty     getName (Element name _ _ _) = name +    hasTarget _ _ = False+    getTarget _ = mempty+     getAttributes (Text _)              = []     getAttributes (Element _ attrs _ _) = attrs @@ -207,6 +217,9 @@ instance (Functor c, List c) => MkElementClass (NodeG (Maybe a)) c where     mkElement name attrs children = Element name attrs children Nothing +instance (Functor c, List c) => MkElementClass (NodeG ()) c where+    mkElement name attrs children = Element name attrs children ()+ -- | Convert an annotated tree (/Annotated/ module) into a non-annotated -- tree (/Tree/ module).  DEPRECATED in favour of 'fromElement'. unannotate :: Functor c => NodeG a c tag text -> Tree.NodeG c tag text@@ -214,27 +227,27 @@ unannotate (Element na at ch _) = (Tree.Element na at (fmap unannotate ch)) unannotate (Text t) = Tree.Text t --- | Type alias for a single annotated node with unqualified tag names where+-- | Type alias for an annotated node with unqualified tag names where -- tag and text are the same string type type UNode a text = Node a text text --- | Type alias for a single annotated node, annotated with parse location+-- | Type alias for an annotated node, annotated with parse location type LNode tag text = Node XMLParseLocation tag text --- | Type alias for a single node with unqualified tag names where+-- | Type alias for an annotated node with unqualified tag names where -- tag and text are the same string type, annotated with parse location type ULNode text = LNode text text  --- | Type alias for a single annotated node where qualified names are used for tags+-- | Type alias for an annotated node where qualified names are used for tags type QNode a text = Node a (QName text) text --- | Type alias for a single node where qualified names are used for tags, annotated with parse location+-- | Type alias for an annotated node where qualified names are used for tags, annotated with parse location type QLNode text = LNode (QName text) text --- | Type alias for a single annotated node where namespaced names are used for tags+-- | Type alias for an annotated node where namespaced names are used for tags type NNode a text = Node a (NName text) text --- | Type alias for a single node where namespaced names are used for tags, annotated with parse location+-- | Type alias for an annotated node where namespaced names are used for tags, annotated with parse location type NLNode text = LNode (NName text) text  -- | Modify this node's annotation (non-recursively) if it's an element, otherwise no-op.@@ -254,10 +267,11 @@           -> (Node a tag text, Maybe XMLParseError) saxToTree events =     let (nodes, mError, _) = ptl events-    in  (safeHead nodes, mError)+    in  (findRoot nodes, mError)   where-    safeHead (a:_) = a-    safeHead [] = Element (gxFromString "") [] [] (error "saxToTree null annotation")+    findRoot (elt@(Element _ _ _ _):_) = elt+    findRoot (_:nodes) = findRoot nodes+    findRoot [] = Element (gxFromString "") [] [] (error "saxToTree null annotation")     ptl ((StartElement name attrs, ann):rema) =         let (children, err1, rema') = ptl rema             elt = Element name attrs children ann@@ -268,13 +282,14 @@         let (out, err, rema') = ptl rema         in  (Text txt:out, err, rema')     ptl ((FailDocument err, _):_) = ([], Just err, [])+    ptl (_:rema) = ptl rema  -- extended node types not supported in this tree type     ptl [] = ([], Nothing, [])  -- | Lazily parse XML to tree. Note that forcing the XMLParseError return value -- will force the entire parse.  Therefore, to ensure lazy operation, don't -- check the error status until you have processed the tree. parse :: (GenericXMLString tag, GenericXMLString text) =>-         ParseOptions tag text   -- ^ Optional encoding override+         ParseOptions tag text    -- ^ Parse options       -> L.ByteString             -- ^ Input text (a lazy ByteString)       -> (LNode tag text, Maybe XMLParseError) parse opts bs = saxToTree $ SAX.parseLocations opts bs@@ -299,7 +314,7 @@ -- situations where it's not expected during normal operation, depending on the -- design of your program. parseThrowing :: (GenericXMLString tag, GenericXMLString text) =>-                 ParseOptions tag text   -- ^ Optional encoding override+                 ParseOptions tag text    -- ^ Parse options               -> L.ByteString             -- ^ Input text (a lazy ByteString)               -> LNode tag text parseThrowing opts bs = fst $ saxToTree $ SAX.parseLocationsThrowing opts bs@@ -316,7 +331,7 @@  -- | Strictly parse XML to tree. Returns error message or valid parsed tree. parse' :: (GenericXMLString tag, GenericXMLString text) =>-          ParseOptions tag text  -- ^ Optional encoding override+          ParseOptions tag text   -- ^ Parse options        -> B.ByteString            -- ^ Input text (a strict ByteString)        -> Either XMLParseError (LNode tag text) parse' opts bs = case parse opts (L.fromChunks [bs]) of
+ Text/XML/Expat/Extended.hs view
@@ -0,0 +1,432 @@+{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, TypeFamilies,+        FlexibleContexts, EmptyDataDecls #-}+-- | An extended variant of /Node/ intended to implement the entire XML+-- specification.  DTDs are not yet supported, however.+--+-- The names conflict with those in /Tree/ so you must use qualified import+-- if you want to use both modules.+module Text.XML.Expat.Extended (+  -- * Tree structure+  Document,+  DocumentG(..),+  Node,+  NodeG(..),+  UDocument,+  LDocument,+  ULDocument,+  UNode,+  LNode,+  ULNode,++  -- * Generic document/node manipulation+  module Text.XML.Expat.Internal.DocumentClass,+  module Text.XML.Expat.Internal.NodeClass,++  -- * Annotation-specific+  modifyAnnotation,+  mapAnnotation,+  mapDocumentAnnotation,++  -- * Qualified nodes+  QDocument,+  QLDocument,+  QNode,+  QLNode,+  module Text.XML.Expat.Internal.Qualified,++  -- * Namespaced nodes+  NDocument,+  NLDocument,+  NNode,+  NLNode,+  module Text.XML.Expat.Internal.Namespaced,++  -- * Parse to tree+  ParseOptions(..),+  defaultParseOptions,+  Encoding(..),+  parse,+  parse',+  XMLParseError(..),+  XMLParseLocation(..),++  -- * Variant that throws exceptions+  parseThrowing,+  XMLParseException(..),++  -- * Convert from SAX+  saxToTree,++  -- * Abstraction of string types+  GenericXMLString(..)+  ) where++import Control.Arrow+import Text.XML.Expat.SAX ( Encoding(..)+                          , GenericXMLString(..)+                          , ParseOptions(..)+                          , defaultParseOptions+                          , SAXEvent+                          , XMLParseError(..)+                          , XMLParseException(..)+                          , XMLParseLocation(..) )+import qualified Text.XML.Expat.SAX as SAX+import Text.XML.Expat.Internal.DocumentClass+import Text.XML.Expat.Internal.Namespaced+import Text.XML.Expat.Internal.NodeClass+import Text.XML.Expat.Internal.Qualified++import Control.Monad (mplus, mzero)+import Control.DeepSeq+import qualified Data.ByteString as B+import qualified Data.ByteString.Lazy as L+import Data.List.Class+import Data.Maybe+import Data.Monoid+++-- | Document representation of the XML document, intended to support the entire+-- XML specification.  DTDs are not yet supported, however.+data DocumentG a c tag text = Document {+        dXMLDeclaration          :: Maybe (XMLDeclaration text),+        dDocumentTypeDeclaration :: Maybe (DocumentTypeDeclaration c tag text),+        dTopLevelMiscs           :: c (Misc text),+        dRoot                    :: NodeG a c tag text+    }++instance (Show tag, Show text, Show a) => Show (DocumentG a [] tag text) where+    showsPrec d (Document xd dtd m r) = showParen (d > 10) $+        ("Document "++) . showsPrec 11 xd . (" "++) .+                          showsPrec 11 dtd . (" "++) .+                          showsPrec 11 m . (" "++) .+                          showsPrec 11 r++instance (Eq tag, Eq text, Eq a) => Eq (DocumentG a [] tag text) where+    Document xd1 dtd1 m1 r1 == Document xd2 dtd2 m2 r2 =+        xd1 == xd2 &&+        dtd1 == dtd2 &&+        m1 == m2 &&+        r1 == r2++-- | A pure representation of an XML document that uses a list as its container type.+type Document a tag text = DocumentG a [] tag text++type instance NodeType (DocumentG ann) = NodeG ann++instance (Functor c, List c) => DocumentClass (DocumentG ann) c where+    getXMLDeclaration          = dXMLDeclaration+    getDocumentTypeDeclaration = dDocumentTypeDeclaration+    getTopLevelMiscs           = dTopLevelMiscs+    getRoot                    = dRoot+    mkDocument                 = Document++-- | Extended variant of the tree representation of the XML document, intended+-- to support the entire XML specification.  DTDs are not yet supported, however.+--+-- @c@ is the container type for the element's children, which is [] in the+-- @hexpat@ package, and a monadic list type for @hexpat-iteratee@.+--+-- @tag@ is the tag type, which can either be one of several string types,+-- or a special type from the @Text.XML.Expat.Namespaced@ or+-- @Text.XML.Expat.Qualified@ modules.+--+-- @text@ is the string type for text content.+--+-- @a@ is the type of the annotation.  One of the things this can be used for+-- is to store the XML parse location, which is useful for error handling.+--+-- Note that some functions in the @Text.XML.Expat.Cursor@ module need to create+-- new nodes through the 'MkElementClass' type class. Normally this can only be done+-- if @a@ is a Maybe type or () (so it can provide the Nothing value for the annotation+-- on newly created nodes).  Or, you can write your own 'MkElementClass' instance.+-- Apart from that, there is no requirement for @a@ to be a Maybe type.+data NodeG a c tag text =+    Element {+        eName       :: !tag,+        eAttributes :: ![(tag,text)],+        eChildren   :: c (NodeG a c tag text),+        eAnn        :: a+    } |+    Text !text |+    CData !text |+    Misc (Misc text)++type instance ListOf (NodeG a c tag text) = c (NodeG a c tag text)++-- | A pure tree representation that uses a list as its container type,+-- extended variant.+--+-- In the @hexpat@ package, a list of nodes has the type @[Node tag text]@, but note+-- that you can also use the more general type function 'ListOf' to give a list of+-- any node type, using that node's associated list type, e.g.+-- @ListOf (UNode Text)@.+type Node a tag text = NodeG a [] tag text++instance (Show tag, Show text, Show a) => Show (NodeG a [] tag text) where+    showsPrec d (Element na at ch an) = showParen (d > 10) $+        ("Element "++) . showsPrec 11 na . (" "++) .+                         showsPrec 11 at . (" "++) .+                         showsPrec 11 ch . (" "++) .+                         showsPrec 11 an+    showsPrec d (Text t) = showParen (d > 10) $ ("Text "++) . showsPrec 11 t+    showsPrec d (CData t) = showParen (d > 10) $ ("CData "++) . showsPrec 11 t+    showsPrec d (Misc m)  = showParen (d > 10) $ ("Misc "++) . showsPrec 11 m++instance (Eq tag, Eq text, Eq a) => Eq (NodeG a [] tag text) where+    Element na1 at1 ch1 an1 == Element na2 at2 ch2 an2 =+        na1 == na2 &&+        at1 == at2 &&+        ch1 == ch2 &&+        an1 == an2+    Text t1 == Text t2 = t1 == t2+    CData t1 == CData t2 = t1 == t2+    Misc t1 == Misc t2 = t1 == t2+    _ == _ = False++instance (NFData tag, NFData text, NFData a) => NFData (NodeG a [] tag text) where+    rnf (Element nam att chi ann) = rnf (nam, att, chi, ann)+    rnf (Text txt) = rnf txt+    rnf (CData txt) = rnf txt+    rnf (Misc m) = rnf m++instance (Functor c, List c) => NodeClass (NodeG a) c where+    textContentM (Element _ _ children _) = foldlL mappend mempty $ joinM $ fmap textContentM children+    textContentM (Text txt) = return txt+    textContentM (CData txt) = return txt+    textContentM (Misc (ProcessingInstruction _ _)) = return mempty+    textContentM (Misc (Comment _)) = return mempty++    isElement (Element _ _ _ _) = True+    isElement _                 = False+    +    isText (Text _) = True+    isText (CData _) = True+    isText _        = False+    +    isCData (CData _) = True+    isCData _        = False+    +    isProcessingInstruction (Misc (ProcessingInstruction _ _)) = True+    isProcessingInstruction _        = False+    +    isComment (Misc (Comment _)) = True+    isComment _                  = False+    +    isNamed nm (Element nm' _ _ _) = nm == nm'+    isNamed _  _ = False+    +    getName (Element name _ _ _) = name+    getName _             = mempty+    +    hasTarget t (Misc (ProcessingInstruction t' _ )) = t == t'+    hasTarget _  _ = False+    +    getTarget (Misc (ProcessingInstruction target _)) = target+    getTarget _                                       = mempty++    getAttributes (Element _ attrs _ _) = attrs+    getAttributes _              = []++    getChildren (Element _ _ ch _) = ch+    getChildren _           = mzero++    getText (Text txt) = txt+    getText (CData txt) = txt+    getText (Misc (ProcessingInstruction _ txt)) = txt+    getText (Misc (Comment txt)) = txt+    getText (Element _ _ _ _) = mempty++    modifyName f (Element n a c ann) = Element (f n) a c ann+    modifyName _ node = node++    modifyAttributes f (Element n a c ann) = Element n (f a) c ann+    modifyAttributes _ node = node++    modifyChildren f (Element n a c ann) = Element n a (f c) ann+    modifyChildren _ node = node++    mapAllTags f (Element n a c ann) = Element (f n) (map (first f) a) (fmap (mapAllTags f) c) ann+    mapAllTags _ (Text txt) = Text txt+    mapAllTags _ (CData txt) = CData txt+    mapAllTags _ (Misc (ProcessingInstruction n txt)) = Misc (ProcessingInstruction n txt)+    mapAllTags _ (Misc (Comment txt)) = Misc (Comment txt)++    modifyElement f (Element n a c ann) =+        let (n', a', c') = f (n, a, c)+        in  Element n' a' c' ann+    modifyElement _ (Text txt) = Text txt+    modifyElement _ (CData txt) = CData txt+    modifyElement _ (Misc (ProcessingInstruction n txt)) = Misc (ProcessingInstruction n txt)+    modifyElement _ (Misc (Comment txt)) = Misc (Comment txt)++    mapNodeContainer f (Element n a ch an) = do+        ch' <- mapNodeListContainer f ch+        return $ Element n a ch' an+    mapNodeContainer _ (Text txt) = return $ (Text txt)+    mapNodeContainer _ (CData txt) = return $ (CData txt)+    mapNodeContainer _ (Misc (ProcessingInstruction n txt)) = return $ Misc (ProcessingInstruction n txt)+    mapNodeContainer _ (Misc (Comment txt)) = return $ Misc (Comment txt)++    mkText = Text++instance (Functor c, List c) => MkElementClass (NodeG (Maybe a)) c where+    mkElement name attrs children = Element name attrs children Nothing++instance (Functor c, List c) => MkElementClass (NodeG ()) c where+    mkElement name attrs children = Element name attrs children ()++-- | Type alias for an extended document with unqualified tag names where+-- tag and text are the same string type+type UDocument a text = Document a text text++-- | Type alias for an extended document, annotated with parse location+type LDocument tag text = Document XMLParseLocation tag text++-- | Type alias for an extended document with unqualified tag names where+-- tag and text are the same string type, annotated with parse location+type ULDocument text = Document XMLParseLocation text text++-- | Type alias for an extended document where qualified names are used for tags+type QDocument a text = Document a (QName text) text++-- | Type alias for an extended document where qualified names are used for tags, annotated with parse location+type QLDocument text = Document XMLParseLocation (QName text) text++-- | Type alias for an extended document where namespaced names are used for tags+type NDocument a text = Document a (NName text) text++-- | Type alias for an extended document where namespaced names are used for tags, annotated with parse location+type NLDocument text = Document XMLParseLocation (NName text) text++-- | Type alias for an extended node with unqualified tag names where+-- tag and text are the same string type+type UNode a text = Node a text text++-- | Type alias for an extended node, annotated with parse location+type LNode tag text = Node XMLParseLocation tag text++-- | Type alias for an extended node with unqualified tag names where+-- tag and text are the same string type, annotated with parse location+type ULNode text = LNode text text ++-- | Type alias for an extended node where qualified names are used for tags+type QNode a text = Node a (QName text) text++-- | Type alias for an extended node where qualified names are used for tags, annotated with parse location+type QLNode text = LNode (QName text) text++-- | Type alias for an extended node where namespaced names are used for tags+type NNode a text = Node a (NName text) text++-- | Type alias for an extended node where namespaced names are used for tags, annotated with parse location+type NLNode text = LNode (NName text) text++-- | Modify this node's annotation (non-recursively) if it's an element, otherwise no-op.+modifyAnnotation :: (a -> a) -> Node a tag text -> Node a tag text+f `modifyAnnotation` Element na at ch an = Element na at ch (f an)+_ `modifyAnnotation` Text t = Text t+_ `modifyAnnotation` CData t = CData t+_ `modifyAnnotation` Misc (ProcessingInstruction n t) = Misc (ProcessingInstruction n t)+_ `modifyAnnotation` Misc (Comment t) = Misc (Comment t)++-- | Modify this node's annotation and all its children recursively if it's an element, otherwise no-op.+mapAnnotation :: (a -> b) -> Node a tag text -> Node b tag text+f `mapAnnotation` Element na at ch an = Element na at (map (f `mapAnnotation`) ch) (f an)+_ `mapAnnotation` Text t = Text t+_ `mapAnnotation` CData t = CData t+_ `mapAnnotation` Misc (ProcessingInstruction n t) = Misc (ProcessingInstruction n t)+_ `mapAnnotation` Misc (Comment t) = Misc (Comment t)++-- | Modify the annotation of every node in the document recursively.+mapDocumentAnnotation :: (a -> b) -> Document a tag text -> Document b tag text+mapDocumentAnnotation f doc = Document {+        dXMLDeclaration          = dXMLDeclaration doc,+        dDocumentTypeDeclaration = dDocumentTypeDeclaration doc,+        dTopLevelMiscs           = dTopLevelMiscs doc,+        dRoot                    = mapAnnotation f (dRoot doc)+    }++-- | A lower level function that lazily converts a SAX stream into a tree structure.+-- Variant that takes annotations for start tags.+saxToTree :: (GenericXMLString tag, Monoid text) =>+             [(SAXEvent tag text, a)]+          -> (Document a tag text, Maybe XMLParseError)+saxToTree ((SAX.XMLDeclaration ver mEnc mSD, _):events) =+    let (doc, mErr) = saxToTree events+    in  (doc {+            dXMLDeclaration = Just $ XMLDeclaration ver mEnc mSD+        }, mErr)+saxToTree events =+    let (nodes, mError, _) = ptl events False []+        doc = Document {+                dXMLDeclaration          = Nothing,+                dDocumentTypeDeclaration = Nothing,+                dTopLevelMiscs           = findTopLevelMiscs nodes,+                dRoot                    = findRoot nodes+            }+    in  (doc, mError)+  where+    findRoot (elt@(Element _ _ _ _):_) = elt+    findRoot (_:nodes) = findRoot nodes+    findRoot [] = Element (gxFromString "") [] [] (error "saxToTree null annotation")+    findTopLevelMiscs = mapMaybe $ \node -> case node of+        Misc m -> Just m+        _      -> Nothing+    ptl ((SAX.StartElement name attrs,ann):rema) isCD cd =+        let (children, err1, rema') = ptl rema isCD cd+            elt = Element name attrs children ann+            (out, err2, rema'') = ptl rema' isCD cd+        in  (elt:out, err1 `mplus` err2, rema'')+    ptl ((SAX.EndElement _, _):rema) _ _ = ([], Nothing, rema)+    ptl ((SAX.CharacterData txt, _):rema) isCD cd =+        if isCD then+            ptl rema isCD (txt:cd)+        else+            let (out, err, rema') = ptl rema isCD cd+            in  (Text txt:out, err, rema')+    ptl ((SAX.StartCData,_) :rema) _ _ =+        ptl rema True mzero+    ptl ((SAX.EndCData, _) :rema) _ cd =+        let (out, err, rema') = ptl rema False mzero+        in  (CData (mconcat $ reverse cd):out, err, rema')+    ptl ((SAX.Comment txt, _):rema) isCD cd =+        let (out, err, rema') = ptl rema isCD cd+        in  (Misc (Comment txt):out, err, rema')+    ptl ((SAX.ProcessingInstruction target txt, _):rema) isCD cd =+        let (out, err, rema') = ptl rema isCD cd+        in  (Misc (ProcessingInstruction target txt):out, err, rema')+    ptl ((SAX.FailDocument err, _):_) _ _ = ([], Just err, [])+    ptl ((SAX.XMLDeclaration _ _ _, _):rema) isCD cd = ptl rema isCD cd  -- doesn't appear in the middle of a document+    ptl [] _ _ = ([], Nothing, [])++-- | Lazily parse XML to tree. Note that forcing the XMLParseError return value+-- will force the entire parse.  Therefore, to ensure lazy operation, don't+-- check the error status until you have processed the tree.+parse :: (GenericXMLString tag, GenericXMLString text) =>+         ParseOptions tag text    -- ^ Parse options+      -> L.ByteString             -- ^ Input text (a lazy ByteString)+      -> (LDocument tag text, Maybe XMLParseError)+parse opts bs = saxToTree $ SAX.parseLocations opts bs++-- | Lazily parse XML to tree. In the event of an error, throw 'XMLParseException'.+--+-- @parseThrowing@ can throw an exception from pure code, which is generally a bad+-- way to handle errors, because Haskell\'s lazy evaluation means it\'s hard to+-- predict where it will be thrown from.  However, it may be acceptable in+-- situations where it's not expected during normal operation, depending on the+-- design of your program.+parseThrowing :: (GenericXMLString tag, GenericXMLString text) =>+                 ParseOptions tag text    -- ^ Parse options+              -> L.ByteString             -- ^ Input text (a lazy ByteString)+              -> LDocument tag text+parseThrowing opts bs = fst $ saxToTree $ SAX.parseLocationsThrowing opts bs++-- | Strictly parse XML to tree. Returns error message or valid parsed tree.+parse' :: (GenericXMLString tag, GenericXMLString text) =>+          ParseOptions tag text   -- ^ Parse options+       -> B.ByteString            -- ^ Input text (a strict ByteString)+       -> Either XMLParseError (LDocument tag text)+parse' opts bs = case parse opts (L.fromChunks [bs]) of+    (_, Just err)   -> Left err+    (root, Nothing) -> Right root +
Text/XML/Expat/Format.hs view
@@ -5,6 +5,17 @@  -- | This module provides functions to format a tree -- structure or SAX stream as UTF-8 encoded XML.+--+-- The formatting functions always outputs only UTF-8, regardless+-- of what encoding is specified in the document's 'Doc.XMLDeclaration'.+-- If you want to output a document in another encoding, then make sure the+-- 'Doc.XMLDeclaration' agrees with the final output encoding, then format the+-- document, and convert from UTF-8 to your desired encoding using some text+-- conversion library.+--+-- The lazy 'L.ByteString' representation of the output in generated with very+-- small chunks, so in some applications you may want to combine them into+-- larger chunks to get better efficiency. module Text.XML.Expat.Format (         -- * High level         format,@@ -13,12 +24,17 @@         formatNode,         formatNode',         formatNodeG,+        -- * Format document (for use with Extended.hs)+        formatDocument,+        formatDocument',+        formatDocumentG,         -- * Deprecated names         formatTree,         formatTree',         -- * Low level         xmlHeader,         treeToSAX,+        documentToSAX,         formatSAX,         formatSAX',         formatSAXG,@@ -27,6 +43,7 @@         indent_     ) where +import qualified Text.XML.Expat.Internal.DocumentClass as Doc import Text.XML.Expat.Internal.NodeClass import Text.XML.Expat.SAX @@ -89,10 +106,44 @@            -> c B.ByteString formatNodeG = formatSAXG . treeToSAX +-- | Format an XML document - lazy variant that returns lazy ByteString.+formatDocument :: (Doc.DocumentClass d [], GenericXMLString tag, GenericXMLString text) =>+                  d [] tag text+               -> L.ByteString+formatDocument = formatSAX . documentToSAX++-- | Format an XML document - strict variant that returns strict ByteString.+formatDocument' :: (Doc.DocumentClass d [], GenericXMLString tag, GenericXMLString text) =>+                   d [] tag text+                -> B.ByteString+formatDocument' = B.concat . L.toChunks . formatDocument++-- | Format an XML document - generalized variant that returns a generic+-- list of strict ByteStrings.+formatDocumentG :: (Doc.DocumentClass d c, GenericXMLString tag, GenericXMLString text) =>+                   d c tag text+                -> c B.ByteString+formatDocumentG = formatSAXG . documentToSAX+ -- | The standard XML header with UTF-8 encoding. xmlHeader :: B.ByteString xmlHeader = B.pack $ map c2w "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +documentToSAX :: forall tag text d c . (GenericXMLString tag, GenericXMLString text,+                     Monoid text, Doc.DocumentClass d c) =>+                 d c tag text -> c (SAXEvent tag text)+documentToSAX doc =+    (case Doc.getXMLDeclaration doc of+        Just (Doc.XMLDeclaration ver mEnc sd) -> fromList [+                  XMLDeclaration ver mEnc sd, CharacterData (gxFromString "\n")]+        Nothing                               -> mzero) `mplus`+    join (fmap (\misc -> fromList [case misc of+            Doc.ProcessingInstruction target text -> ProcessingInstruction target text+            Doc.Comment text                      -> Comment text,+            CharacterData (gxFromString "\n")]+        ) (Doc.getTopLevelMiscs doc)) `mplus`+    treeToSAX (Doc.getRoot doc)+ -- | Flatten a tree structure into SAX events, monadic version. treeToSAX :: forall tag text n c . (GenericXMLString tag, GenericXMLString text,                  Monoid text, NodeClass n c) =>@@ -110,8 +161,14 @@                     Cons n l' -> cons n (postpend l')         in  cons (StartElement name atts) $             postpend (concatL $ fmap treeToSAX children)+    | isCData node =+        cons StartCData (cons (CharacterData $ getText node) (singleton EndCData))     | isText node =-        singleton (CharacterData $ getText node)+        singleton (CharacterData $ getText node)        +    | isProcessingInstruction node =+        singleton (ProcessingInstruction (getTarget node) (getText node))+    | isComment node =+        singleton (Comment $ getText node)         | otherwise = mzero   where     singleton = return@@ -152,12 +209,40 @@               GenericXMLString text) =>           c (SAXEvent tag text)    -- ^ SAX events        -> c B.ByteString-formatSAXG l1 = joinL $ do+formatSAXG l1 = formatSAXGb l1 False++formatSAXGb :: forall c tag text . (List c, GenericXMLString tag,+              GenericXMLString text) =>+          c (SAXEvent tag text)    -- ^ SAX events+       -> Bool                     -- ^ True if processing CDATA+       -> c B.ByteString+formatSAXGb l1 cd = joinL $ do     it1 <- runList l1     return $ formatItem it1   where     formatItem it1 = case it1 of         Nil -> mzero+        Cons (XMLDeclaration ver mEnc mSD) l2 ->+            return (pack "<?xml version=\"") `mplus`+            fromList (escapeText (gxToByteString ver)) `mplus`+            return (pack "\"") `mplus`+            (+                case mEnc of+                    Nothing -> mzero+                    Just enc ->+                        return (pack " encoding=\"") `mplus`+                        fromList (escapeText (gxToByteString enc)) `mplus`+                        return (pack "\"")+            ) `mplus`+            (+                case mSD of+                    Nothing -> mzero+                    Just True  -> return (pack " standalone=\"yes\"")+                    Just False -> return (pack " standalone=\"no\"")+            ) `mplus`+            return (pack ("?>"))+            `mplus`+            formatSAXGb l2 cd         Cons (StartElement name attrs) l2 ->             fromList (startTagHelper name attrs)             `mplus` (@@ -166,7 +251,7 @@                     return $ case it2 of                         Cons (EndElement _) l3 ->                             cons (pack "/>") $-                            formatSAXG l3+                            formatSAXGb l3 cd                         _ ->                             cons (B.singleton (c2w '>')) $                             formatItem it2@@ -175,13 +260,33 @@             cons (pack "</") $             cons (gxToByteString name) $             cons (B.singleton (c2w '>')) $-            formatSAXG l2+            formatSAXGb l2 cd         Cons (CharacterData txt) l2 ->-            fromList (escapeText (gxToByteString txt))-            `mplus`-            formatSAXG l2+            (if cd then+                fromList [gxToByteString txt]+             else+                fromList (escapeText (gxToByteString txt))+            ) `mplus` (formatSAXGb l2 cd)+        Cons StartCData l2 ->+            cons(pack "<![CDATA[") $+            formatSAXGb l2 True+        Cons EndCData l2 ->+            cons(pack "]]>") $+            formatSAXGb l2 False+        Cons (ProcessingInstruction target txt) l2 ->+            cons (pack "<?") $+            cons (gxToByteString target) $+            cons (pack " ") $+            cons (gxToByteString txt) $+            cons (pack "?>") $+            formatSAXGb l2 cd+        Cons (Comment txt) l2 ->+            cons (pack "<!--") $+            cons (gxToByteString txt) $+            cons (pack "-->") $+            formatSAXGb l2 cd         Cons (FailDocument _) l2 ->-            formatSAXG l2+            formatSAXGb l2 cd  pack :: String -> B.ByteString pack = B.pack . map c2w
+ Text/XML/Expat/Internal/DocumentClass.hs view
@@ -0,0 +1,98 @@+{-# LANGUAGE MultiParamTypeClasses, TypeFamilies, FlexibleContexts #-}+-- | Type classes to allow for XML handling functions to be generalized to+-- work with different document types.+module Text.XML.Expat.Internal.DocumentClass where++import Text.XML.Expat.Internal.NodeClass (NodeClass)+import Control.DeepSeq+import Control.Monad (mzero)+import Data.List.Class+++-- | XML declaration, consisting of version, encoding and standalone.+--+-- The formatting functions always outputs only UTF-8, regardless+-- of what encoding is specified here.  If you want to produce a document in a+-- different encoding, then set the encoding here, format the document, and then+-- convert the output text from UTF-8 to your desired encoding using some+-- text conversion library.+data XMLDeclaration text = XMLDeclaration text (Maybe text) (Maybe Bool) deriving (Eq, Show)++-- | Stub for future expansion.+data DocumentTypeDeclaration c tag text = DocumentTypeDeclaration deriving (Eq, Show)++data Misc text =+    Comment !text |+    ProcessingInstruction !text !text++instance Show text => Show (Misc text) where+    showsPrec d (ProcessingInstruction t txt) = showParen (d > 10) $+        ("ProcessingInstruction "++) . showsPrec 11 t . (" "++) . showsPrec 11 txt+    showsPrec d (Comment t) = showParen (d > 10) $ ("Comment "++) . showsPrec 11 t++instance Eq text => Eq (Misc text) where+    ProcessingInstruction t1 d1 == ProcessingInstruction t2 d2 = +        t1 == t2 &&+        d1 == d2+    Comment t1 == Comment t2 = t1 == t2+    _ == _ = False++instance NFData text => NFData (Misc text) where+    rnf (ProcessingInstruction target txt) = rnf (target, txt)+    rnf (Comment txt) = rnf txt++type family NodeType d :: (* -> *) -> * -> * -> *++class (Functor c, List c, NodeClass (NodeType d) c) => DocumentClass d c where+    -- | Get the XML declaration for this document.+    getXMLDeclaration :: d c tag text -> Maybe (XMLDeclaration text)++    -- | Get the Document Type Declaration (DTD) for this document.+    getDocumentTypeDeclaration :: d c tag text -> Maybe (DocumentTypeDeclaration c tag text)++    -- | Get the top-level 'Misc' nodes for this document.+    getTopLevelMiscs :: d c tag text -> c (Misc text)++    -- | Get the root element for this document.+    getRoot :: d c tag text -> NodeType d c tag text++    -- | Make a document with the specified fields.+    mkDocument :: Maybe (XMLDeclaration text)+               -> Maybe (DocumentTypeDeclaration c tag text)+               -> c (Misc text)+               -> NodeType d c tag text+               -> d c tag text++-- | Make a document with the specified root node and all other information+-- set to defaults.+mkPlainDocument :: DocumentClass d c => NodeType d c tag text -> d c tag text+mkPlainDocument = mkDocument Nothing Nothing mzero++modifyXMLDeclaration :: DocumentClass d c =>+                        (Maybe (XMLDeclaration text) -> Maybe (XMLDeclaration text))+                     -> d c tag text+                     -> d c tag text+modifyXMLDeclaration f doc = mkDocument (f $ getXMLDeclaration doc) (getDocumentTypeDeclaration doc)+        (getTopLevelMiscs doc) (getRoot doc)++modifyDocumentTypeDeclaration :: DocumentClass d c =>+                                 (Maybe (DocumentTypeDeclaration c tag text) -> Maybe (DocumentTypeDeclaration c tag text))+                              -> d c tag text+                              -> d c tag text+modifyDocumentTypeDeclaration f doc = mkDocument (getXMLDeclaration doc) (f $ getDocumentTypeDeclaration doc)+        (getTopLevelMiscs doc) (getRoot doc)++modifyTopLevelMiscs :: DocumentClass d c =>+                       (c (Misc text) -> c (Misc text))+                    -> d c tag text+                    -> d c tag text+modifyTopLevelMiscs f doc = mkDocument (getXMLDeclaration doc) (getDocumentTypeDeclaration doc)+        (f $ getTopLevelMiscs doc) (getRoot doc)++modifyRoot :: DocumentClass d c =>+              (NodeType d c tag text -> NodeType d c tag text)+           -> d c tag text+           -> d c tag text+modifyRoot f doc = mkDocument (getXMLDeclaration doc) (getDocumentTypeDeclaration doc)+        (getTopLevelMiscs doc) (f $ getRoot doc)+
Text/XML/Expat/Internal/IO.hs view
@@ -1,12 +1,8 @@ {-# LANGUAGE ForeignFunctionInterface, EmptyDataDecls #-} --- hexpat, a Haskell wrapper for expat--- Copyright (C) 2008 Evan Martin <martine@danga.com>--- Copyright (C) 2009 Stephen Blackheath <http://blacksapphire.com/antispam>- -- | Low-level interface to Expat. Unless speed is paramount, this should -- normally be avoided in favour of the interfaces provided by--- 'Text.XML.Expat.SAX' and 'Text.XML.Expat.Tree'.  Basic usage is:+-- 'Text.XML.Expat.SAX' and 'Text.XML.Expat.Tree', etc.  Basic usage is: -- -- (1) Make a new parser: 'newParser'. --@@ -30,14 +26,24 @@   XMLParseLocation(..),    -- ** Parser Callbacks+  XMLDeclarationHandler,   StartElementHandler,   EndElementHandler,   CharacterDataHandler,   ExternalEntityRefHandler,   SkippedEntityHandler,+  StartCDataHandler,+  EndCDataHandler,+  CommentHandler,+  ProcessingInstructionHandler,+  setXMLDeclarationHandler,   setStartElementHandler,   setEndElementHandler,   setCharacterDataHandler,+  setStartCDataHandler,+  setEndCDataHandler,+  setProcessingInstructionHandler,+  setCommentHandler,   setExternalEntityRefHandler,   setSkippedEntityHandler,   setUseForeignDTD,@@ -64,16 +70,21 @@ data Parser_struct type ParserPtr = Ptr Parser_struct data Parser = Parser-    { _parserObj                :: ForeignPtr Parser_struct-    , _startElementHandler      :: IORef CStartElementHandler-    , _endElementHandler        :: IORef CEndElementHandler-    , _cdataHandler             :: IORef CCharacterDataHandler-    , _externalEntityRefHandler :: IORef (Maybe CExternalEntityRefHandler)-    , _skippedEntityHandler     :: IORef (Maybe CSkippedEntityHandler)+    { _parserObj                    :: ForeignPtr Parser_struct+    , _xmlDeclarationHandler        :: IORef CXMLDeclarationHandler+    , _startElementHandler          :: IORef CStartElementHandler+    , _endElementHandler            :: IORef CEndElementHandler+    , _cdataHandler                 :: IORef CCharacterDataHandler+    , _externalEntityRefHandler     :: IORef (Maybe CExternalEntityRefHandler)+    , _skippedEntityHandler         :: IORef (Maybe CSkippedEntityHandler)+    , _startCDataHandler            :: IORef CStartCDataHandler+    , _endCDataHandler              :: IORef CEndCDataHandler+    , _processingInstructionHandler :: IORef CProcessingInstructionHandler+    , _commentHandler               :: IORef CCommentHandler     }  instance Show Parser where-    showsPrec _ (Parser fp _ _ _ _ _) = showsPrec 0 fp+    showsPrec _ (Parser fp _ _ _ _ _ _ _ _ _ _) = showsPrec 0 fp  -- |Encoding types available for the document encoding. data Encoding = ASCII | UTF8 | UTF16 | ISO88591@@ -98,16 +109,22 @@ -- | Create a 'Parser'. newParser :: Maybe Encoding -> IO Parser newParser enc = do-  ptr        <- parserCreate enc-  fptr       <- newForeignPtr parserFree ptr-  nullStartH <- newIORef nullCStartElementHandler-  nullEndH   <- newIORef nullCEndElementHandler-  nullCharH  <- newIORef nullCCharacterDataHandler-  extH       <- newIORef Nothing-  skipH      <- newIORef Nothing--  return $ Parser fptr nullStartH nullEndH nullCharH extH skipH+  ptr          <- parserCreate enc+  fptr         <- newForeignPtr parserFree ptr+  nullXMLDeclH <- newIORef nullCXMLDeclarationHandler+  nullStartH   <- newIORef nullCStartElementHandler+  nullEndH     <- newIORef nullCEndElementHandler+  nullCharH    <- newIORef nullCCharacterDataHandler+  extH         <- newIORef Nothing+  skipH        <- newIORef Nothing+  nullSCDataH  <- newIORef nullCStartCDataHandler+  nullECDataH  <- newIORef nullCEndCDataHandler+  nullPIH      <- newIORef nullCProcessingInstructionHandler+  nullCommentH <- newIORef nullCCommentHandler +  return $ Parser fptr nullXMLDeclH nullStartH nullEndH nullCharH extH skipH +    nullSCDataH nullECDataH nullPIH nullCommentH+   setUseForeignDTD :: Parser -> Bool -> IO () setUseForeignDTD p b = withParser p $ \p' -> xmlUseForeignDTD p' b'   where@@ -188,21 +205,27 @@     (FunPtr CCharacterDataHandler)     (Maybe (FunPtr CExternalEntityRefHandler))     (Maybe (FunPtr CSkippedEntityHandler))+    (FunPtr CStartCDataHandler)+    (FunPtr CEndCDataHandler)+    (FunPtr CProcessingInstructionHandler)+    (FunPtr CCommentHandler)  -- | Most of the low-level functions take a ParserPtr so are required to be -- called inside @withParser@. withParser :: Parser            -> (ParserPtr -> IO a)  -- ^ Computation where parseChunk and other low-level functions may be used            -> IO a-withParser parser@(Parser fp _ _ _ _ _) code = withForeignPtr fp $ \pp -> do+withParser parser@(Parser fp _ _ _ _ _ _ _ _ _ _) code = withForeignPtr fp $ \pp -> do     bracket         (unsafeSetHandlers parser pp)         unsafeReleaseHandlers         (\_ -> code pp)   where     unsafeSetHandlers :: Parser -> ParserPtr -> IO ExpatHandlers-    unsafeSetHandlers (Parser _ startRef endRef charRef extRef skipRef) pp =+    unsafeSetHandlers (Parser _ xmlDeclRef startRef endRef charRef extRef skipRef +        startCDataRef endCDataRef processingInstructionRef commentRef) pp =       do+        cXMLDeclH <- mkCXMLDeclarationHandler =<< readIORef xmlDeclRef         cStartH <- mkCStartElementHandler =<< readIORef startRef         cEndH   <- mkCEndElementHandler =<< readIORef endRef         cCharH  <- mkCCharacterDataHandler =<< readIORef charRef@@ -213,10 +236,21 @@         mSkipH  <- readIORef skipRef >>=                        maybe (return Nothing)                              (\h -> liftM Just $ mkCSkippedEntityHandler h)+                             +        cStartCDataH <- mkCStartCDataHandler =<< readIORef startCDataRef+        cEndCDataH   <- mkCEndCDataHandler =<< readIORef endCDataRef+        +        cProcessingInstructionH   <- mkCProcessingInstructionHandler =<< readIORef processingInstructionRef+        cCommentH   <- mkCCommentHandler =<< readIORef commentRef     +        xmlSetxmldeclhandler       pp cXMLDeclH         xmlSetstartelementhandler  pp cStartH         xmlSetendelementhandler    pp cEndH         xmlSetcharacterdatahandler pp cCharH+        xmlSetstartcdatahandler  pp cStartCDataH+        xmlSetendcdatahandler    pp cEndCDataH+        xmlSetprocessinginstructionhandler pp cProcessingInstructionH+        xmlSetcommenthandler pp cCommentH         maybe (return ())               (xmlSetExternalEntityRefHandler pp)               mExtH@@ -224,16 +258,21 @@               (xmlSetSkippedEntityHandler pp)               mSkipH     -        return $ ExpatHandlers cStartH cEndH cCharH mExtH mSkipH+        return $ ExpatHandlers cStartH cEndH cCharH mExtH mSkipH +            cStartCDataH cEndCDataH cProcessingInstructionH cCommentH          unsafeReleaseHandlers :: ExpatHandlers -> IO ()-    unsafeReleaseHandlers (ExpatHandlers cStartH cEndH cCharH mcExtH mcSkipH) = do+    unsafeReleaseHandlers (ExpatHandlers cStartH cEndH cCharH mcExtH mcSkipH +            cStartCDataH cEndCDataH cProcessingInstructionH cCommentH) = do         freeHaskellFunPtr cStartH         freeHaskellFunPtr cEndH         freeHaskellFunPtr cCharH         maybe (return ()) freeHaskellFunPtr mcExtH         maybe (return ()) freeHaskellFunPtr mcSkipH-+        freeHaskellFunPtr cStartCDataH+        freeHaskellFunPtr cEndCDataH+        freeHaskellFunPtr cProcessingInstructionH+        freeHaskellFunPtr cCommentH  -- |Obtain C value from Haskell 'Bool'. --@@ -279,6 +318,12 @@             xmlByteCount = fromIntegral count         } +-- | The type of the \"XML declaration\" callback.  Parameters are version,+-- encoding (which can be nullPtr), and standalone declaration, where -1 = no+-- declaration, 0 = "no" and 1 = "yes". Return True to continue parsing as+-- normal, or False to terminate the parse.+type XMLDeclarationHandler = ParserPtr -> CString -> CString -> CInt -> IO Bool+ -- | The type of the \"element started\" callback.  The first parameter is the -- element name; the second are the (attribute, value) pairs. Return True to -- continue parsing as normal, or False to terminate the parse.@@ -295,6 +340,25 @@ -- parsing as normal, or False to terminate the parse. type CharacterDataHandler = ParserPtr -> CStringLen -> IO Bool +-- | The type of the \"start cdata\" callback.   Return True to continue+-- parsing as normal, or False to terminate the parse.+type StartCDataHandler = ParserPtr -> IO Bool++-- | The type of the \"end cdata\" callback.   Return True to continue+-- parsing as normal, or False to terminate the parse.+type EndCDataHandler = ParserPtr -> IO Bool++-- | The type of the \"processing instruction\" callback.  The first parameter+-- is the first word in the processing instruction.  The second parameter is+-- the rest of the characters in the processing instruction after skipping all+-- whitespace after the initial word. Return True to continue parsing as normal,+-- or False to terminate the parse.+type ProcessingInstructionHandler = ParserPtr -> CString -> CString -> IO Bool++-- | The type of the \"comment\" callback.  The parameter is the comment text.+-- Return True to continue parsing as normal, or False to terminate the parse.+type CommentHandler = ParserPtr -> CString -> IO Bool+ -- | The type of the \"external entity reference\" callback. See the expat -- documentation. type ExternalEntityRefHandler =  Parser@@ -316,6 +380,35 @@                           -> Int       -- is a parameter entity?                           -> IO Bool ++type CXMLDeclarationHandler = ParserPtr -> CString -> CString -> CInt -> IO ()++nullCXMLDeclarationHandler :: CXMLDeclarationHandler+nullCXMLDeclarationHandler _ _ _ _ = return ()++foreign import ccall safe "wrapper"+  mkCXMLDeclarationHandler :: CXMLDeclarationHandler+                           -> IO (FunPtr CXMLDeclarationHandler)++wrapXMLDeclarationHandler :: Parser -> XMLDeclarationHandler -> CXMLDeclarationHandler+wrapXMLDeclarationHandler parser handler = h+  where+    h pp ver enc sd | ver /= nullPtr = do+        stillRunning <- handler pp ver enc sd+        unless stillRunning $ stopp parser+    {- From expat.h:+       The XML declaration handler is called for *both* XML declarations+       and text declarations. The way to distinguish is that the version+       parameter will be NULL for text declarations.+     -}+    h _ _ _ _ = return ()  -- text declaration (ignore)++-- | Attach a XMLDeclarationHandler to a Parser.+setXMLDeclarationHandler :: Parser -> XMLDeclarationHandler -> IO ()+setXMLDeclarationHandler parser@(Parser _ xmlDeclRef _ _ _ _ _ _ _ _ _) handler =+    writeIORef xmlDeclRef $ wrapXMLDeclarationHandler parser handler++ type CStartElementHandler = ParserPtr -> CString -> Ptr CString -> IO ()  nullCStartElementHandler :: CStartElementHandler@@ -335,9 +428,10 @@  -- | Attach a StartElementHandler to a Parser. setStartElementHandler :: Parser -> StartElementHandler -> IO ()-setStartElementHandler parser@(Parser _ startRef _ _ _ _) handler =+setStartElementHandler parser@(Parser _ _ startRef _ _ _ _ _ _ _ _) handler =     writeIORef startRef $ wrapStartElementHandler parser handler + type CEndElementHandler = ParserPtr -> CString -> IO ()  nullCEndElementHandler :: CEndElementHandler@@ -355,9 +449,10 @@  -- | Attach an EndElementHandler to a Parser. setEndElementHandler :: Parser -> EndElementHandler -> IO ()-setEndElementHandler parser@(Parser _ _ endRef _ _ _) handler =+setEndElementHandler parser@(Parser _ _ _ endRef _ _ _ _ _ _ _) handler =     writeIORef endRef $ wrapEndElementHandler parser handler + type CCharacterDataHandler = ParserPtr -> CString -> CInt -> IO ()  nullCCharacterDataHandler :: CCharacterDataHandler@@ -375,9 +470,98 @@  -- | Attach an CharacterDataHandler to a Parser. setCharacterDataHandler :: Parser -> CharacterDataHandler -> IO ()-setCharacterDataHandler parser@(Parser _ _ _ charRef _ _) handler =+setCharacterDataHandler parser@(Parser _ _ _ _ charRef _ _ _ _ _ _) handler =     writeIORef charRef $ wrapCharacterDataHandler parser handler ++type CStartCDataHandler = ParserPtr -> IO ()++nullCStartCDataHandler :: CStartCDataHandler+nullCStartCDataHandler _ = return ()++foreign import ccall safe "wrapper"+  mkCStartCDataHandler :: CStartCDataHandler+                         -> IO (FunPtr CStartCDataHandler)++wrapStartCDataHandler :: Parser -> StartCDataHandler -> CStartCDataHandler+wrapStartCDataHandler parser handler = h+  where+    h pp = do+        stillRunning <- handler pp+        unless stillRunning $ stopp parser++-- | Attach a StartCDataHandler to a Parser.+setStartCDataHandler :: Parser -> StartCDataHandler -> IO ()+setStartCDataHandler parser@(Parser _ _ _ _ _ _ _ startCData _ _ _) handler =+    writeIORef startCData $ wrapStartCDataHandler parser handler+++type CEndCDataHandler = ParserPtr -> IO ()++nullCEndCDataHandler :: CEndCDataHandler+nullCEndCDataHandler _ = return ()++foreign import ccall safe "wrapper"+  mkCEndCDataHandler :: CEndCDataHandler+                         -> IO (FunPtr CEndCDataHandler)++wrapEndCDataHandler :: Parser -> EndCDataHandler -> CEndCDataHandler+wrapEndCDataHandler parser handler = h+  where+    h pp = do+        stillRunning <- handler pp+        unless stillRunning $ stopp parser++-- | Attach a EndCDataHandler to a Parser.+setEndCDataHandler :: Parser -> EndCDataHandler -> IO ()+setEndCDataHandler parser@(Parser _ _ _ _ _ _ _ _ endCData _ _) handler =+    writeIORef endCData $ wrapEndCDataHandler parser handler+++type CProcessingInstructionHandler = ParserPtr -> CString -> CString -> IO ()++nullCProcessingInstructionHandler :: CProcessingInstructionHandler+nullCProcessingInstructionHandler _ _ _ = return ()++foreign import ccall safe "wrapper"+  mkCProcessingInstructionHandler :: CProcessingInstructionHandler+                          -> IO (FunPtr CProcessingInstructionHandler)+                          +wrapProcessingInstructionHandler :: Parser -> ProcessingInstructionHandler -> CProcessingInstructionHandler+wrapProcessingInstructionHandler parser handler = h+  where+    h pp ctarget cdata = do+        stillRunning <- handler pp ctarget cdata+        unless stillRunning $ stopp parser++-- | Attach a ProcessingInstructionHandler to a Parser.+setProcessingInstructionHandler :: Parser -> ProcessingInstructionHandler -> IO ()+setProcessingInstructionHandler parser@(Parser _ _ _ _ _ _ _ _ _ piRef _) handler =+    writeIORef piRef $ wrapProcessingInstructionHandler parser handler+++type CCommentHandler = ParserPtr -> CString -> IO ()++nullCCommentHandler :: CCommentHandler+nullCCommentHandler _ _ = return ()++foreign import ccall safe "wrapper"+  mkCCommentHandler :: CCommentHandler+                          -> IO (FunPtr CCommentHandler)+                          +wrapCommentHandler :: Parser -> CommentHandler -> CCommentHandler+wrapCommentHandler parser handler = h+  where+    h pp cdata = do+        stillRunning <- handler pp cdata+        unless stillRunning $ stopp parser++-- | Attach a CommentHandler to a Parser.+setCommentHandler :: Parser -> CommentHandler -> IO ()+setCommentHandler parser@(Parser _ _ _ _ _ _ _ _ _ _ commentRef) handler =+    writeIORef commentRef $ wrapCommentHandler parser handler++ pairwise :: [a] -> [(a,a)] pairwise (x1:x2:xs) = (x1,x2) : pairwise xs pairwise _          = []@@ -394,14 +578,29 @@ foreign import ccall unsafe "XML_SetUserData"   xmlSetUserData :: ParserPtr -> ParserPtr -> IO () +foreign import ccall unsafe "XML_SetXmlDeclHandler"+  xmlSetxmldeclhandler :: ParserPtr -> FunPtr CXMLDeclarationHandler -> IO ()+ foreign import ccall unsafe "XML_SetStartElementHandler"   xmlSetstartelementhandler :: ParserPtr -> ((FunPtr (ParserPtr -> ((Ptr CChar) -> ((Ptr (Ptr CChar)) -> (IO ())))) -> (IO ())))  foreign import ccall unsafe "XML_SetEndElementHandler"   xmlSetendelementhandler :: ParserPtr -> ((FunPtr (ParserPtr -> ((Ptr CChar) -> (IO ()))) -> (IO ())))-+   foreign import ccall unsafe "XML_SetCharacterDataHandler"   xmlSetcharacterdatahandler :: ParserPtr -> ((FunPtr (ParserPtr -> ((Ptr CChar) -> (CInt -> (IO ())))) -> (IO ())))+  +foreign import ccall unsafe "XML_SetStartCdataSectionHandler"+  xmlSetstartcdatahandler :: ParserPtr -> FunPtr CStartCDataHandler -> IO ()++foreign import ccall unsafe "XML_SetEndCdataSectionHandler"+  xmlSetendcdatahandler :: ParserPtr -> FunPtr CStartCDataHandler -> IO ()+  +foreign import ccall unsafe "XML_SetCommentHandler"+  xmlSetcommenthandler :: ParserPtr -> ((FunPtr (ParserPtr -> ((Ptr CChar) -> (IO ()))) -> (IO ())))+  +foreign import ccall unsafe "XML_SetProcessingInstructionHandler"+  xmlSetprocessinginstructionhandler :: ParserPtr -> ((FunPtr (ParserPtr -> ((Ptr CChar) -> ((Ptr CChar) -> (IO ())))) -> (IO ())))  foreign import ccall safe "XML_Parse"   doParseChunk'_ :: ParserPtr -> ((Ptr CChar) -> (CInt -> (CInt -> (IO CInt))))
Text/XML/Expat/Internal/NodeClass.hs view
@@ -20,7 +20,8 @@ type UAttributes text = Attributes text text  -- | Extract all text content from inside a tag into a single string, including--- any text contained in children.+-- any text contained in children.  This /excludes/ the contents of /comments/ or+-- /processing instructions/.  To get the text for these node types, use 'getText'. textContent :: (NodeClass n [], Monoid text) => n [] tag text -> text textContent node = runIdentity $ textContentM node @@ -35,9 +36,19 @@      -- | Is the given node text?     isText :: n c tag text -> Bool+    +    -- | Is the given node CData?+    isCData :: n c tag text -> Bool+    +    -- | Is the given node a processing instruction?+    isProcessingInstruction :: n c tag text -> Bool+    +    -- | Is the given node a comment?+    isComment :: n c tag text -> Bool      -- | Extract all text content from inside a tag into a single string, including-    -- any text contained in children.+    -- any text contained in children.  This /excludes/ the contents of /comments/ or+    -- /processing instructions/.  To get the text for these node types, use 'getText'.     textContentM :: Monoid text => n c tag text -> ItemM c text      -- | Is the given node a tag with the given name?@@ -45,14 +56,21 @@      -- | Get the name of this node if it's an element, return empty string otherwise.     getName :: Monoid tag => n c tag text -> tag+    +    -- | Is the given node a Processing Instruction with the given target?+    hasTarget :: Eq text => text -> n c tag text -> Bool +    -- | Get the target of this node if it's a Processing Instruction, return empty string otherwise.+    getTarget :: Monoid text => n c tag text -> text+     -- | Get the attributes of a node if it's an element, return empty list otherwise.     getAttributes :: n c tag text -> [(tag,text)]      -- | Get children of a node if it's an element, return empty list otherwise.     getChildren :: n c tag text -> c (n c tag text) -    -- | Get this node's text if it's a text node, return empty text otherwise.+    -- | Get this node's text if it's a text node, comment, or processing instruction,+    -- return empty text otherwise.     getText :: Monoid text => n c tag text -> text      -- | Modify name if it's an element, no-op otherwise.
Text/XML/Expat/SAX.hs view
@@ -138,19 +138,28 @@   data SAXEvent tag text =+    XMLDeclaration text (Maybe text) (Maybe Bool) |     StartElement tag [(tag, text)] |     EndElement tag |     CharacterData text |+    StartCData |+    EndCData |+    ProcessingInstruction text text |+    Comment text |     FailDocument XMLParseError     deriving (Eq, Show)  instance (NFData tag, NFData text) => NFData (SAXEvent tag text) where-    rnf (StartElement tag atts) = rnf (tag, atts)+    rnf (XMLDeclaration ver mEnc mSD) = rnf ver `seq` rnf mEnc `seq` rnf mSD+    rnf (StartElement tag atts) = rnf tag `seq` rnf atts     rnf (EndElement tag) = rnf tag     rnf (CharacterData text) = rnf text+    rnf StartCData = ()+    rnf EndCData = ()+    rnf (ProcessingInstruction target text) = rnf target `seq` rnf text+    rnf (Comment text) = rnf text     rnf (FailDocument err) = rnf err - -- | Converts a 'CString' to a 'GenericXMLString' type. textFromCString :: GenericXMLString text => CString -> IO text {-# INLINE textFromCString #-}@@ -191,7 +200,7 @@ -- | Lazily parse XML to SAX events. In the event of an error, FailDocument is -- the last element of the output list. parse :: (GenericXMLString tag, GenericXMLString text) =>-         ParseOptions tag text -- ^ Parser options+         ParseOptions tag text -- ^ Parse options       -> L.ByteString           -- ^ Input text (a lazy ByteString)       -> [SAXEvent tag text] parse opts input = unsafePerformIO $ do@@ -206,6 +215,17 @@             modifyIORef queueRef (CharacterData txt:)         Nothing -> return () +    setXMLDeclarationHandler parser $ \_ cVer cEnc cSd -> do+        ver <- textFromCString cVer+        mEnc <- if cEnc == nullPtr+            then return Nothing+            else Just <$> textFromCString cEnc+        let sd = if cSd < 0+                then Nothing+                else Just $ if cSd /= 0 then True else False+        modifyIORef queueRef (XMLDeclaration ver mEnc sd:)+        return True+     setStartElementHandler parser $ \_ cName cAttrs -> do         name <- textFromCString cName         attrs <- forM cAttrs $ \(cAttrName,cAttrValue) -> do@@ -224,6 +244,25 @@         txt <- gxFromCStringLen cText         modifyIORef queueRef (CharacterData txt:)         return True+        +    setStartCDataHandler parser $ \_  -> do+        modifyIORef queueRef (StartCData :)+        return True+        +    setEndCDataHandler parser $ \_  -> do+        modifyIORef queueRef (EndCData :)+        return True+        +    setProcessingInstructionHandler parser $ \_ cTarget cText -> do+        target <- textFromCString cTarget+        txt <- textFromCString cText+        modifyIORef queueRef (ProcessingInstruction target txt :)+        return True+        +    setCommentHandler parser $ \_ cText -> do+        txt <- textFromCString cText+        modifyIORef queueRef (Comment txt :)+        return True      let runParser inp = unsafeInterleaveIO $ do             rema <- withParser parser $ \pp -> case inp of@@ -266,7 +305,7 @@  -- | A variant of parseSAX that gives a document location with each SAX event. parseLocations :: (GenericXMLString tag, GenericXMLString text) =>-                  ParseOptions tag text  -- ^ Parser options+                  ParseOptions tag text  -- ^ Parse options                -> L.ByteString            -- ^ Input text (a lazy ByteString)                -> [(SAXEvent tag text, XMLParseLocation)] parseLocations opts input = unsafePerformIO $ do@@ -283,6 +322,18 @@             modifyIORef queueRef ((CharacterData txt, loc):)         Nothing -> return () +    setXMLDeclarationHandler parser $ \pp cVer cEnc cSd -> do+        ver <- textFromCString cVer+        mEnc <- if cEnc == nullPtr+            then return Nothing+            else Just <$> textFromCString cEnc+        let sd = if cSd < 0+                then Nothing+                else Just $ if cSd /= 0 then True else False+        loc <- getParseLocation pp+        modifyIORef queueRef ((XMLDeclaration ver mEnc sd,loc):)+        return True+     setStartElementHandler parser $ \pp cName cAttrs -> do         name <- textFromCString cName         attrs <- forM cAttrs $ \(cAttrName,cAttrValue) -> do@@ -304,6 +355,29 @@         loc <- getParseLocation pp         modifyIORef queueRef ((CharacterData txt, loc):)         return True+        +    setStartCDataHandler parser $ \pp -> do+        loc <- getParseLocation pp+        modifyIORef queueRef ((StartCData, loc):)+        return True+        +    setEndCDataHandler parser $ \pp -> do+        loc <- getParseLocation pp+        modifyIORef queueRef ((EndCData, loc):)+        return True+        +    setProcessingInstructionHandler parser $ \pp cTarget cText -> do+        target <- textFromCString cTarget+        txt <- textFromCString cText+        loc <- getParseLocation pp+        modifyIORef queueRef ((ProcessingInstruction target txt, loc) :)+        return True+        +    setCommentHandler parser $ \pp cText -> do+        txt <- textFromCString cText+        loc <- getParseLocation pp+        modifyIORef queueRef ((Comment txt, loc) :)+        return True      let runParser inp = unsafeInterleaveIO $ do             rema <- withParser parser $ \pp -> case inp of@@ -348,7 +422,7 @@ -- situations where it's not expected during normal operation, depending on the -- design of your program. parseThrowing :: (GenericXMLString tag, GenericXMLString text) =>-                 ParseOptions tag text  -- ^ Parser options+                 ParseOptions tag text  -- ^ Parse options               -> L.ByteString            -- ^ input text (a lazy ByteString)               -> [SAXEvent tag text] parseThrowing opts bs = map freakOut $ parse opts bs
Text/XML/Expat/Tree.hs view
@@ -122,8 +122,7 @@   parseThrowing,   XMLParseException(..), -  -- * SAX-style parse-  SAXEvent(..),+  -- * Convert from SAX   saxToTree,    -- * Abstraction of string types@@ -198,8 +197,11 @@ type instance ListOf (NodeG c tag text) = c (NodeG c tag text)  instance (Show tag, Show text) => Show (NodeG [] tag text) where-    show (Element na at ch) = "Element "++show na++" "++show at++" "++show ch-    show (Text t) = "Text "++show t+    showsPrec d (Element na at ch) = showParen (d > 10) $+        ("Element "++) . showsPrec 11 na . (" "++) .+                         showsPrec 11 at . (" "++) .+                         showsPrec 11 ch+    showsPrec d (Text t) = showParen (d > 10) $ ("Text "++) . showsPrec 11 t  instance (Eq tag, Eq text) => Eq (NodeG [] tag text) where     Element na1 at1 ch1 == Element na2 at2 ch2 =@@ -238,7 +240,7 @@ type UNodes text = Nodes text text {-# DEPRECATED UNodes "use [UNode text] instead" #-} --- | Type alias for a single node with unqualified tag names where tag and+-- | Type alias for a node with unqualified tag names where tag and -- text are the same string type. type UNode text = Node text text @@ -248,7 +250,7 @@ {-# DEPRECATED QNodes "use [QNode text] instead" #-} type QNodes text = [Node (QName text) text] --- | Type alias for a single node where qualified names are used for tags+-- | Type alias for a node where qualified names are used for tags type QNode text = Node (QName text) text  -- | DEPRECATED: Use [NNode text] instead.@@ -257,7 +259,7 @@ {-# DEPRECATED NNodes "use [NNode text] instead" #-} type NNodes text = [Node (NName text) text] --- | Type alias for a single node where namespaced names are used for tags+-- | Type alias for a node where namespaced names are used for tags type NNode text = Node (NName text) text  instance (Functor c, List c) => NodeClass NodeG c where@@ -269,13 +271,20 @@          isText (Text _) = True     isText _        = False-    ++    isCData _ = False+    isProcessingInstruction _ = False+    isComment _ = False+     isNamed _  (Text _) = False     isNamed nm (Element nm' _ _) = nm == nm'      getName (Text _)             = mempty     getName (Element name _ _)   = name +    hasTarget _ _ = False+    getTarget _ = mempty+     getAttributes (Text _)            = []     getAttributes (Element _ attrs _) = attrs @@ -314,7 +323,7 @@  -- | Strictly parse XML to tree. Returns error message or valid parsed tree. parse' :: (GenericXMLString tag, GenericXMLString text) =>-          ParseOptions tag text  -- ^ Parser options+          ParseOptions tag text  -- ^ Parse options        -> ByteString              -- ^ Input text (a strict ByteString)        -> Either XMLParseError (Node tag text) parse' opts doc = unsafePerformIO $ runParse where@@ -383,10 +392,11 @@           -> (Node tag text, Maybe XMLParseError) saxToTree events =     let (nodes, mError, _) = ptl events-    in  (safeHead nodes, mError)+    in  (findRoot nodes, mError)   where-    safeHead (a:_) = a-    safeHead [] = Element (gxFromString "") [] []+    findRoot (elt@(Element _ _ _ ):_) = elt+    findRoot (_:nodes) = findRoot nodes+    findRoot [] = Element (gxFromString "") [] []     ptl (StartElement name attrs:rema) =         let (children, err1, rema') = ptl rema             elt = Element name attrs children@@ -397,6 +407,7 @@         let (out, err, rema') = ptl rema         in  (Text txt:out, err, rema')     ptl (FailDocument err:_) = ([], Just err, [])+    ptl (_:rema) = ptl rema  -- extended node types not supported in this tree type     ptl [] = ([], Nothing, [])  @@ -404,7 +415,7 @@ -- will force the entire parse.  Therefore, to ensure lazy operation, don't -- check the error status until you have processed the tree. parse :: (GenericXMLString tag, GenericXMLString text) =>-         ParseOptions tag text   -- ^ Parser options+         ParseOptions tag text    -- ^ Parse options       -> L.ByteString             -- ^ Input text (a lazy ByteString)       -> (Node tag text, Maybe XMLParseError) parse opts bs = saxToTree $ SAX.parse opts bs@@ -431,7 +442,7 @@ -- situations where it's not expected during normal operation, depending on the -- design of your program. parseThrowing :: (GenericXMLString tag, GenericXMLString text) =>-         ParseOptions tag text -- ^ Parser options+         ParseOptions tag text -- ^ Parse options       -> L.ByteString           -- ^ Input text (a lazy ByteString)       -> Node tag text parseThrowing opts bs = fst $ saxToTree $ SAX.parseThrowing opts bs
hexpat.cabal view
@@ -1,6 +1,6 @@ Cabal-Version: >= 1.6 Name: hexpat-Version: 0.18.3+Version: 0.19 Synopsis: XML parser/formatter based on expat Description:   This package provides a general purpose Haskell XML library using Expat to@@ -10,8 +10,10 @@   .   Basic usage: Parsing a tree (/Tree/), formatting a tree (/Format/).   Other features: Helpers for processing XML trees (/Proc/), trees annotated with-  XML source location (/Annotated/), XML cursors (/Cursor/), SAX-style parse (/SAX/),-  and access to the low-level interface in case speed is paramount (/Internal.IO/).+  XML source location (/Annotated/), extended XML trees with comments,+  processing instructions, etc (/Extended/), XML cursors (/Cursor/),+  SAX-style parse (/SAX/), and access to the low-level interface in case speed+  is paramount (/Internal.IO/).   .   The design goals are speed, speed, speed, interface simplicity and modularity.   .@@ -25,10 +27,11 @@   I\/O is a better approach: Take a look at the /hexpat-iteratee/ package.   .   /IO/ is filed under /Internal/ because it's low-level and most users won't want-  it.  The other /Internal/ modules are re-exported by /Annotated/ and /Tree/,+  it.  The other /Internal/ modules are re-exported by /Annotated/, /Tree/ and /Extended/,   so you won't need to import them directly.   .   Credits to Iavor Diatchki and the @xml@ (XML.Light) package for /Proc/ and /Cursor/.+  Thanks to the many contributors.   .   INSTALLATION: Unix install requires an OS package called something like @libexpat-dev@.   On MacOSX, expat comes with Apple's optional X11 package, or you can install it from source.@@ -43,7 +46,7 @@     that seemed only to happen only on ghc6.12.X; 0.15.1 Fix broken Annotated parse;     0.16 switch from mtl to transformers; 0.17 fix mapNodeContainer & rename some things.;     0.18 rename defaultEncoding to overrideEncoding. 0.18.3 formatG and indent were demanding list-    items more than once (inefficient in chunked processing).+    items more than once (inefficient in chunked processing); 0.19 add Extended.hs. Category: XML License: BSD3 License-File: LICENSE@@ -52,7 +55,8 @@   Doug Beardsley,   Gregory Collins,   Evan Martin (who started the project),-  Matthew Pocock [drdozer]+  Matthew Pocock [drdozer],+  Kevin Jardine Maintainer: http://blacksapphire.com/antispam/ Copyright:   (c) 2009 Doug Beardsley <mightybyte@gmail.com>,@@ -60,7 +64,8 @@   (c) 2009 Gregory Collins,   (c) 2008 Evan Martin <martine@danga.com>,   (c) 2009 Matthew Pocock <matthew.pocock@ncl.ac.uk>,-  (c) 2007-2009 Galois Inc.+  (c) 2007-2009 Galois Inc.,+  (c) 2010 Kevin Jardine Homepage: http://haskell.org/haskellwiki/Hexpat/ Extra-Source-Files:   test/hexpat-tests.cabal,@@ -69,7 +74,8 @@   test/suite/Text/XML/Expat/Proc/Tests.hs,   test/suite/Text/XML/Expat/UnitTests.hs,   test/suite/Text/XML/Expat/Tests.hs,-  test/suite/Text/XML/Expat/Cursor/Tests.hs+  test/suite/Text/XML/Expat/Cursor/Tests.hs,+  test/suite/Text/XML/Expat/ParseFormat.hs Build-Type: Simple Stability: beta source-repository head@@ -90,6 +96,7 @@   Exposed-Modules:     Text.XML.Expat.Annotated,     Text.XML.Expat.Cursor,+    Text.XML.Expat.Extended,     Text.XML.Expat.Format,     Text.XML.Expat.IO,     Text.XML.Expat.Namespaced,@@ -98,6 +105,7 @@     Text.XML.Expat.Qualified,     Text.XML.Expat.SAX,     Text.XML.Expat.Tree,+    Text.XML.Expat.Internal.DocumentClass,     Text.XML.Expat.Internal.IO,     Text.XML.Expat.Internal.Namespaced,     Text.XML.Expat.Internal.NodeClass,
test/suite/TestSuite.hs view
@@ -3,6 +3,7 @@ import qualified Text.XML.Expat.UnitTests import qualified Text.XML.Expat.Cursor.Tests import qualified Text.XML.Expat.Proc.Tests+import qualified Text.XML.Expat.ParseFormat  import Test.Framework (defaultMain, testGroup) @@ -14,4 +15,7 @@                             Text.XML.Expat.Proc.Tests.tests                 , testGroup "Text.XML.Expat.Cursor"                             Text.XML.Expat.Cursor.Tests.tests+                , testGroup "Text.XML.Expat.ParseFormat"+                            Text.XML.Expat.ParseFormat.tests                 ]+
+ test/suite/Text/XML/Expat/ParseFormat.hs view
@@ -0,0 +1,204 @@+{-# LANGUAGE OverloadedStrings, FlexibleContexts #-}+module Text.XML.Expat.ParseFormat where++import Text.XML.Expat.Extended+import Text.XML.Expat.Format+import qualified Text.XML.Expat.Tree as Tree+import qualified Text.XML.Expat.Annotated as Annotated++import Data.ByteString.Char8 (ByteString)+import qualified Data.ByteString.Char8 as B+import qualified Data.ByteString.Lazy.Char8 as L+import Data.List+import Data.Maybe+import Data.Monoid+import Data.Text (Text)+import Test.Framework.Providers.HUnit (hUnitTestToTests)+import Test.HUnit+++tests = hUnitTestToTests $+    TestList $ concatMap mkTests pfTests++pfTests :: [PFTest]+pfTests = [+        PFTest {+            pfName = "quotation",+            pfXML = "<test>\n" `mappend`+                    "<sample id=\"5\">This \"text with quotations\" should be escaped.</sample>\n" `mappend`+                    "<mytest>\n" `mappend`+                    "//<![CDATA[\n" `mappend`+                    "This \"text with quotations\" should not be escaped.\n" `mappend`+                    "Another line goes here.\n" `mappend`+                    "\n" `mappend`+                    "And more.\n" `mappend`+                    "//]]>\n" `mappend`+                    "</mytest>\n" `mappend`+                    "<?php somecode(); ?>\n" `mappend`+                    "<!-- this is a comment -->\n" `mappend`+                    "</test>",+            pfDoc = mkPlainDocument $+                Element "test" [] [+                    Text "\n",+                    Element "sample" [("id","5")] [Text "This \"text with quotations\" should be escaped."] (),+                    Text "\n",+                    Element "mytest" [] [+                        Text "\n",+                        Text "//",+                        CData "\nThis \"text with quotations\" should not be escaped.\nAnother line goes here.\n\nAnd more.\n//",+                        Text "\n"+                    ] (),+                    Text "\n",+                    Misc (ProcessingInstruction "php" "somecode(); "),+                    Text "\n",+                    Misc (Comment " this is a comment "),+                    Text "\n"+                ] (),+            pfOutXML = [(Extended,+                    "<test>\n" `mappend`+                    -- " gets translated into &quot; here but not inside CDATA.+                    "<sample id=\"5\">This &quot;text with quotations&quot; should be escaped.</sample>\n" `mappend`+                    "<mytest>\n" `mappend`+                    "//<![CDATA[\n" `mappend`+                    "This \"text with quotations\" should not be escaped.\n" `mappend`+                    "Another line goes here.\n" `mappend`+                    "\n" `mappend`+                    "And more.\n" `mappend`+                    "//]]>\n" `mappend`+                    "</mytest>\n" `mappend`+                    "<?php somecode(); ?>\n" `mappend`+                    "<!-- this is a comment -->\n" `mappend`+                    "</test>"                )],+            pfImpls = [Extended]+        },+        PFTest {+            pfName = "xmlDecl1",+            pfXML = "<?xml version=\"1.0\"?>\n<hello/>",+            pfDoc = Document (Just (XMLDeclaration "1.0" Nothing Nothing)) Nothing [] (Element "hello" [] [] ()),+            pfOutXML = [],+            pfImpls = [Extended]+        },+        PFTest {+            pfName = "xmlDecl2",+            pfXML = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n<hello/>",+            pfDoc = Document (Just (XMLDeclaration "1.0" (Just "ISO-8859-1") Nothing)) Nothing [] (Element "hello" [] [] ()),+            pfOutXML = [],+            pfImpls = [Extended]+        },+        PFTest {+            pfName = "xmlDecl3",+            pfXML = "<?xml version=\"1.0\" standalone=\"yes\"?>\n<hello/>",+            pfDoc = Document (Just (XMLDeclaration "1.0" Nothing (Just True))) Nothing [] (Element "hello" [] [] ()),+            pfOutXML = [],+            pfImpls = [Extended]+        },+        PFTest {+            pfName = "xmlDecl4",+            pfXML = "<?xml version=\"1.0\" standalone=\"no\"?>\n<hello/>",+            pfDoc = Document (Just (XMLDeclaration "1.0" Nothing (Just False))) Nothing [] (Element "hello" [] [] ()),+            pfOutXML = [],+            pfImpls = [Extended]+        },+        PFTest {+            pfName = "topLevelMiscs1",+            pfXML = "<?xml version=\"1.0\"?>\n<?process My code?>\n<!-- And a comment -->\n<hello/>",+            pfDoc = Document (Just (XMLDeclaration "1.0" Nothing Nothing)) Nothing [+                    ProcessingInstruction "process" "My code",+                    Comment " And a comment "+                ] (Element "hello" [] [] ()),+            pfOutXML = [],+            pfImpls = [Extended]+        },+        PFTest {+            pfName = "topLevelMiscs2",+            -- Test that we can read processing instructions and comments from after the root element.+            pfXML = "<?xml version=\"1.0\"?>\n<?process My code?>\n<!-- And a comment -->\n" `mappend`+                    "<hello/>\n<!-- Also afterwards -->\n<?php something();?>",+            pfDoc = Document (Just (XMLDeclaration "1.0" Nothing Nothing)) Nothing [+                    ProcessingInstruction "process" "My code",+                    Comment " And a comment ",+                    Comment " Also afterwards ",+                    ProcessingInstruction "php" "something();"+                ] (Element "hello" [] [] ()),+            -- In the output they appear *before* the root element, however.+            pfOutXML = [(Extended,+                    "<?xml version=\"1.0\"?>\n<?process My code?>\n<!-- And a comment -->\n" `mappend`+                    "<!-- Also afterwards -->\n<?php something();?>\n<hello/>"+                )],+            pfImpls = [Extended]+        },+        PFTest {+            pfName = "basic",+            pfXML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" `mappend`+                    "<second><test><test1 type=\"expression\">Cat &amp; mouse</test1>In between" `mappend`+                    "<test2 type=\"communication\" language=\"Rhyming slang\">Dog &amp; bone</test2>" `mappend`+                    "</test><test>Rose &amp; Crown</test></second>",+            pfDoc = Document (Just (XMLDeclaration "1.0" (Just "UTF-8") Nothing)) Nothing [] (+                 Element "second" [] [Element "test" [] [Element "test1" [("type","expression")]+                     [Text "Cat ",Text "&",Text " mouse"] (),Text "In between",+                      Element "test2" [("type","communication"),("language","Rhyming slang")]+                     [Text "Dog &",Text " bone"] ()] (),Element "test" []+                     [Text "Ro", Text "se & Crown"] ()] ()),  -- Test text normalization+            pfOutXML = [],+            pfImpls = [Tree, Annotated, Extended]+        }+    ]++-- | Recursively append all adjacent Text nodes.+normalizeText :: (NodeClass n [], Monoid text) => n [] tag text -> n [] tag text+normalizeText = modifyChildren combine+  where+    combine (t1:t2:ns) | isText t1 && isText t2 = combine ((mkText $ getText t1 `mappend` getText t2):ns)+    combine (e:ns) | isElement e = normalizeText e : combine ns+    combine (n:ns) = n:combine ns+    combine [] = []++mkTests :: PFTest -> [Test]+mkTests pf = flip concatMap (pfImpls pf) $ \impl ->+    case impl of+        Tree -> [+                TestLabel (pfName pf ++ "-tree") $ TestCase $ do+                    case Tree.parse' defaultParseOptions (pfXML pf) of+                        Left err -> assertFailure $ "parse failed: "++show err+                        Right root0 -> do+                            let root = normalizeText root0+                                sbDoc = normalizeText $ fromElement (getRoot $ pfDoc pf)+                            assertEqual "parse match" sbDoc (root :: Tree.UNode Text)+                            let sb = fromMaybe (pfXML pf) (impl `lookup` pfOutXML pf)+                                bs = formatTree' root+                            assertEqual "format match" sb bs+            ]+        Annotated -> [+                TestLabel (pfName pf ++ "-tree") $ TestCase $ do+                    case Annotated.parse' defaultParseOptions (pfXML pf) of+                        Left err -> assertFailure $ "parse failed: "++show err+                        Right root0 -> do+                            let root = normalizeText $ Annotated.mapAnnotation (const ()) root0+                                sbDoc = normalizeText $ fromElement (getRoot $ pfDoc pf)+                            assertEqual "parse match" sbDoc (root :: Annotated.UNode () Text)+                            let sb = fromMaybe (pfXML pf) (impl `lookup` pfOutXML pf)+                                bs = formatTree' root+                            assertEqual "format match" sb bs+            ]+        Extended  -> [+                TestLabel (pfName pf ++ "-extended") $ TestCase $ do+                    case parse' defaultParseOptions (pfXML pf) of+                        Left err -> assertFailure $ "parse failed: "++show err+                        Right doc0 -> do+                            let doc = modifyRoot normalizeText $ mapDocumentAnnotation (const ()) doc0+                            assertEqual "parse match" (modifyRoot normalizeText $ pfDoc pf) doc+                    let sb = fromMaybe (pfXML pf) (impl `lookup` pfOutXML pf)+                        bs = formatDocument' (pfDoc pf)+                    assertEqual "format match" sb bs+            ]++data Impl = Tree | Annotated | Extended deriving (Eq, Ord, Show)++data PFTest = PFTest {+        pfName   :: String,+        pfXML    :: ByteString,+        pfDoc    :: UDocument () Text,+        pfOutXML :: [(Impl, ByteString)],  -- ^ Output XML where it differs from the input XML+        pfImpls  :: [Impl]+    }+
test/suite/Text/XML/Expat/UnitTests.hs view
@@ -225,6 +225,8 @@     "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<test>Cat &amp; mouse</test>"   ] +data ParseFormatTest = ParseFormatTest {+    }  tests = hUnitTestToTests $     TestList [@@ -232,32 +234,32 @@             Tree.parse' :: ParseOptions String String                         -> B.ByteString                         -> Either XMLParseError (Node String String),-            formatTree),+            format),         t' ("ByteString",             Tree.parse' :: ParseOptions B.ByteString B.ByteString                         -> B.ByteString                         -> Either XMLParseError (Node B.ByteString B.ByteString),-            formatTree),+            format),         t' ("Text",             Tree.parse' :: ParseOptions T.Text T.Text                         -> B.ByteString                         -> Either XMLParseError (Node T.Text T.Text),-            formatTree),+            format),         t ("String/Lazy",             eitherify $ Tree.parse :: ParseOptions String String                                    -> L.ByteString                                    -> Either XMLParseError (Node String String),-            formatTree),+            format),         t ("ByteString/Lazy",             eitherify $ Tree.parse :: ParseOptions B.ByteString B.ByteString                                    -> L.ByteString                                    -> Either XMLParseError (Node B.ByteString B.ByteString),-            formatTree),+            format),         t ("Text/Lazy",             eitherify $ Tree.parse :: ParseOptions T.Text T.Text                                    -> L.ByteString                                    -> Either XMLParseError (Node T.Text T.Text),-            formatTree),+            format),         TestLabel "error1" $ TestCase $ test_error1,         TestLabel "error2" $ TestCase $ test_error2,         TestLabel "error3" $ TestCase $ test_error3,