on master help sign in
17 files changed+278−53
since splitsnapshots
unifiedsplit
.ghci+1−1 .github/workflows/ci.yml+33 .travis.yml−13 CHANGES.txt+2 LICENSE+190−3 PULL_REQUEST_TEMPLATE.md+5 README.md+4−2 appveyor.yml−5 cbits/hexml.c+2−2 gen/Lib.hs+3−3 gen/Main.hs+3−3 gen/Parser.hs+4−2 gen/test.c+13−3 ghci.bat+1 hexml.cabal+4−3 src/Main.hs+4−4 src/Text/XML/Hexml.hs+9−9
.ghci+1−1
@@ -1,4 +1,4 @@
1−:set -fwarn-unused-binds -fwarn-unused-imports -fwarn-orphans
1+:set -Wunused-binds -Wunused-imports -Worphans
22:set -isrc
33:load Main
44
.github/workflows/ci.ymlnew file+33
@@ -0,0 +1,33 @@
1+name: ci
2+on:
3+ push:
4+ pull_request:
5+ schedule:
6+ - cron: '0 3 * * 6' # 3am Saturday
7+jobs:
8+ test:
9+ runs-on: ${{ matrix.os }}
10+
11+ strategy:
12+ fail-fast: false
13+ matrix:
14+ os: [ubuntu-latest]
15+ ghc: ['9.6', '9.4', '9.2', '9.0', '8.10', '8.8']
16+ include:
17+ - os: windows-latest
18+ - os: macOS-latest
19+
20+ steps:
21+ - run: git config --global core.autocrlf false
22+ - uses: actions/checkout@v2
23+ - uses: haskell/actions/setup@v2
24+ id: setup-haskell
25+ with:
26+ ghc-version: ${{ matrix.ghc }}
27+ - run: cabal v2-freeze --enable-tests
28+ - uses: actions/cache@v2
29+ with:
30+ path: ${{ steps.setup-haskell.outputs.cabal-store }}
31+ key: ${{ runner.os }}-${{ matrix.ghc }}-${{ hashFiles('cabal.project.freeze') }}
32+ restore-keys: ${{ runner.os }}-${{ matrix.ghc }}-
33+ - uses: ndmitchell/neil@master
.travis.ymldeleted−13
@@ -1,13 +0,0 @@
1−sudo: required
2−env:
3−- GHCVER=7.6.3
4−- GHCVER=7.8.4
5−- GHCVER=7.10.3
6−- GHCVER=8.0.2
7−- GHCVER=8.2.1
8−- GHCVER=8.4.1
9−- GHCVER=head
10−
11−script:
12−- curl -sL https://raw.github.com/ndmitchell/neil/master/travis.sh | sh
13−- sh afl.sh
CHANGES.txt+2
@@ -1,5 +1,7 @@
11Changelog for Hexml
22
3+0.3.4, released 2018-09-10
4+ #15, licensed under BSD-3-Clause OR Apache-2.0
350.3.3, released 2017-10-27
46 #13, allow the . character in tags and attributes
570.3.2, released 2017-05-22
LICENSE+190−3
@@ -1,9 +1,20 @@
1−Copyright Neil Mitchell 2018.
1+Copyright Neil Mitchell 2016-2023.
2+
3+Licensed under either of:
4+
5+* BSD-3-Clause license (https://opensource.org/licenses/BSD-3-Clause)
6+* Apache License, version 2.0 (https://opensource.org/licenses/Apache-2.0)
7+
8+As a user, you may use this code under either license, at your option.
9+
10+
11+---------------------------------------------------------------------
12+BSD 3-Clause License
213All rights reserved.
314
415Redistribution and use in source and binary forms, with or without
5−modification, are permitted provided that the following conditions are
6−met:
16+modification, are permitted provided that the following conditions
17+are met:
718
819 * Redistributions of source code must retain the above copyright
920 notice, this list of conditions and the following disclaimer.
@@ -28,3 +39,179 @@ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2839THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2940(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
3041OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42+
43+
44+---------------------------------------------------------------------
45+Apache License
46+Version 2.0, January 2004
47+<http://www.apache.org/licenses/>
48+
49+Terms and Conditions for use, reproduction, and distribution
50+1. Definitions
51+"License" shall mean the terms and conditions for use, reproduction,
52+and distribution as defined by Sections 1 through 9 of this document.
53+
54+"Licensor" shall mean the copyright owner or entity authorized by the
55+copyright owner that is granting the License.
56+
57+"Legal Entity" shall mean the union of the acting entity and all
58+other entities that control, are controlled by, or are under common
59+control with that entity. For the purposes of this definition,
60+"control" means (i) the power, direct or indirect, to cause the
61+direction or management of such entity, whether by contract or
62+otherwise, or (ii) ownership of fifty percent (50%) or more of the
63+outstanding shares, or (iii) beneficial ownership of such entity.
64+
65+"You" (or "Your") shall mean an individual or Legal Entity exercising
66+permissions granted by this License.
67+
68+"Source" form shall mean the preferred form for making modifications,
69+including but not limited to software source code, documentation
70+source, and configuration files.
71+
72+"Object" form shall mean any form resulting from mechanical
73+transformation or translation of a Source form, including but not
74+limited to compiled object code, generated documentation, and
75+conversions to other media types.
76+
77+"Work" shall mean the work of authorship, whether in Source or Object
78+form, made available under the License, as indicated by a copyright
79+notice that is included in or attached to the work).
80+
81+"Derivative Works" shall mean any work, whether in Source or Object
82+form, that is based on (or derived from) the Work and for which the
83+editorial revisions, annotations, elaborations, or other
84+modifications represent, as a whole, an original work of authorship.
85+For the purposes of this License, Derivative Works shall not include
86+works that remain separable from, or merely link (or bind by name) to
87+the interfaces of, the Work and Derivative Works thereof.
88+
89+"Contribution" shall mean any work of authorship, including the
90+original version of the Work and any modifications or additions to
91+that Work or Derivative Works thereof, that is intentionally
92+submitted to Licensor for inclusion in the Work by the copyright
93+owner or by an individual or Legal Entity authorized to submit on
94+behalf of the copyright owner. For the purposes of this definition,
95+"submitted" means any form of electronic, verbal, or written
96+communication sent to the Licensor or its representatives, including
97+but not limited to communication on electronic mailing lists, source
98+code control systems, and issue tracking systems that are managed by,
99+or on behalf of, the Licensor for the purpose of discussing and
100+improving the Work, but excluding communication that is conspicuously
101+marked or otherwise designated in writing by the copyright owner as
102+"Not a Contribution."
103+
104+"Contributor" shall mean Licensor and any individual or Legal Entity
105+on behalf of whom a Contribution has been received by Licensor and
106+subsequently incorporated within the Work.
107+
108+2. Grant of Copyright License
109+Subject to the terms and conditions of this License, each Contributor
110+hereby grants to You a perpetual, worldwide, non-exclusive,
111+no-charge, royalty-free, irrevocable copyright license to reproduce,
112+prepare Derivative Works of, publicly display, publicly perform,
113+sublicense, and distribute the Work and such Derivative Works in
114+Source or Object form.
115+
116+3. Grant of Patent License
117+Subject to the terms and conditions of this License, each Contributor
118+hereby grants to You a perpetual, worldwide, non-exclusive,
119+no-charge, royalty-free, irrevocable (except as stated in this
120+section) patent license to make, have made, use, offer to sell, sell,
121+import, and otherwise transfer the Work, where such license applies
122+only to those patent claims licensable by such Contributor that are
123+necessarily infringed by their Contribution(s) alone or by
124+combination of their Contribution(s) with the Work to which such
125+Contribution(s) was submitted. If You institute patent litigation
126+against any entity (including a cross-claim or counterclaim in a
127+lawsuit) alleging that the Work or a Contribution incorporated within
128+the Work constitutes direct or contributory patent infringement, then
129+any patent licenses granted to You under this License for that Work
130+shall terminate as of the date such litigation is filed.
131+
132+4. Redistribution
133+You may reproduce and distribute copies of the Work or Derivative
134+Works thereof in any medium, with or without modifications, and in
135+Source or Object form, provided that You meet the following
136+conditions:
137+
138+(a) You must give any other recipients of the Work or Derivative
139+Works a copy of this License; and
140+(b) You must cause any modified files to carry prominent notices
141+stating that You changed the files; and
142+(c) You must retain, in the Source form of any Derivative Works that
143+You distribute, all copyright, patent, trademark, and attribution
144+notices from the Source form of the Work, excluding those notices
145+that do not pertain to any part of the Derivative Works; and
146+(d) If the Work includes a "NOTICE" text file as part of its
147+distribution, then any Derivative Works that You distribute must
148+include a readable copy of the attribution notices contained within
149+such NOTICE file, excluding those notices that do not pertain to any
150+part of the Derivative Works, in at least one of the following
151+places: within a NOTICE text file distributed as part of the
152+Derivative Works; within the Source form or documentation, if
153+provided along with the Derivative Works; or, within a display
154+generated by the Derivative Works, if and wherever such third-party
155+notices normally appear. The contents of the NOTICE file are for
156+informational purposes only and do not modify the License. You may
157+add Your own attribution notices within Derivative Works that You
158+distribute, alongside or as an addendum to the NOTICE text from the
159+Work, provided that such additional attribution notices cannot be
160+construed as modifying the License.
161+You may add Your own copyright statement to Your modifications and
162+may provide additional or different license terms and conditions for
163+use, reproduction, or distribution of Your modifications, or for any
164+such Derivative Works as a whole, provided Your use, reproduction,
165+and distribution of the Work otherwise complies with the conditions
166+stated in this License.
167+
168+5. Submission of Contributions
169+Unless You explicitly state otherwise, any Contribution intentionally
170+submitted for inclusion in the Work by You to the Licensor shall be
171+under the terms and conditions of this License, without any
172+additional terms or conditions. Notwithstanding the above, nothing
173+herein shall supersede or modify the terms of any separate license
174+agreement you may have executed with Licensor regarding such
175+Contributions.
176+
177+6. Trademarks
178+This License does not grant permission to use the trade names,
179+trademarks, service marks, or product names of the Licensor, except
180+as required for reasonable and customary use in describing the origin
181+of the Work and reproducing the content of the NOTICE file.
182+
183+7. Disclaimer of Warranty
184+Unless required by applicable law or agreed to in writing, Licensor
185+provides the Work (and each Contributor provides its Contributions)
186+on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
187+either express or implied, including, without limitation, any
188+warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY,
189+or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for
190+determining the appropriateness of using or redistributing the Work
191+and assume any risks associated with Your exercise of permissions
192+under this License.
193+
194+8. Limitation of Liability
195+In no event and under no legal theory, whether in tort (including
196+negligence), contract, or otherwise, unless required by applicable
197+law (such as deliberate and grossly negligent acts) or agreed to in
198+writing, shall any Contributor be liable to You for damages,
199+including any direct, indirect, special, incidental, or consequential
200+damages of any character arising as a result of this License or out
201+of the use or inability to use the Work (including but not limited to
202+damages for loss of goodwill, work stoppage, computer failure or
203+malfunction, or any and all other commercial damages or losses), even
204+if such Contributor has been advised of the possibility of such
205+damages.
206+
207+9. Accepting Warranty or Additional Liability
208+While redistributing the Work or Derivative Works thereof, You may
209+choose to offer, and charge a fee for, acceptance of support,
210+warranty, indemnity, or other liability obligations and/or rights
211+consistent with this License. However, in accepting such obligations,
212+You may act only on Your own behalf and on Your sole responsibility,
213+not on behalf of any other Contributor, and only if You agree to
214+indemnify, defend, and hold each Contributor harmless for any
215+liability incurred by, or claims asserted against, such Contributor
216+by reason of your accepting any such warranty or additional
217+liability.
PULL_REQUEST_TEMPLATE.mdnew file+5
@@ -0,0 +1,5 @@
1+Thanks for the pull request!
2+
3+By raising this pull request you confirm you are licensing your contribution under all licenses that apply to this project (see LICENSE) and that you have no patents covering your contribution.
4+
5+If you care, my PR preferences are at https://github.com/ndmitchell/neil#contributions, but they're all guidelines, and I'm not too fussy - you don't have to read them.
README.md+4−2
@@ -1,4 +1,4 @@
1−# Hexml [![Hackage version](https://img.shields.io/hackage/v/hexml.svg?label=Hackage)](https://hackage.haskell.org/package/hexml) [![Stackage version](https://www.stackage.org/package/hexml/badge/lts?label=Stackage)](https://www.stackage.org/package/hexml) [![Linux Build Status](https://img.shields.io/travis/ndmitchell/hexml.svg?label=Linux%20build)](https://travis-ci.org/ndmitchell/hexml) [![Windows Build Status](https://img.shields.io/appveyor/ci/ndmitchell/hexml.svg?label=Windows%20build)](https://ci.appveyor.com/project/ndmitchell/hexml)
1+# Hexml [![Hackage version](https://img.shields.io/hackage/v/hexml.svg?label=Hackage)](https://hackage.haskell.org/package/hexml) [![Stackage version](https://www.stackage.org/package/hexml/badge/nightly?label=Stackage)](https://www.stackage.org/package/hexml) [![Build status](https://img.shields.io/github/actions/workflow/status/ndmitchell/hexml/ci.yml?branch=master)](https://github.com/ndmitchell/hexml/actions)
22
33An XML DOM-style parser, that only parses a subset of XML, but is designed to be fast. In particular:
44
@@ -8,8 +8,10 @@ An XML DOM-style parser, that only parses a subset of XML, but is designed to be
88
99The name "hexml" is a combination of "Hex" (a curse) and "XML". The "X" should not be capitalised because the parser is more curse and less XML.
1010
11−Hexml may be suitable if you want to quickly parse XML, from known sources, and a full XML parser has been shown to be a bottleneck. As an alternative to hexml, which supports things like entities but is still pretty fast, see [Pugixml](http://pugixml.org/) (with a [Haskell binding](https://hackage.haskell.org/package/pugixml)).
11+Hexml may be suitable if you want to quickly parse XML, from known sources, and a full XML parser has been shown to be a bottleneck. As an alternative to hexml, which supports things like entities but is still pretty fast, see [Pugixml](http://pugixml.org/) (with a [Haskell binding](https://hackage.haskell.org/package/pugixml) - but be aware the Haskell binding [can segfault](https://github.com/philopon/pugixml-hs/issues/5)).
1212
1313Hexml is tested with [AFL](http://lcamtuf.coredump.cx/afl/).
1414
1515If you want lenses for Hexml, see [hexml-lens](http://hackage.haskell.org/package/hexml-lens).
16+
17+The optimisation work around Hexml spawned [Xeno](http://hackage.haskell.org/package/xeno), a Haskell-only alternative to Hexml. There is a [talk](https://ndmitchell.com/#hexml_12_oct_2017) covering the performance tricks of Hexml and Xeno.
appveyor.ymldeleted−5
@@ -1,5 +0,0 @@
1−build: off
2−cache: "c:\\sr -> appveyor.yml"
3−
4−test_script:
5−- ps: Invoke-Expression (Invoke-WebRequest 'https://raw.githubusercontent.com/ndmitchell/neil/master/appveyor.ps1')
cbits/hexml.c+2−2
@@ -40,7 +40,7 @@ typedef struct
4040 // have a cursor at the end, which is stack scoped, so children write out, then I do
4141 // when you commit, you copy over from end to front
4242
43− // nodes
43+ // nodes
4444 int size;
4545 int used_front; // front entries, stored for good
4646 int used_back; // back entries, stack based, copied into front
@@ -539,7 +539,7 @@ document* hexml_document_parse(const char* s, int slen)
539539 d->nodes.nodes[0].outer = start_length(0, slen);
540540 d->nodes.nodes[0].inner = start_length(0, slen);
541541 d->nodes.nodes[0].attrs = start_length(0, 0);
542−
542+
543543 // Introduce an intermediate result, otherwise behaviour is undefined
544544 // because there is no guaranteed ordering between LHS and RHS evaluation
545545 str content = parse_content(d);
gen/Lib.hs+3−3
@@ -10,7 +10,7 @@ module Lib(
1010import Control.Monad.Extra
1111import Control.Monad.Trans.Writer
1212import Data.Maybe
13−import Data.List
13+import Data.List.Extra
1414import Data.Bits
1515import Data.Monoid
1616import Numeric
@@ -67,7 +67,7 @@ choices :: [Parser o ()] -> Parser o ()
6767choices = foldr1 choice
6868
6969optional :: Parser o () -> Parser o ()
70−optional x = choice x $ return ()
70+optional x = choice x $ pure ()
7171
7272
7373---------------------------------------------------------------------
@@ -106,7 +106,7 @@ charSetC sets =
106106 ,\x -> fromMaybe ("table[(int) *p] & " ++ showBit (bit $ fromJust $ elemIndex x hard)) $ simple x)
107107 where
108108 showBit x = "0x" ++ showHex (x :: Int) ""
109− disp c = showBit $ foldr (.|.) 0 [bit i | (i, h) <- zip [0..] hard, fromCharSet h c]
109+ disp c = showBit $ foldr (.|.) 0 [bit i | (i, h) <- zipFrom 0 hard, fromCharSet h c]
110110
111111 hard = nub $ filter (isNothing . simple) sets
112112
gen/Main.hs+3−3
@@ -23,18 +23,18 @@ main = do
2323 putStrLn $ rerender src res
2424 writeFileChanged "test.h" $ unlines $ compile html
2525 buildRule ["test.h","test.c"] ["gen" <.> (if isWindows then "exe" else "")] $
26− system_ "gcc test.c -o gen -Werror"
26+ system_ "gcc test.c -o gen -Werror -Wall -Wextra -DTEST"
2727 system_ "gen"
2828
2929writeFileChanged :: FilePath -> String -> IO ()
3030writeFileChanged file new = do
31− old <- ifM (doesFileExist file) (readFile' file) (return "")
31+ old <- ifM (doesFileExist file) (readFile' file) (pure "")
3232 when (old /= new) $
3333 writeFile file new
3434
3535buildRule :: [FilePath] -> [FilePath] -> IO () -> IO ()
3636buildRule from to act = do
37− let modTime x = ifM (doesFileExist x) (Just <$> getModificationTime x) (return Nothing)
37+ let modTime x = ifM (doesFileExist x) (Just <$> getModificationTime x) (pure Nothing)
3838 from <- mapM modTime from
3939 to <- mapM modTime to
4040 when (any isNothing (from ++ to) || maximum from >= minimum to)
gen/Parser.hs+4−2
@@ -13,7 +13,7 @@ data Out
1313
1414is lower upper x = x >= lower && x <= upper
1515isName1 x = x == ':' || x == '_' || is 'a' 'z' x || is 'A' 'Z' x
16−isName x = isName1 x || x == '-' || is '0' '9' x
16+isName x = isName1 x || x == '-' || x == '.' || is '0' '9' x
1717isSpace x = x == ' ' || x == '\t' || x == '\r' || x == '\n'
1818
1919whitespace = many $ match isSpace
@@ -52,7 +52,9 @@ tag = do
5252 let name_ = name `choice` abort "Missing tag name"
5353 choices
5454 [do lit "!--"
55− many $ lit "-->" -- FIXME: Not correct!!!
55+ many $ match (/= '-')
56+ -- FIXME: Not correct!!!
57+ lit "-->"
5658 out TagComment
5759 ,do lit "?"
5860 name_
gen/test.c+13−3
@@ -330,15 +330,21 @@ static inline str gap(const char* ref, const char* start, const char* end)
330330 attr* attr = attr_alloc(&d->attrs); \
331331 attr->name = gap(d->body, name_start, name_end); \
332332 attr->value = gap(d->body, quote_start, p);
333−#define P_TagComment printf("MISSING: TagComment %s\n", p)
333+#define P_TagComment \
334+ // No action required - comments just flow as normal text
334335#define P_TagOpen \
335336 me->nodes.length = -1; \
336337 me->inner = gap(d->body, p, p); \
337338 me->outer = gap(d->body, tag_start, tag_start);
338339#define P_TagClose \
339− str str = node_commit(&d->nodes); \
340+ str nodes = node_commit(&d->nodes); \
341+ if (d->nodes.used_back == 0) \
342+ return "Closing tag with no openning"; \
340343 me = &d->nodes.nodes[d->nodes.size - d->nodes.used_back]; \
341− me->nodes = str; \
344+ me->nodes = nodes; \
345+ if (me->name.length != gap(d->body, name_start, name_end).length || \
346+ memcmp(&d->body[me->name.start], name_start, me->name.length) != 0) \
347+ return "Mismatch in closing tag"; \
342348 me->inner = gap(d->body, &d->body[me->inner.start], tag_start); \
343349 me->outer = gap(d->body, &d->body[me->outer.start], p);
344350#define P_TagOpenClose \
@@ -409,6 +415,8 @@ document* hexml_document_parse(const char* s, int slen)
409415/////////////////////////////////////////////////////////////////////
410416// TEST HARNESS (temporary)
411417
418+#ifdef TEST
419+
412420static char* resolve(const document* d, str loc)
413421{
414422 char* buf = malloc(loc.length + 3);
@@ -456,6 +464,8 @@ int main()
456464 return 0;
457465}
458466
467+#endif
468+
459469/*
460470void main()
461471{
ghci.bat+1
@@ -1 +1,2 @@
11gcc -O2 -std=c99 -c cbits\hexml.c -o cbits\hexml.o -Wall -Wextra -Werror && ghci.exe cbits\hexml.o %*
2+rem gcc -O2 -std=c99 -c gen\test.c -o gen\test.o -Wall -Wextra -Werror && ghci.exe gen\test.o %*
hexml.cabal+4−3
@@ -1,19 +1,20 @@
11cabal-version: >= 1.18
22build-type: Simple
33name: hexml
4−version: 0.3.3
4+version: 0.3.4
55license: BSD3
6+x-license: BSD-3-Clause OR Apache-2.0
67license-file: LICENSE
78category: XML
89author: Neil Mitchell <ndmitchell@gmail.com>
910maintainer: Neil Mitchell <ndmitchell@gmail.com>
10−copyright: Neil Mitchell 2018
11+copyright: Neil Mitchell 2016-2023
1112synopsis: XML subset DOM parser
1213description:
1314 An XML DOM-style parser, that only parses a subset of XML, but is designed to be fast.
1415homepage: https://github.com/ndmitchell/hexml#readme
1516bug-reports: https://github.com/ndmitchell/hexml/issues
16−tested-with: GHC==8.2.1, GHC==8.0.2, GHC==7.10.3, GHC==7.8.4, GHC==7.6.3
17+tested-with: GHC==9.6, GHC==9.4, GHC==9.2, GHC==9.0, GHC==8.10, GHC==8.8
1718extra-doc-files:
1819 README.md
1920 CHANGES.txt
src/Main.hs+4−4
@@ -44,12 +44,12 @@ main = do
4444 attributes (head $ children doc) === [Attribute "id" "1", Attribute "extra" "2"]
4545 map (`attributeBy` "id") (childrenBy doc "test") === map (fmap (Attribute "id")) [Just "1", Just "2", Just "4", Nothing]
4646
47− Right _ <- return $ parse $ "<test " <> BS.unwords [BS.pack $ "x" ++ show i ++ "='value'" | i <- [1..10000]] <> " />"
48− Right _ <- return $ parse $ BS.unlines $ replicate 10000 "<test x='value' />"
47+ Right _ <- pure $ parse $ "<test " <> BS.unwords [BS.pack $ "x" ++ show i ++ "='value'" | i <- [1..10000]] <> " />"
48+ Right _ <- pure $ parse $ BS.unlines $ replicate 10000 "<test x='value' />"
4949
5050 let attrs = ["usd:jpy","test","extra","more","stuff","jpy:usd","xxx","xxxx"]
51− Right doc <- return $ parse $ "<test " <> BS.unwords [x <> "='" <> x <> "'" | x <- attrs] <> ">middle</test>"
52− [c] <- return $ childrenBy doc "test"
51+ Right doc <- pure $ parse $ "<test " <> BS.unwords [x <> "='" <> x <> "'" | x <- attrs] <> ">middle</test>"
52+ [c] <- pure $ childrenBy doc "test"
5353 forM_ attrs $ \a -> attributeBy c a === Just (Attribute a a)
5454 forM_ ["missing","gone","nothing"] $ \a -> attributeBy c a === Nothing
5555 putStrLn "\nSuccess"
src/Text/XML/Hexml.hs+9−9
@@ -90,11 +90,11 @@ parse src = do
9090 if err /= nullPtr then do
9191 bs <- BS.packCString =<< hexml_document_error doc
9292 hexml_document_free doc
93− return $ Left bs
93+ pure $ Left bs
9494 else do
9595 node <- hexml_document_node doc
9696 doc <- newForeignPtr hexml_document_free_funptr doc
97− return $ Right $ Node src0 doc node
97+ pure $ Right $ Node src0 doc node
9898
9999-- | Given a node, rerender it to something with an equivalent parse tree.
100100-- Mostly useful for debugging - if you want the real source document use 'outer' instead.
@@ -103,7 +103,7 @@ render (Node src doc n) = unsafePerformIO $ withForeignPtr doc $ \d -> do
103103 i <- hexml_node_render d n nullPtr 0
104104 res <- BS.create (fromIntegral i) $ \ptr -> void $ hexml_node_render d n (castPtr ptr) i
105105 touchBS src
106− return res
106+ pure res
107107
108108applyStr :: BS.ByteString -> Str -> BS.ByteString
109109applyStr bs Str{..} = BS.take (fromIntegral strLength) $ BS.drop (fromIntegral strStart) bs
@@ -118,7 +118,7 @@ attrPeek :: BS.ByteString -> ForeignPtr CDocument -> Ptr CAttr -> Attribute
118118attrPeek src doc a = unsafePerformIO $ withForeignPtr doc $ \_ -> do
119119 name <- applyStr src <$> peekElemOff (castPtr a) 0
120120 val <- applyStr src <$> peekElemOff (castPtr a) 1
121− return $ Attribute name val
121+ pure $ Attribute name val
122122
123123-- | Get the name of a node, e.g. @\<test /\>@ produces @\"test\"@.
124124name :: Node -> BS.ByteString
@@ -142,7 +142,7 @@ outer = nodeBS 2
142142contents :: Node -> [Either BS.ByteString Node]
143143contents n@(Node src _ _) = f (strStart inner) outers
144144 where
145− f i [] = string i (strEnd inner) ++ []
145+ f i [] = string i (strEnd inner)
146146 f i ((x, n):xs) = string i (strStart x) ++ Right n : f (strEnd x) xs
147147
148148 string start end | start == end = []
@@ -156,7 +156,7 @@ children (Node src doc n) = unsafePerformIO $ withForeignPtr doc $ \d ->
156156 alloca $ \count -> do
157157 res <- hexml_node_children d n count
158158 count <- fromIntegral <$> peek count
159− return [Node src doc $ plusPtr res $ i*szNode | i <- [0..count-1]]
159+ pure [Node src doc $ plusPtr res $ i*szNode | i <- [0..count-1]]
160160
161161-- | Get the attributes of this node.
162162attributes :: Node -> [Attribute]
@@ -164,7 +164,7 @@ attributes (Node src doc n) = unsafePerformIO $ withForeignPtr doc $ \d ->
164164 alloca $ \count -> do
165165 res <- hexml_node_attributes d n count
166166 count <- fromIntegral <$> peek count
167− return [attrPeek src doc $ plusPtr res $ i*szAttr | i <- [0..count-1]]
167+ pure [attrPeek src doc $ plusPtr res $ i*szAttr | i <- [0..count-1]]
168168
169169-- | Get the direct children of this node which have a specific name.
170170-- A more efficient version of:
@@ -177,7 +177,7 @@ childrenBy (Node src doc n) str = go nullPtr
177177 BS.unsafeUseAsCStringLen str $ \(bs, len) -> do
178178 r <- hexml_node_child d n old bs $ fromIntegral len
179179 touchBS src
180− return $ if r == nullPtr then [] else Node src doc r : go r
180+ pure $ if r == nullPtr then [] else Node src doc r : go r
181181
182182-- | Get the first attribute of this node which has a specific name, if there is one.
183183-- A more efficient version of:
@@ -188,7 +188,7 @@ attributeBy (Node src doc n) str = unsafePerformIO $ withForeignPtr doc $ \d ->
188188 BS.unsafeUseAsCStringLen str $ \(bs, len) -> do
189189 r <- hexml_node_attribute d n bs $ fromIntegral len
190190 touchBS src
191− return $ if r == nullPtr then Nothing else Just $ attrPeek src doc r
191+ pure $ if r == nullPtr then Nothing else Just $ attrPeek src doc r
192192
193193-- | Find the starting location of a node, the @<@ character.
194194-- The first character will be reported as @(line 1,column 1)@, because thats