git.iko.soy/typeable/dom-parser
on master help sign in
Upd: add ElemMatcher
7fb1685Aleksey Uimanov
src/Text/XML/DOM/Parser/Combinators.hs+20−19
@@ -31,15 +31,15 @@ import Text.XML.DOM.Parser.Types
31import Text.XML.Lens
31import Text.XML.Lens
32
32
33
33
34−-- | Generic function to traverse arbitrary inner cursors.
34+-- | Generic function to traverse arbitrary inner elements.
35traverseElems
35traverseElems
36 :: (Monad m, Foldable g, Traversable f)
36 :: (Monad m, Foldable g, Traversable f)
37 => ([Element] -> DomParserT g m (f (DomPath, Element)))
37 => ([Element] -> DomParserT g m (f (DomPath, Element)))
38 -- ^ Takes list of current elements and returns container with
38 -- ^ Takes list of current elements and returns container with
39− -- pairs of subpath (relatively to current element) and element
39+ -- pairs of subpath (relatively to current elements) and element
40 -- to run parser in
40 -- to run parser in
41 -> DomParserT Identity m a
41 -> DomParserT Identity m a
42− -- ^ Parser will be runned for each element found in traversable
42+ -- ^ Parser to run for each element found in traversable 'f'
43 -> DomParserT g m (f a)
43 -> DomParserT g m (f a)
44traverseElems trav parser = do
44traverseElems trav parser = do
45 pd <- ask
45 pd <- ask
@@ -49,15 +49,15 @@ traverseElems trav parser = do
49 newpd = ParserData
49 newpd = ParserData
50 { _pdElements = Identity e
50 { _pdElements = Identity e
51 , _pdPath = pd ^. pdPath <> subpath }
51 , _pdPath = pd ^. pdPath <> subpath }
52− -- type of reader has been changed, local won't work here
52+ lift $ runReaderT parser newpd
53− magnify (to $ const newpd) parser
54
53
55−-- | Takes function filtering
54+-- | Traverses elements located in same path using filtering function
56inFilteredTrav
55inFilteredTrav
57 :: (Monad m, Foldable g, Buildable f)
56 :: (Monad m, Foldable g, Buildable f)
58 => ([Element] -> (DomPath, [Element]))
57 => ([Element] -> (DomPath, [Element]))
59− -- ^ Function returning some filtered elements with path suffixes which will
58+ -- ^ Takes list of current elements and returns some descendants
60− -- be appended to parser's state. The argument is a list of current elements.
59+ -- subset and path this descendants located at. Path is should be
60+ -- same for all descendants and required for error message
61 -> DomParserT Identity m a
61 -> DomParserT Identity m a
62 -> DomParserT g m (f a)
62 -> DomParserT g m (f a)
63inFilteredTrav deeper = traverseElems trav
63inFilteredTrav deeper = traverseElems trav
@@ -70,38 +70,39 @@ inFilteredTrav deeper = traverseElems trav
70
70
71inElemTrav
71inElemTrav
72 :: (Monad m, Foldable g, Buildable f)
72 :: (Monad m, Foldable g, Buildable f)
73− => NameMatcher -- ^ Name of tag to traverse in
73+ => ElemMatcher -- ^ Name of tag to traverse in
74 -> DomParserT Identity m a
74 -> DomParserT Identity m a
75 -> DomParserT g m (f a)
75 -> DomParserT g m (f a)
76inElemTrav n = inFilteredTrav deeper
76inElemTrav n = inFilteredTrav deeper
77 where
77 where
78+ elemsFold = folded . nodes . folded . _Element . elMatch n
78− deeper = (DomPath [_nmShow n],) . toListOf (folded . nodes . folded . _Element . elMatchName n)
79+ deeper = (DomPath [_emShow n],) . toListOf elemsFold
79
80
80-- | Runs parser inside first children element with given name
81-- | Runs parser inside first children element with given name
81inElem
82inElem
82 :: (Monad m, Foldable g)
83 :: (Monad m, Foldable g)
83− => NameMatcher
84+ => ElemMatcher
84 -> DomParserT Identity m a
85 -> DomParserT Identity m a
85 -> DomParserT g m a
86 -> DomParserT g m a
86inElem n = fmap runIdentity . inElemTrav n
87inElem n = fmap runIdentity . inElemTrav n
87
88
88inElemAll
89inElemAll
89 :: (Monad m, Foldable g)
90 :: (Monad m, Foldable g)
90− => NameMatcher
91+ => ElemMatcher
91 -> DomParserT Identity m a
92 -> DomParserT Identity m a
92 -> DomParserT g m [a]
93 -> DomParserT g m [a]
93inElemAll = inElemTrav
94inElemAll = inElemTrav
94
95
95inElemMay
96inElemMay
96 :: (Monad m, Foldable g)
97 :: (Monad m, Foldable g)
97− => NameMatcher
98+ => ElemMatcher
98 -> DomParserT Identity m a
99 -> DomParserT Identity m a
99 -> DomParserT g m (Maybe a)
100 -> DomParserT g m (Maybe a)
100inElemMay = inElemTrav
101inElemMay = inElemTrav
101
102
102inElemNe
103inElemNe
103 :: (Monad m, Foldable g)
104 :: (Monad m, Foldable g)
104− => NameMatcher
105+ => ElemMatcher
105 -> DomParserT Identity m a
106 -> DomParserT Identity m a
106 -> DomParserT g m (NonEmpty a)
107 -> DomParserT g m (NonEmpty a)
107inElemNe = inElemTrav
108inElemNe = inElemTrav
@@ -133,21 +134,21 @@ in given path.
133divePath
134divePath
134 :: forall m g a
135 :: forall m g a
135 . (Monad m, Foldable g)
136 . (Monad m, Foldable g)
136− => [NameMatcher]
137+ => [ElemMatcher]
137 -> DomParserT [] m a
138 -> DomParserT [] m a
138 -> DomParserT g m a
139 -> DomParserT g m a
139divePath path = magnify $ to modElems
140divePath path = magnify $ to modElems
140 where
141 where
141 modElems
142 modElems
142 = over pdElements (toListOf $ folded . diver)
143 = over pdElements (toListOf $ folded . diver)
143− . over pdPath (<> DomPath (L.map _nmShow path))
144+ . over pdPath (<> DomPath (L.map _emShow path))
144 diver :: Fold Element Element
145 diver :: Fold Element Element
145 diver = F.foldr (.) id $ L.map toDive path
146 diver = F.foldr (.) id $ L.map toDive path
146− toDive n = nodes . folded . _Element . elMatchName n
147+ toDive n = nodes . folded . _Element . elMatch n
147
148
148diveElem
149diveElem
149 :: (Monad m, Foldable g)
150 :: (Monad m, Foldable g)
150− => NameMatcher
151+ => ElemMatcher
151 -> DomParserT [] m a
152 -> DomParserT [] m a
152 -> DomParserT g m a
153 -> DomParserT g m a
153diveElem p = divePath [p]
154diveElem p = divePath [p]
@@ -188,6 +189,6 @@ ignoreBlank = ignoreElem test
188 let
189 let
189 elems = e ^.. nodes . folded . _Element
190 elems = e ^.. nodes . folded . _Element
190 cont = mconcat $ e ^.. nodes . folded . _Content
191 cont = mconcat $ e ^.. nodes . folded . _Content
191− in if | not $ L.null elems -> False
192+ in if | not $ L.null elems -> False
192 | T.null $ T.strip cont -> True
193 | T.null $ T.strip cont -> True
193 | otherwise -> False
194 | otherwise -> False
src/Text/XML/DOM/Parser/Types.hs+35−6
@@ -6,7 +6,12 @@ module Text.XML.DOM.Parser.Types
6 , matchName
6 , matchName
7 , matchLocalName
7 , matchLocalName
8 , matchCILocalName
8 , matchCILocalName
9− , elMatchName
9+ -- * Element matching
10+ , ElemMatcher(..)
11+ , emMatch
12+ , emShow
13+ , matchElemName
14+ , elMatch
10 -- * Parser internals
15 -- * Parser internals
11 , DomPath(..)
16 , DomPath(..)
12 , ParserError(..)
17 , ParserError(..)
@@ -37,6 +42,30 @@ import GHC.Generics (Generic)
37import Text.XML
42import Text.XML
38import Text.XML.Lens
43import Text.XML.Lens
39
44
45+-- | Arbitrary element matcher
46+data ElemMatcher = ElemMatcher
47+ { _emMatch :: Element -> Bool
48+ , _emShow :: Text
49+ }
50+
51+makeLenses ''ElemMatcher
52+
53+instance IsString ElemMatcher where
54+ fromString = matchElemName . fromString
55+
56+instance Show ElemMatcher where
57+ show = T.unpack . _emShow
58+
59+-- | Match element by name
60+matchElemName :: NameMatcher -> ElemMatcher
61+matchElemName (NameMatcher matchName showName) = ElemMatcher
62+ { _emMatch = views name matchName
63+ , _emShow = showName
64+ }
65+
66+elMatch :: ElemMatcher -> Traversal' Element Element
67+elMatch (ElemMatcher match _) = filtered match
68+
40-- | Arbitrary name matcher. Match name any way you want, but
69-- | Arbitrary name matcher. Match name any way you want, but
41-- considered to be used as comparator with some name with some rules
70-- considered to be used as comparator with some name with some rules
42data NameMatcher = NameMatcher
71data NameMatcher = NameMatcher
@@ -81,9 +110,8 @@ matchName n = NameMatcher
81 , _nmShow = nameLocalName n
110 , _nmShow = nameLocalName n
82 }
111 }
83
112
84−elMatchName :: NameMatcher -> Traversal' Element Element
113+-- | Path some element should be found at. Path starts from the root
85−elMatchName nm = filtered (views name $ nm ^. nmMatch)
114+-- element of the document. Errors are much more usefull with path.
86−
87newtype DomPath = DomPath
115newtype DomPath = DomPath
88 { unDomPath :: [Text]
116 { unDomPath :: [Text]
89 } deriving (Eq, Ord, Show, Monoid)
117 } deriving (Eq, Ord, Show, Monoid)
@@ -139,8 +167,9 @@ makePrisms ''ParserErrors
139instance Exception ParserErrors
167instance Exception ParserErrors
140
168
141
169
170+{- | Parser scope.
171+
142−{- | Parser scope parser runs in. Functor argument is usually @Identity@ or
172+Functor argument is usually @Identity@ or @[]@.
143−@[]@.
144
173
145If functor is @Identity@ then parser expects exactly ONE current element. This
174If functor is @Identity@ then parser expects exactly ONE current element. This
146is common behavior for content parsers, or parsers expecting strict XML
175is common behavior for content parsers, or parsers expecting strict XML