-- | XML as a tree of XML tags. -- -- The module provides an `XmlTree` data type, which can be used to represent -- a parsed XML file. The `XmlTree` structure can be generated lazily by using -- the `parseTree` (or `parseForest`) function on any string-like input -- supported by the tagsoup library. -- -- Note, that the parsing functions do not validate correctness of the input -- XML data. module Text.XML.PolySoup.XmlTree ( -- * XML Tree XmlTree , XmlForest -- ** Parsing , parseTree , parseForest -- ** Rendering , renderTree , renderForest ) where import Data.Tree import qualified Text.HTML.TagSoup as S import Text.ParserCombinators.Poly.Lazy --------------------------------------------------------------------- -- Types --------------------------------------------------------------------- -- | A lazy XML parser. type XmlParser s a = Parser (S.Tag s) a -- | A parsed XML tree. Closing tags are not preserved. type XmlTree s = Tree (S.Tag s) -- data XmlTree s = Node -- { rootLabel :: S.Tag s -- , subForest :: Forest a } -- deriving (Show, Eq, Ord) -- | A parsed XML forest. Closing tags are not preserved. type XmlForest s = [XmlTree s] --------------------------------------------------------------------- -- Parsing XML --------------------------------------------------------------------- -- | Parse XML tree from a list of tags. parseTree :: Eq s => [S.Tag s] -> XmlTree s parseTree = fst . runParser xmlTreeP -- | Parse XML forest from a list of tags. Note, that if the XML file -- has additional headers, the `parseForest` function has to be used to -- parse it correctly. parseForest :: Eq s => [S.Tag s] -> XmlForest s parseForest = fst . runParser (many xmlTreeP) -- | A parser from tags to an XML tree. xmlTreeP :: Eq s => XmlParser s (XmlTree s) xmlTreeP = nodeP <|> leafP -- | Internal node parser. nodeP :: Eq s => XmlParser s (XmlTree s) nodeP = do x <- satisfy S.isTagOpen x `seq` Node x <$> many xmlTreeP <* satisfy (S.isTagCloseName $ tagName x) where tagName (S.TagOpen x _) = x tagName _ = error "tagName: not an open tag" -- | Leaf node parser. leafP :: XmlParser s (XmlTree s) leafP = fmap (flip Node []) (satisfy $ \x -> not (S.isTagOpen x || S.isTagClose x)) --------------------------------------------------------------------- -- Rendering XML --------------------------------------------------------------------- -- | Render XML tree tags. renderTree :: XmlTree s -> [S.Tag s] renderTree (Node v xs) = if S.isTagOpen v then v : renderForest xs ++ [endFrom v] else [v] -- | Render XML forest tags. renderForest :: XmlForest s -> [S.Tag s] renderForest = concatMap renderTree -- | Make closing tag from the opening tag. endFrom :: S.Tag s -> S.Tag s endFrom (S.TagOpen x _) = S.TagClose x endFrom _ = error "endFrom: not an opening tag"