on 6da2ece help sign in
.ghci+1−13
since splitsnapshots
unifiedsplit
@@ -1,17 +1,5 @@
1−:set -Wunused-binds -Wunused-imports -Worphans
1+:set -fwarn-unused-binds -fwarn-unused-imports
2:set -isrc
2:set -isrc
3:load Main
3:load Main
4
4
5:def test \xs -> return $ ":main " ++ xs
5:def test \xs -> return $ ":main " ++ xs
6−
7−:{
8−:def docs_ const $ return $ unlines
9− [":!cabal configure"
10− ,":!cabal haddock"]
11−:}
12−
13−:{
14−:def docs const $ return $ unlines
15− [":docs_"
16− ,":!start dist\\doc\\html\\hexml\\Text-XML-Hexml.html"]
17−:}
.github/workflows/ci.ymldeleted−33
@@ -1,33 +0,0 @@
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
.gitignoredeleted−11
@@ -1,11 +0,0 @@
1−/dist/
2−*.o
3−*.prof
4−*.suo
5−*.opendb
6−*.db
7−*.user
8−/.stack-work/
9−/issues/
10−/vs/x64/
11−/afl-results/
CHANGES.txtdeleted−19
@@ -1,19 +0,0 @@
1−Changelog for Hexml
2−
3−0.3.4, released 2018-09-10
4− #15, licensed under BSD-3-Clause OR Apache-2.0
5−0.3.3, released 2017-10-27
6− #13, allow the . character in tags and attributes
7−0.3.2, released 2017-05-22
8− #10, mark the small fast FFI calls as "unsafe" (up to 10x faster)
9−0.3.1, released 2016-12-27
10− #9, don't walk off the end of the character table
11−0.3, released 2016-12-22
12− #8, Mac compatibility
13− Fix a bug when parsing closing comments
14− #7, prefix all the C functions with hexml_
15−0.2, released 2016-12-14
16− #6, remove buffer overruns
17− #5, add lower bounds
18−0.1, released 2016-12-04
19− Initial version
LICENSEdeleted−217
@@ -1,217 +0,0 @@
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
13−All rights reserved.
14−
15−Redistribution and use in source and binary forms, with or without
16−modification, are permitted provided that the following conditions
17−are met:
18−
19− * Redistributions of source code must retain the above copyright
20− notice, this list of conditions and the following disclaimer.
21−
22− * Redistributions in binary form must reproduce the above
23− copyright notice, this list of conditions and the following
24− disclaimer in the documentation and/or other materials provided
25− with the distribution.
26−
27− * Neither the name of Neil Mitchell nor the names of other
28− contributors may be used to endorse or promote products derived
29− from this software without specific prior written permission.
30−
31−THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
32−"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
33−LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
34−A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
35−OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
36−SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
37−LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
38−DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
39−THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
40−(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
41−OF 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.mddeleted−5
@@ -1,5 +0,0 @@
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+2−17
@@ -1,17 +1,2 @@
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)
1+# hexml
2−
2+A bad XML parser
3−An XML DOM-style parser, that only parses a subset of XML, but is designed to be fast. In particular:
4−
5−* Entities, e.g. `&amp;`, are not expanded.
6−* Not all the validity conditions are checked.
7−* No support for `<!DOCTYPE` related features.
8−
9−The 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.
10−
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)).
12−
13−Hexml is tested with [AFL](http://lcamtuf.coredump.cx/afl/).
14−
15−If 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.
Setup.hsdeleted−2
@@ -1,2 +0,0 @@
1−import Distribution.Simple
2−main = defaultMain
afl.shdeleted−40
@@ -1,40 +0,0 @@
1−# Script based on code from https://github.com/ndmitchell/hexml/issues/6
2−# More examples of things to do are at that ticket
3−set -e
4−
5−echo DISABLED DUE TO SEGFAULT IN CLANG WHEN COMPILING AFL
6−echo See https://github.com/ndmitchell/hexml/issues/14
7−exit 0
8−
9−# First install AFL
10−if ! command -v afl-clang-fast > /dev/null ; then
11− wget http://lcamtuf.coredump.cx/afl/releases/afl-latest.tgz
12− tar xf afl-latest.tgz
13− cd afl-*
14− make
15− cd llvm_mode && make && cd ..
16− sudo make install
17− cd libdislocator && make && sudo make install && cd ..
18− cd ..
19−fi
20−
21−# Compile it
22−AFL_USE_ASAN=1 afl-clang-fast -O2 -Icbits cbits/fuzz.c -o $PWD/hexml-fuzz
23−
24−# Fuzz it
25−if ! grep -q core /proc/sys/kernel/core_pattern ; then
26− echo core | sudo tee /proc/sys/kernel/core_pattern
27−fi
28−if [ -z "$CI" ] ; then
29− afl-fuzz -T hexml -m none -x /usr/local/share/afl/dictionaries/xml.dict -i $PWD/xml -o $PWD/afl-results -- $PWD/hexml-fuzz @@
30−else
31− AFL_EXIT_WHEN_DONE=1 timeout 5m afl-fuzz -T hexml -m none -x /usr/local/share/afl/dictionaries/xml.dict -i $PWD/xml -o $PWD/afl-results -- $PWD/hexml-fuzz @@ > /dev/null || true
32− cat afl-results/fuzzer_stats
33− grep "unique_crashes *: 0" afl-results/fuzzer_stats # fail if there are failures
34−fi
35−
36−# Minimize failures
37−# $ AFL_PRELOAD=/usr/local/lib/afl/libdislocator.so afl-cmin -i results/crashes/ -o results.shrunk -- $PWD/a.out @@
38−# $ mkdir results.min
39−# $ for x in `ls results.shrunk`; do AFL_PRELOAD=/usr/local/lib/afl/libdislocator.so afl-tmin -i results.shrunk/$x -o results.min/$x -- $PWD/a.out @@; done
40−# $ AFL_PRELOAD=/usr/local/lib/afl/libdislocator.so afl-analyze -i results.shrunk/id:000013,sig:11,src:000002,op:havoc,rep:4 -- ./a.out @@
cbits/fuzz.cdeleted−26
@@ -1,26 +0,0 @@
1−#include <stdio.h>
2−#include <stdlib.h>
3−#include <string.h>
4−#include "hexml.c"
5−
6−int main(int ac, char** av)
7−{
8−#ifdef __AFL_HAVE_MANUAL_CONTROL
9− __AFL_INIT();
10−#endif
11−
12− FILE *f = fopen(av[1], "rb");
13− fseek(f, 0, SEEK_END);
14− size_t fsize = ftell(f);
15− rewind(f);
16−
17− char* string = malloc(fsize+1);
18− memset(string, 0, fsize+1);
19− (void) fread(string, fsize, 1, f);
20− fclose(f);
21−
22− document *doc = hexml_document_parse(string, fsize);
23− hexml_document_free(doc);
24− free(string);
25− return 0;
26−}
cbits/grammar.txtdeleted−129
@@ -1,129 +0,0 @@
1−-- Taken from https://www.w3.org/TR/xml11/ and simplified
2−
3−[1] document ::= content
4−[39] element ::= XMLDecl | EmptyElemTag | STag content ETag
5−[23] XMLDecl ::= '<?xml' (S Attribute)* S? '?>'
6−[10] AttValue ::= '"' [^<"] '"' | "'" [^<'] "'"
7−[44] EmptyElemTag ::= '<' Name (S Attribute)* S? '/>'
8−[25] Eq ::= S? '=' S?
9−[40] STag ::= '<' Name (S Attribute)* S? '>' [WFC: Unique Att Spec]
10−[41] Attribute ::= Name Eq AttValue
11−[43] content ::= (element | CDSect | Comment | [^<]+)*
12−[42] ETag ::= '</' Name S? '>'
13−[18] CDSect ::= CDStart CData CDEnd
14−[19] CDStart ::= '<![CDATA['
15−[20] CData ::= (Char* - (Char* ']]>' Char*))
16−[21] CDEnd ::= ']]>'
17−[15] Comment ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->'
18−
19−-- The original, mechanically extracted
20−
21−[1] document ::= ( prolog element Misc* ) - ( Char* RestrictedChar Char* )
22−[2] Char ::= [#x1-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] /* any Unicode character, excluding the surrogate blocks, FFFE, and FFFF. */
23−[2a] RestrictedChar ::= [#x1-#x8] | [#xB-#xC] | [#xE-#x1F] | [#x7F-#x84] | [#x86-#x9F]
24−[3] S ::= (#x20 | #x9 | #xD | #xA)+
25−[4] NameStartChar ::= ":" | [A-Z] | "_" | [a-z] | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF] | [#x370-#x37D] | [#x37F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF]
26−[4a] NameChar ::= NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040]
27−[5] Name ::= NameStartChar (NameChar)*
28−[6] Names ::= Name (#x20 Name)*
29−[7] Nmtoken ::= (NameChar)+
30−[8] Nmtokens ::= Nmtoken (#x20 Nmtoken)*
31−[9] EntityValue ::= '"' ([^%&"] | PEReference | Reference)* '"'
32−| "'" ([^%&'] | PEReference | Reference)* "'"
33−[10] AttValue ::= '"' ([^<&"] | Reference)* '"'
34−| "'" ([^<&'] | Reference)* "'"
35−[11] SystemLiteral ::= ('"' [^"]* '"') | ("'" [^']* "'")
36−[12] PubidLiteral ::= '"' PubidChar* '"' | "'" (PubidChar - "'")* "'"
37−[13] PubidChar ::= #x20 | #xD | #xA | [a-zA-Z0-9] | [-'()+,./:=?;!*#@$_%]
38−[14] CharData ::= [^<&]* - ([^<&]* ']]>' [^<&]*)
39−[15] Comment ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->'
40−[16] PI ::= '<?' PITarget (S (Char* - (Char* '?>' Char*)))? '?>'
41−[17] PITarget ::= Name - (('X' | 'x') ('M' | 'm') ('L' | 'l'))
42−[18] CDSect ::= CDStart CData CDEnd
43−[19] CDStart ::= '<![CDATA['
44−[20] CData ::= (Char* - (Char* ']]>' Char*))
45−[21] CDEnd ::= ']]>'
46−[22] prolog ::= XMLDecl Misc* (doctypedecl Misc*)?
47−[23] XMLDecl ::= '<?xml' VersionInfo EncodingDecl? SDDecl? S? '?>'
48−[24] VersionInfo ::= S 'version' Eq ("'" VersionNum "'" | '"' VersionNum '"')
49−[25] Eq ::= S? '=' S?
50−[26] VersionNum ::= '1.1'
51−[27] Misc ::= Comment | PI | S
52−[28] doctypedecl ::= '<!DOCTYPE' S Name (S ExternalID)? S? ('[' intSubset ']' S?)? '>' [VC: Root Element Type] [WFC: External Subset]
53−[28a] DeclSep ::= PEReference | S [WFC: PE Between Declarations]
54−[28b] intSubset ::= (markupdecl | DeclSep)*
55−[29] markupdecl ::= elementdecl | AttlistDecl | EntityDecl | NotationDecl | PI | Comment [VC: Proper Declaration/PE Nesting] [WFC: PEs in Internal Subset]
56−[30] extSubset ::= TextDecl? extSubsetDecl
57−[31] extSubsetDecl ::= ( markupdecl | conditionalSect | DeclSep)*
58−[32] SDDecl ::= S 'standalone' Eq (("'" ('yes' | 'no') "'") | ('"' ('yes' | 'no') '"')) [VC: Standalone Document Declaration]
59−[39] element ::= EmptyElemTag
60−| STag content ETag [WFC: Element Type Match]
61−[VC: Element Valid]
62−[40] STag ::= '<' Name (S Attribute)* S? '>' [WFC: Unique Att Spec]
63−[41] Attribute ::= Name Eq AttValue [VC: Attribute Value Type]
64−[WFC: No External Entity References]
65−[WFC: No < in Attribute Values]
66−[42] ETag ::= '</' Name S? '>'
67−[43] content ::= CharData? ((element | Reference | CDSect | PI | Comment) CharData?)*
68−[44] EmptyElemTag ::= '<' Name (S Attribute)* S? '/>' [WFC: Unique Att Spec]
69−[45] elementdecl ::= '<!ELEMENT' S Name S contentspec S? '>' [VC: Unique Element Type Declaration]
70−[46] contentspec ::= 'EMPTY' | 'ANY' | Mixed | children
71−[47] children ::= (choice | seq) ('?' | '*' | '+')?
72−[48] cp ::= (Name | choice | seq) ('?' | '*' | '+')?
73−[49] choice ::= '(' S? cp ( S? '|' S? cp )+ S? ')' [VC: Proper Group/PE Nesting]
74−[50] seq ::= '(' S? cp ( S? ',' S? cp )* S? ')' [VC: Proper Group/PE Nesting]
75−[51] Mixed ::= '(' S? '#PCDATA' (S? '|' S? Name)* S? ')*'
76−| '(' S? '#PCDATA' S? ')' [VC: Proper Group/PE Nesting]
77−[VC: No Duplicate Types]
78−[52] AttlistDecl ::= '<!ATTLIST' S Name AttDef* S? '>'
79−[53] AttDef ::= S Name S AttType S DefaultDecl
80−[54] AttType ::= StringType | TokenizedType | EnumeratedType
81−[55] StringType ::= 'CDATA'
82−[56] TokenizedType ::= 'ID' [VC: ID]
83−[VC: One ID per Element Type]
84−[VC: ID Attribute Default]
85−| 'IDREF' [VC: IDREF]
86−| 'IDREFS' [VC: IDREF]
87−| 'ENTITY' [VC: Entity Name]
88−[57] EnumeratedType ::= NotationType | Enumeration
89−[58] NotationType ::= 'NOTATION' S '(' S? Name (S? '|' S? Name)* S? ')' [VC: Notation Attributes]
90−[VC: One Notation Per Element Type]
91−[VC: No Notation on Empty Element]
92−[VC: No Duplicate Tokens]
93−[59] Enumeration ::= '(' S? Nmtoken (S? '|' S? Nmtoken)* S? ')' [VC: Enumeration]
94−[VC: No Duplicate Tokens]
95−[60] DefaultDecl ::= '#REQUIRED' | '#IMPLIED'
96−| (('#FIXED' S)? AttValue) [VC: Required Attribute]
97−[VC: Attribute Default Value Syntactically Correct]
98−[WFC: No < in Attribute Values]
99−[VC: Fixed Attribute Default]
100−[WFC: No External Entity References]
101−[61] conditionalSect ::= includeSect | ignoreSect
102−[62] includeSect ::= '<![' S? 'INCLUDE' S? '[' extSubsetDecl ']]>' [VC: Proper Conditional Section/PE Nesting]
103−[63] ignoreSect ::= '<![' S? 'IGNORE' S? '[' ignoreSectContents* ']]>' [VC: Proper Conditional Section/PE Nesting]
104−[64] ignoreSectContents ::= Ignore ('<![' ignoreSectContents ']]>' Ignore)*
105−[65] Ignore ::= Char* - (Char* ('<![' | ']]>') Char*)
106−[66] CharRef ::= '&#' [0-9]+ ';'
107−| '&#x' [0-9a-fA-F]+ ';' [WFC: Legal Character]
108−[67] Reference ::= EntityRef | CharRef
109−[68] EntityRef ::= '&' Name ';' [WFC: Entity Declared]
110−[VC: Entity Declared]
111−[WFC: Parsed Entity]
112−[WFC: No Recursion]
113−[69] PEReference ::= '%' Name ';' [VC: Entity Declared]
114−[WFC: No Recursion]
115−[WFC: In DTD]
116−[70] EntityDecl ::= GEDecl | PEDecl
117−[71] GEDecl ::= '<!ENTITY' S Name S EntityDef S? '>'
118−[72] PEDecl ::= '<!ENTITY' S '%' S Name S PEDef S? '>'
119−[73] EntityDef ::= EntityValue | (ExternalID NDataDecl?)
120−[74] PEDef ::= EntityValue | ExternalID
121−[75] ExternalID ::= 'SYSTEM' S SystemLiteral
122−| 'PUBLIC' S PubidLiteral S SystemLiteral
123−[76] NDataDecl ::= S 'NDATA' S Name [VC: Notation Declared]
124−[77] TextDecl ::= '<?xml' VersionInfo? EncodingDecl S? '?>'
125−[78] extParsedEnt ::= ( TextDecl? content ) - ( Char* RestrictedChar Char* )
126−[80] EncodingDecl ::= S 'encoding' Eq ('"' EncName '"' | "'" EncName "'" )
127−[81] EncName ::= [A-Za-z] ([A-Za-z0-9._] | '-')* /* Encoding name contains only Latin characters */
128−[82] NotationDecl ::= '<!NOTATION' S Name S (ExternalID | PublicID) S? '>' [VC: Unique Notation Name]
129−[83] PublicID ::= 'PUBLIC' S PubidLiteral
cbits/hexml.c+109−162
@@ -1,5 +1,5 @@
1#include "hexml.h"
1#include "hexml.h"
2−#include <stdlib.h>
2+#include <malloc.h>
3#include <string.h>
3#include <string.h>
4#include <assert.h>
4#include <assert.h>
5#include <ctype.h>
5#include <ctype.h>
@@ -10,18 +10,18 @@ typedef int bool;
10/////////////////////////////////////////////////////////////////////
10/////////////////////////////////////////////////////////////////////
11// TYPES
11// TYPES
12
12
13−static inline int end(str s) { return s.start + s.length; }
13+int static inline end(str s) { return s.start + s.length; }
14
14
15−static inline str start_length(int32_t start, int32_t length)
15+str static inline start_length(int32_t start, int32_t length)
16{
16{
17− assert(start >= 0 && length >= 0);
17+ if (start < 0 || length < 0) assert(0);
18 str res;
18 str res;
19 res.start = start;
19 res.start = start;
20 res.length = length;
20 res.length = length;
21 return res;
21 return res;
22}
22}
23
23
24−static inline str start_end(int32_t start, int32_t end)
24+str static inline start_end(int32_t start, int32_t end)
25{
25{
26 return start_length(start, end - start);
26 return start_length(start, end - start);
27}
27}
@@ -40,7 +40,7 @@ typedef struct
40 // have a cursor at the end, which is stack scoped, so children write out, then I do
40 // have a cursor at the end, which is stack scoped, so children write out, then I do
41 // when you commit, you copy over from end to front
41 // when you commit, you copy over from end to front
42
42
43− // nodes
43+ // nodes
44 int size;
44 int size;
45 int used_front; // front entries, stored for good
45 int used_front; // front entries, stored for good
46 int used_back; // back entries, stack based, copied into front
46 int used_back; // back entries, stack based, copied into front
@@ -51,11 +51,11 @@ typedef struct
51
51
52struct document
52struct document
53{
53{
54− const char* body; // pointer to initial argument, not owned by us
54+ char* body; // pointer to initial argument, not owned by us
55
55
56 // things only used while parsing
56 // things only used while parsing
57− const char* cursor; // pointer to where we are in body
57+ char* cursor; // pointer to where we are in body
58− const char* end; // pointer to one past the last char
58+ char* end; // pointer to one past the last char
59 // if cursor is > end we have gone past the end
59 // if cursor is > end we have gone past the end
60
60
61 char* error_message;
61 char* error_message;
@@ -63,8 +63,8 @@ struct document
63 attr_buffer attrs;
63 attr_buffer attrs;
64};
64};
65
65
66−static inline int doc_length(const document* d) { return (int) (d->end - d->body); }
66+int static inline doc_length(document* d) { return d->end - d->body; }
67−static inline int doc_position(const document* d) { return (int) (d->cursor - d->body); }
67+int static inline doc_position(document* d) { return d->cursor - d->body; }
68
68
69
69
70/////////////////////////////////////////////////////////////////////
70/////////////////////////////////////////////////////////////////////
@@ -72,45 +72,49 @@ static inline int doc_position(const document* d) { return (int) (d->cursor - d-
72
72
73typedef struct
73typedef struct
74{
74{
75− const document* d;
75+ document* d;
76 char* buffer;
76 char* buffer;
77 int length;
77 int length;
78 int cursor;
78 int cursor;
79} render;
79} render;
80
80
81−static inline void render_char(render* r, char c)
81+void static inline render_char(render* r, char c)
82{
82{
83 if (r->cursor < r->length)
83 if (r->cursor < r->length)
84 r->buffer[r->cursor] = c;
84 r->buffer[r->cursor] = c;
85 r->cursor++;
85 r->cursor++;
86}
86}
87
87
88−static inline void bound(int idx, int mn, int mx)
88+void static inline bound(char* msg, int index, int mn, int mx)
89{
89{
90+ if (index < mn || index > mx)
91+ {
92+ //printf("Bounds checking failed %s, got %i, which should be in %i:%i\n", msg, index, mn, mx);
90− assert(idx >= mn && idx <= mx);
93+ assert(0);
94+ }
91}
95}
92
96
93−static inline void bound_str(str s, int mn, int mx)
97+void static inline bound_str(char* msg, str s, int mn, int mx)
94{
98{
95− assert(s.length >= 0);
99+ if (s.length < 0) assert(0);
96− bound(s.start, mn, mx);
100+ bound(msg, s.start, mn, mx);
97− bound(end(s), mn, mx);
101+ bound(msg, end(s), mn, mx);
98}
102}
99
103
100−static void render_str(render* r, str s)
104+void render_str(render* r, str s)
101{
105{
102− bound_str(s, 0, doc_length(r->d));
106+ bound_str("render_str", s, 0, doc_length(r->d));
103 for (int i = 0; i < s.length; i++)
107 for (int i = 0; i < s.length; i++)
104 render_char(r, r->d->body[s.start + i]);
108 render_char(r, r->d->body[s.start + i]);
105}
109}
106
110
107−static void render_tag(render* r, const node* n);
111+void render_tag(render* r, node* n);
108
112
109−static void render_content(render* r, const node* n)
113+void render_content(render* r, node* n)
110{
114{
111− bound_str(n->inner, 0, doc_length(r->d));
115+ bound_str("render_conent inner", n->inner, 0, doc_length(r->d));
112− bound_str(n->nodes, 0, r->d->nodes.used_front);
116+ bound_str("render_conent nodes", n->nodes, 0, r->d->nodes.used_front);
113− bound_str(n->attrs, 0, r->d->attrs.used);
117+ bound_str("render_conent attrs", n->attrs, 0, r->d->attrs.used);
114
118
115 int done = n->inner.start;
119 int done = n->inner.start;
116 for (int i = 0; i < n->nodes.length; i++)
120 for (int i = 0; i < n->nodes.length; i++)
@@ -123,7 +127,7 @@ static void render_content(render* r, const node* n)
123 render_str(r, start_end(done, end(n->inner)));
127 render_str(r, start_end(done, end(n->inner)));
124}
128}
125
129
126−static void render_tag(render* r, const node* n)
130+void render_tag(render* r, node* n)
127{
131{
128 render_char(r, '<');
132 render_char(r, '<');
129 render_str(r, n->name);
133 render_str(r, n->name);
@@ -145,18 +149,14 @@ static void render_tag(render* r, const node* n)
145 render_char(r, '>');
149 render_char(r, '>');
146}
150}
147
151
148−int hexml_node_render(const document* d, const node* n, char* buffer, int length)
152+int document_render(document* d, char* buffer, int length)
149{
153{
150 render r;
154 render r;
151 r.d = d;
155 r.d = d;
152 r.buffer = buffer;
156 r.buffer = buffer;
153 r.length = length;
157 r.length = length;
154 r.cursor = 0;
158 r.cursor = 0;
155− // The root node (and only the root node) has an empty length, so just render its innards
156− if (n->name.length == 0)
157− render_content(&r, n);
159+ render_content(&r, document_node(d));
158− else
159− render_tag(&r, n);
160 return r.cursor;
160 return r.cursor;
161}
161}
162
162
@@ -164,10 +164,10 @@ int hexml_node_render(const document* d, const node* n, char* buffer, int length
164/////////////////////////////////////////////////////////////////////
164/////////////////////////////////////////////////////////////////////
165// NOT THE PARSER
165// NOT THE PARSER
166
166
167−char* hexml_document_error(const document* d){return d->error_message;}
167+char* document_error(document* d){return d->error_message;}
168−node* hexml_document_node(const document* d){return &d->nodes.nodes[0];}
168+node* document_node(document* d){return &d->nodes.nodes[0];}
169
169
170−void hexml_document_free(document* d)
170+void document_free(document* d)
171{
171{
172 free(d->error_message);
172 free(d->error_message);
173 free(d->nodes.alloc);
173 free(d->nodes.alloc);
@@ -175,24 +175,25 @@ void hexml_document_free(document* d)
175 free(d);
175 free(d);
176}
176}
177
177
178−node* hexml_node_children(const document* d, const node* n, int* res)
178+node* node_children(document* d, node* n, int* res)
179{
179{
180 *res = n->nodes.length;
180 *res = n->nodes.length;
181 return &d->nodes.nodes[n->nodes.start];
181 return &d->nodes.nodes[n->nodes.start];
182}
182}
183
183
184−attr* hexml_node_attributes(const document* d, const node* n, int* res)
184+attr* node_attributes(document* d, node* n, int* res)
185{
185{
186 *res = n->attrs.length;
186 *res = n->attrs.length;
187 return &d->attrs.attrs[n->attrs.start];
187 return &d->attrs.attrs[n->attrs.start];
188}
188}
189
189
190
190
191−attr* hexml_node_attribute(const document* d, const node* n, const char* s, int slen)
191+attr* node_attributeBy(document* d, node* n, char* s, int slen)
192{
192{
193− if (slen == -1) slen = (int) strlen(s);
193+ if (slen == -1) slen = strlen(s);
194+ int i = n->attrs.start;
194− const int limit = end(n->attrs);
195+ int limit = end(n->attrs);
195− for (int i = n->attrs.start; i < limit; i++)
196+ for (i = n->attrs.start; i < limit; i++)
196 {
197 {
197 attr* r = &d->attrs.attrs[i];
198 attr* r = &d->attrs.attrs[i];
198 if (r->name.length == slen && memcmp(s, &d->body[r->name.start], slen) == 0)
199 if (r->name.length == slen && memcmp(s, &d->body[r->name.start], slen) == 0)
@@ -202,11 +203,11 @@ attr* hexml_node_attribute(const document* d, const node* n, const char* s, int
202}
203}
203
204
204// Search for given strings within a node
205// Search for given strings within a node
205−node* hexml_node_child(const document* d, const node* parent, const node* prev, const char* s, int slen)
206+node* node_childBy(document* d, node* parent, node* prev, char* s, int slen)
206{
207{
207− if (slen == -1) slen = (int) strlen(s);
208+ if (slen == -1) slen = strlen(s);
208− int i = prev == NULL ? parent->nodes.start : (int) (prev + 1 - d->nodes.nodes);
209+ int i = prev == NULL ? parent->nodes.start : prev + 1 - d->nodes.nodes;
209− const int limit = end(parent->nodes);
210+ int limit = end(parent->nodes);
210 for (; i < limit; i++)
211 for (; i < limit; i++)
211 {
212 {
212 node* r = &d->nodes.nodes[i];
213 node* r = &d->nodes.nodes[i];
@@ -220,49 +221,49 @@ node* hexml_node_child(const document* d, const node* parent, const node* prev,
220/////////////////////////////////////////////////////////////////////
221/////////////////////////////////////////////////////////////////////
221// PARSE TABLE
222// PARSE TABLE
222
223
223−static const char tag_name1 = 0x1;
224+const char tag_name1 = 0x1;
224−static const char tag_name = 0x2;
225+const char tag_name = 0x2;
225−static const char tag_space = 0x4;
226+const char tag_space = 0x4;
226
227
227−static char parse_table[256];
228+char parse_table[256];
228
229
229−static void init_parse_table()
230+void init_parse_table()
230{
231{
231 static bool done = 0;
232 static bool done = 0;
232 if (done) return;
233 if (done) return;
233 for (int i = 0; i < 256; i++)
234 for (int i = 0; i < 256; i++)
234 {
235 {
235 bool name1 = i == ':' || i == '_' || (i >= 'a' && i <= 'z') || (i >= 'A' && i <= 'Z');
236 bool name1 = i == ':' || i == '_' || (i >= 'a' && i <= 'z') || (i >= 'A' && i <= 'Z');
236− bool name = name1 || i == '-' || i == '.' || (i >= '0' && i <= '9');
237+ bool name = name1 || i == '-' || (i >= '0' && i <= '9');
237 bool space = i == ' ' || i == '\t' || i == '\r' || i == '\n';
238 bool space = i == ' ' || i == '\t' || i == '\r' || i == '\n';
238 parse_table[i] = (name1 ? tag_name1 : 0) | (name ? tag_name : 0) | (space ? tag_space : 0);
239 parse_table[i] = (name1 ? tag_name1 : 0) | (name ? tag_name : 0) | (space ? tag_space : 0);
239 }
240 }
240 done = 1;
241 done = 1;
241}
242}
242
243
243−static inline bool is(char c, char tag) { return parse_table[(unsigned char) c] & tag; }
244+bool static inline is(char c, char tag) { return parse_table[c] & tag; }
244−static inline bool is_name1(char c) { return is(c, tag_name1); }
245+bool static inline is_name1(char c) { return is(c, tag_name1); }
245−static inline bool is_name(char c) { return is(c, tag_name); }
246+bool static inline is_name(char c) { return is(c, tag_name); }
246−static inline bool is_space(char c) { return is(c, tag_space); }
247+bool static inline is_space(char c) { return is(c, tag_space); }
247
248
248
249
249/////////////////////////////////////////////////////////////////////
250/////////////////////////////////////////////////////////////////////
250// PARSER COMBINATORS
251// PARSER COMBINATORS
251
252
252−static inline char peek_at(const document* d, int i) { return d->cursor[i]; }
253+char static inline peekAt(document* d, int i) { return d->cursor[i]; }
253−static inline char peek(const document* d) { return peek_at(d, 0); }
254−static inline void skip(document* d, int i) { d->cursor += i; }
254+void static inline skip(document* d, int i) { d->cursor += i; }
255+char static inline peek(document* d) { return peekAt(d, 0); }
255−static inline char get(document* d) { char c = peek(d); skip(d, 1); return c; }
256+char static inline get(document* d) { char c = peek(d); skip(d, 1); return c; }
256
257
257// Remove whitespace characters from the cursor while they are still whitespace
258// Remove whitespace characters from the cursor while they are still whitespace
258−static inline void trim(document* d)
259+void static inline trim(document* d)
259{
260{
260 while (is_space(peek(d)))
261 while (is_space(peek(d)))
261 skip(d, 1);
262 skip(d, 1);
262}
263}
263
264
264// Find this character form the cursor onwards, if true adjust the cursor to that char, otherwise leave it at the end
265// Find this character form the cursor onwards, if true adjust the cursor to that char, otherwise leave it at the end
265−static bool find(document* d, char c)
266+bool find(document* d, char c)
266{
267{
267 char* x = memchr(d->cursor, c, d->end - d->cursor);
268 char* x = memchr(d->cursor, c, d->end - d->cursor);
268 if (x == NULL)
269 if (x == NULL)
@@ -281,47 +282,37 @@ static bool find(document* d, char c)
281/////////////////////////////////////////////////////////////////////
282/////////////////////////////////////////////////////////////////////
282// PARSING CODE
283// PARSING CODE
283
284
284−static inline void node_alloc(node_buffer* b, int ask)
285+void static inline node_alloc(node_buffer* b, int ask)
285{
286{
286 int space = b->size - b->used_back - b->used_front;
287 int space = b->size - b->used_back - b->used_front;
287 if (space >= ask) return;
288 if (space >= ask) return;
288− int size2 = (b->size + 1000 + ask) * 2;
289− node* buf2 = malloc(size2 * sizeof(node));
290− assert(buf2);
289+ assert(0);
291− memcpy(buf2, b->nodes, b->used_front * sizeof(node));
292− memcpy(&buf2[size2 - b->used_back], &b->nodes[b->size - b->used_back], b->used_back * sizeof(node));
293− free(b->alloc);
294− b->size = size2;
295− b->nodes = buf2;
296− b->alloc = buf2;
297}
290}
298
291
299−static inline void attr_alloc(attr_buffer* b, int ask)
292+void static inline attr_alloc(attr_buffer* b, int ask)
300{
293{
301 int space = b->size - b->used;
294 int space = b->size - b->used;
302 if (space >= ask) return;
295 if (space >= ask) return;
303 int size2 = (b->size + 1000 + ask) * 2;
296 int size2 = (b->size + 1000 + ask) * 2;
304 attr* buf2 = malloc(size2 * sizeof(attr));
297 attr* buf2 = malloc(size2 * sizeof(attr));
305− assert(buf2);
306 memcpy(buf2, b->attrs, b->used * sizeof(attr));
298 memcpy(buf2, b->attrs, b->used * sizeof(attr));
307 free(b->alloc);
299 free(b->alloc);
308− b->size = size2;
309 b->attrs = buf2;
300 b->attrs = buf2;
310 b->alloc = buf2;
301 b->alloc = buf2;
311}
302}
312
303
313−static void set_error(document* d, const char* msg)
304+void set_error(document* d, char* msg)
314{
305{
315 if (d->error_message != NULL) return; // keep the first error message
306 if (d->error_message != NULL) return; // keep the first error message
316 d->error_message = malloc(strlen(msg)+1);
307 d->error_message = malloc(strlen(msg)+1);
317− assert(d->error_message);
318 strcpy(d->error_message, msg);
308 strcpy(d->error_message, msg);
319}
309}
320
310
321// you now expect a name, perhaps preceeded by whitespace
311// you now expect a name, perhaps preceeded by whitespace
322// the name may be empty
312// the name may be empty
323−static inline str parse_name(document* d)
313+str static inline parse_name(document* d)
324{
314{
315+ trim(d);
325 int start = doc_position(d);
316 int start = doc_position(d);
326 if (!is_name1(peek(d)))
317 if (!is_name1(peek(d)))
327 return start_length(start, 0);
318 return start_length(start, 0);
@@ -331,14 +322,10 @@ static inline str parse_name(document* d)
331 return start_end(start, doc_position(d));
322 return start_end(start, doc_position(d));
332}
323}
333
324
334−static inline str parse_attrval(document* d)
325+str static inline parse_attrval(document* d)
335{
326{
336 trim(d);
327 trim(d);
337− if (peek(d) != '=')
338− {
339− set_error(d, "Expected = in attribute, but missing");
340− return start_length(0, 0);
328+ if (peek(d) != '=') return start_length(0,0);
341− }
342 skip(d, 1);
329 skip(d, 1);
343 trim(d);
330 trim(d);
344 char c = peek(d);
331 char c = peek(d);
@@ -355,22 +342,19 @@ static inline str parse_attrval(document* d)
355 return start_end(start, doc_position(d) - 1);
342 return start_end(start, doc_position(d) - 1);
356 }
343 }
357 else
344 else
358− {
345+ return parse_name(d);
359− set_error(d, "Invalid attribute");
360− return start_length(0, 0);
361− }
362}
346}
363
347
364
348
365
349
366// seen a tag name, now looking for attributes terminated by >
350// seen a tag name, now looking for attributes terminated by >
367// puts the attributes it finds in the attribute buffer
351// puts the attributes it finds in the attribute buffer
368−static inline str parse_attributes(document* d)
352+str static inline parse_attributes(document* d)
369{
353{
354+ str res;
370− int start = d->attrs.used;
355+ res.start = d->attrs.used;
371− while (d->error_message == NULL)
356+ for (int i = 0; ; i++)
372 {
357 {
373− trim(d);
374 str name = parse_name(d);
358 str name = parse_name(d);
375 if (name.length == 0) break;
359 if (name.length == 0) break;
376 attr_alloc(&d->attrs, 1);
360 attr_alloc(&d->attrs, 1);
@@ -378,39 +362,34 @@ static inline str parse_attributes(document* d)
378 d->attrs.attrs[d->attrs.used].value = parse_attrval(d);
362 d->attrs.attrs[d->attrs.used].value = parse_attrval(d);
379 d->attrs.used++;
363 d->attrs.used++;
380 }
364 }
381− return start_end(start, d->attrs.used);
365+ res.length = d->attrs.used - res.start;
366+ return res;
382}
367}
383
368
384−static str parse_content(document* d);
369+str parse_content(document* d);
385
370
386
371
387// Add a new entry into tag, am at a '<'
372// Add a new entry into tag, am at a '<'
388−static inline void parse_tag(document* d)
373+void static inline parse_tag(document* d)
389{
374{
390 node_alloc(&d->nodes, 1);
375 node_alloc(&d->nodes, 1);
391 d->nodes.used_back++;
376 d->nodes.used_back++;
392− node* me = &d->nodes.nodes[d->nodes.size - d->nodes.used_back];
377+ int me = d->nodes.size - d->nodes.used_back;
393
378
394− me->outer.start = doc_position(d);
379+ d->nodes.nodes[me].outer.start = doc_position(d);
395 char c = get(d);
380 char c = get(d);
396 assert(c == '<');
381 assert(c == '<');
397 if (peek(d) == '?') skip(d, 1);
382 if (peek(d) == '?') skip(d, 1);
398− me->name = parse_name(d);
383+ d->nodes.nodes[me].name = parse_name(d);
399− if (me->name.length == 0)
400− {
401− set_error(d, "Missing tag name");
402− return;
403− }
404− me->attrs = parse_attributes(d);
384+ d->nodes.nodes[me].attrs = parse_attributes(d);
405− if (d->error_message != NULL) return;
406
385
407− c = peek(d);
386+ c = get(d);
408− if ((c == '/' || c == '?') && peek_at(d, 1) == '>')
387+ if ((c == '/' || c == '?') && peek(d) == '>')
409 {
388 {
410− skip(d, 2);
389+ skip(d, 1);
411− me->nodes = start_length(0, 0);
390+ d->nodes.nodes[me].nodes = start_length(0, 0);
412− me->outer.length = start_end(me->outer.start, doc_position(d)).length;
391+ d->nodes.nodes[me].outer.length = start_end(d->nodes.nodes[me].outer.start, doc_position(d)).length;
413− me->inner = start_length(doc_position(d), 0);
392+ d->nodes.nodes[me].inner = start_length(doc_position(d), 0);
414 return;
393 return;
415 }
394 }
416 else if (c != '>')
395 else if (c != '>')
@@ -418,28 +397,23 @@ static inline void parse_tag(document* d)
418 set_error(d, "Gunk at the end of the tag");
397 set_error(d, "Gunk at the end of the tag");
419 return;
398 return;
420 }
399 }
421− else
422− skip(d, 1);
423− me->inner.start = doc_position(d);
400+ d->nodes.nodes[me].inner.start = doc_position(d);
424− str content = parse_content(d);
401+ d->nodes.nodes[me].nodes = parse_content(d);
425−
426− // parse_content may have allocated more nodes, so recompute me
427− me = &d->nodes.nodes[d->nodes.size - d->nodes.used_back];
428− me->nodes = content;
429− me->inner.length = start_end(me->inner.start, doc_position(d)).length;
402+ d->nodes.nodes[me].inner.length = start_end(d->nodes.nodes[me].inner.start, doc_position(d)).length;
430
403
431 if (d->error_message != NULL) return;
404 if (d->error_message != NULL) return;
432− if (peek(d) == '<' && peek_at(d, 1) == '/')
405+ if (peek(d) == '<' && peekAt(d, 1) == '/')
433 {
406 {
434 skip(d, 2);
407 skip(d, 2);
408+ trim(d);
435− if (d->end - d->cursor >= me->name.length &&
409+ if (d->end - d->cursor >= d->nodes.nodes[me].name.length &&
436− memcmp(d->cursor, &d->body[me->name.start], me->name.length) == 0)
410+ memcmp(d->cursor, &d->body[d->nodes.nodes[me].name.start], d->nodes.nodes[me].name.length) == 0)
437 {
411 {
438− skip(d, me->name.length);
412+ skip(d, d->nodes.nodes[me].name.length);
439 trim(d);
413 trim(d);
440 if (get(d) == '>')
414 if (get(d) == '>')
441 {
415 {
442− me->outer.length = start_end(me->outer.start, doc_position(d)).length;
416+ d->nodes.nodes[me].outer.length = start_end(d->nodes.nodes[me].outer.start, doc_position(d)).length;
443 return;
417 return;
444 }
418 }
445 }
419 }
@@ -451,7 +425,7 @@ static inline void parse_tag(document* d)
451
425
452// Parser until </, return the index of your node children
426// Parser until </, return the index of your node children
453// Not inline as it is recursive
427// Not inline as it is recursive
454−static str parse_content(document* d)
428+str parse_content(document* d)
455{
429{
456 int before = d->nodes.used_back;
430 int before = d->nodes.used_back;
457 while (d->error_message == NULL)
431 while (d->error_message == NULL)
@@ -461,33 +435,11 @@ static str parse_content(document* d)
461 {
435 {
462 break;
436 break;
463 }
437 }
464− else if (peek_at(d, 1) == '/')
438+ if (peekAt(d, 1) == '/')
465 {
439 {
466 // have found a </
440 // have found a </
467 break;
441 break;
468 }
442 }
469− else if (peek_at(d, 1) == '!' && peek_at(d, 2) == '-' && peek_at(d, 3) == '-')
470− {
471− skip(d, 3);
472− // you can't reuse the two '-' characters for the closing as well
473− if (peek_at(d, 0) == '\0' || peek_at(d, 1) == '\0')
474− {
475− set_error(d, "Didn't get a closing comment");
476− return start_end(0, 0);
477− }
478− skip(d, 2);
479− for (;;)
480− {
481− if (!find(d, '>'))
482− {
483− set_error(d, "Didn't get a closing comment");
484− return start_end(0, 0);
485− }
486− skip(d, 1);
487− if (peek_at(d, -3) == '-' && peek_at(d, -2) == '-')
488− break;
489− }
490− }
491 else
443 else
492 {
444 {
493 parse_tag(d);
445 parse_tag(d);
@@ -503,23 +455,20 @@ static str parse_content(document* d)
503 return res;
455 return res;
504}
456}
505
457
506−// Based on looking at ~50Kb XML documents, they seem to have ~700 attributes
507−// and ~300 nodes, so size appropriately to cope with that.
508typedef struct
458typedef struct
509{
459{
510 document document;
460 document document;
511 attr attrs[1000];
461 attr attrs[1000];
512− node nodes[500];
462+ node nodes[1000];
513} buffer;
463} buffer;
514
464
515−document* hexml_document_parse(const char* s, int slen)
465+document* document_parse(char* s, int slen)
516{
466{
517− if (slen == -1) slen = (int) strlen(s);
467+ if (slen == -1) slen = strlen(s);
518 assert(s[slen] == 0);
468 assert(s[slen] == 0);
519 init_parse_table();
469 init_parse_table();
520
470
521 buffer* buf = malloc(sizeof(buffer));
471 buffer* buf = malloc(sizeof(buffer));
522− assert(buf);
523 document* d = &buf->document;
472 document* d = &buf->document;
524 d->body = s;
473 d->body = s;
525 d->cursor = s;
474 d->cursor = s;
@@ -529,7 +478,7 @@ document* hexml_document_parse(const char* s, int slen)
529 d->attrs.used = 0;
478 d->attrs.used = 0;
530 d->attrs.attrs = buf->attrs;
479 d->attrs.attrs = buf->attrs;
531 d->attrs.alloc = NULL;
480 d->attrs.alloc = NULL;
532− d->nodes.size = 500;
481+ d->nodes.size = 1000;
533 d->nodes.used_back = 0;
482 d->nodes.used_back = 0;
534 d->nodes.used_front = 1;
483 d->nodes.used_front = 1;
535 d->nodes.nodes = buf->nodes;
484 d->nodes.nodes = buf->nodes;
@@ -539,13 +488,11 @@ document* hexml_document_parse(const char* s, int slen)
539 d->nodes.nodes[0].outer = start_length(0, slen);
488 d->nodes.nodes[0].outer = start_length(0, slen);
540 d->nodes.nodes[0].inner = start_length(0, slen);
489 d->nodes.nodes[0].inner = start_length(0, slen);
541 d->nodes.nodes[0].attrs = start_length(0, 0);
490 d->nodes.nodes[0].attrs = start_length(0, 0);
491+ d->nodes.nodes[0].nodes = parse_content(d);
542
492
543− // Introduce an intermediate result, otherwise behaviour is undefined
544− // because there is no guaranteed ordering between LHS and RHS evaluation
545− str content = parse_content(d);
546− d->nodes.nodes[0].nodes = content;
547−
548− if (d->cursor < d->end)
493+ if (d->cursor < d->end && d->error_message == NULL)
494+ {
549 set_error(d, "Trailing junk at the end of the document");
495 set_error(d, "Trailing junk at the end of the document");
496+ }
550 return d;
497 return d;
551}
498}
cbits/hexml.h+14−18
@@ -21,7 +21,6 @@ typedef struct
21 str name; // tag name, e.g. <[foo]>
21 str name; // tag name, e.g. <[foo]>
22 str inner; // inner text, <foo>[bar]</foo>
22 str inner; // inner text, <foo>[bar]</foo>
23 str outer; // outer text, [<foo>bar</foo>]
23 str outer; // outer text, [<foo>bar</foo>]
24− // Not usable, but required to get the correct sizing when returning arrays of nodes
25 str attrs; // all the attributes, in the attribute buffer (not usable)
24 str attrs; // all the attributes, in the attribute buffer (not usable)
26 str nodes; // all the nodes, in the node buffer (not usable)
25 str nodes; // all the nodes, in the node buffer (not usable)
27} node;
26} node;
@@ -29,29 +28,26 @@ typedef struct
29// Convention: if slen can be -1 for nul-terminated, or given explicitly
28// Convention: if slen can be -1 for nul-terminated, or given explicitly
30
29
31// Parse a document, returns a potentially invalid document - use document_error to check
30// Parse a document, returns a potentially invalid document - use document_error to check
32−// Must use document_free to release the memory (even if invalid).
31+// Must use document_free to release the memory.
33−// The string must be nul terminated, e.g. slen != -1 ==> slen[0]
34−document* hexml_document_parse(const char* s, int slen);
32+document* document_parse(char* s, int slen);
35
33
36// Free the memory returned from document_parse.
34// Free the memory returned from document_parse.
37−void hexml_document_free(document* d);
35+void document_free(document* d);
38
36
39−// Generate a fresh string with the same semantics as the node.
37+// generate a fresh document with the same semantics
40−// Requires an input buffer, returns the size of the rendered document.
38+// requires an input buffer, returns the size of the rendered document
41−// Requires the string passed to document_parse to be valid.
42−int hexml_node_render(const document* d, const node* n, char* buffer, int length);
39+int document_render(document* d, char* buffer, int length);
43
40
44−// Return either NULL (successful parse) or the error message.
41+// return either NULL (successful parse) or the error message
45−char* hexml_document_error(const document* d);
42+char* document_error(document* d);
46
43
47−// Return the root node of the document - imagine the document is wrapped in <>$DOC</> tags.
44+// return the root node of the document - imagine the document is wrapped in <>$DOC</> tags
48−node* hexml_document_node(const document* d);
45+node* document_node(document* d);
49
46
50// List all items within a node
47// List all items within a node
51−node* hexml_node_children(const document* d, const node* n, int* res);
48+node* node_children(document* d, node* n, int* res);
52−attr* hexml_node_attributes(const document* d, const node* n, int* res);
49+attr* node_attributes(document* d, node* n, int* res);
53
50
54// Search for given strings within a node, note that prev may be NULL
51// Search for given strings within a node, note that prev may be NULL
55−// Requires the string passed to document_parse to be valid.
56−node* hexml_node_child(const document* d, const node* parent, const node* prev, const char* s, int slen);
52+node* node_childBy(document* d, node* parent, node* prev, char* s, int slen);
57−attr* hexml_node_attribute(const document* d, const node* n, const char* s, int slen);
53+attr* node_attributeBy(document* d, node* n, char* s, int slen);
cbits/main.cdeleted−46
@@ -1,46 +0,0 @@
1−#include "hexml.h"
2−#include <stdio.h>
3−#include <malloc.h>
4−#include <time.h>
5−#include <string.h>
6−#include <assert.h>
7−
8−char* readFile(char* file)
9−{
10− FILE* f = fopen(file, "rb");
11− fseek(f, 0, SEEK_END);
12− int len = ftell(f);
13− fseek(f, 0, SEEK_SET);
14− char* res = malloc(len + 1);
15− assert(res);
16− res[len] = 0;
17− size_t n = fread(res, 1, len, f);
18− assert(n == len);
19− fclose(f);
20− return res;
21−}
22−
23−char* example = "<?xml version=\"1.0\" lang=\"ja\"?><foo><bar baz=\"qux\">quux</bar><bar baz=\"bar\">hoge</bar><piyo>&gt;piyopiyo&lt;</piyo></foo><foo2 />";
24−
25−int main(int argc, char **argv)
26−{
27− setbuf(stdout, NULL);
28− char* body = argv[1] == NULL ? example : readFile(argv[1]);
29− document* doc = hexml_document_parse(body, -1);
30− char* err = hexml_document_error(doc);
31− if (err == NULL)
32− {
33− int len = hexml_node_render(doc, hexml_document_node(doc), NULL, 0);
34− char* s = malloc(len + 1);
35− assert(s);
36− hexml_node_render(doc, hexml_document_node(doc), s, len);
37− s[len] = 0;
38− printf("Parse successful\n"); // , %s\n", s);
39− return 0;
40− }
41− else
42− {
43− printf("Parse failed, %s\n", err);
44− return 1;
45− }
46−}
gen/.ghcideleted−3
@@ -1,3 +0,0 @@
1−:set -fwarn-unused-binds -fwarn-unused-imports -fwarn-orphans
2−:load Main
3−:def test \xs -> return $ ":main" ++ xs
gen/.ghciddeleted−1
@@ -1 +0,0 @@
1−-c ghci --test=:test --reload=test.c
gen/Lib.hsdeleted−201
@@ -1,201 +0,0 @@
1−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2−
3−module Lib(
4− Parser,
5− out, many, optional, match, lit, choice, abort, choices,
6− run,
7− compile
8− ) where
9−
10−import Control.Monad.Extra
11−import Control.Monad.Trans.Writer
12−import Data.Maybe
13−import Data.List.Extra
14−import Data.Bits
15−import Data.Monoid
16−import Numeric
17−
18−
19−run :: Parser o () -> String -> ([(Int, o)], (Int, String), Maybe String)
20−run p = runC (toC $ toP p)
21−
22−compile :: Show o => Parser o () -> [String]
23−compile = prettyC . optimiseC . toC . toP
24−
25−---------------------------------------------------------------------
26−-- THE UNDERLYING PARSER TYPE
27−
28−data P o
29− = Empty
30− | Seq (P o) (P o)
31− | Choice (P o) (P o)
32− | Match CharSet
33− | Many (P o)
34− | Out o
35− | Panic String
36− deriving Show
37−
38−instance Monoid (P o) where
39− mempty = Empty
40− mappend = Seq
41−
42−newtype Parser o a = Parser (Writer (P o) a)
43− deriving (Functor, Applicative, Monad)
44−
45−toP :: Parser o a -> P o
46−toP (Parser p) = execWriter p
47−
48−out :: o -> Parser o ()
49−out o = Parser $ tell $ Out o
50−
51−abort :: String -> Parser o ()
52−abort x = Parser $ tell $ Panic x
53−
54−many :: Parser o () -> Parser o ()
55−many x = Parser $ tell $ Many $ toP x
56−
57−match :: (Char -> Bool) -> Parser o ()
58−match f = Parser $ tell $ Match $ CharSet f
59−
60−lit :: String -> Parser o ()
61−lit xs = forM_ xs $ \x -> match (== x)
62−
63−choice :: Parser o () -> Parser o () -> Parser o ()
64−choice a b = Parser $ tell $ Choice (toP a) (toP b)
65−
66−choices :: [Parser o ()] -> Parser o ()
67−choices = foldr1 choice
68−
69−optional :: Parser o () -> Parser o ()
70−optional x = choice x $ pure ()
71−
72−
73−---------------------------------------------------------------------
74−-- CHARSET
75−
76−chars = ['\1'..'\xff']
77−
78−newtype CharSet = CharSet {fromCharSet :: Char -> Bool}
79−
80−instance Eq CharSet where
81− CharSet f1 == CharSet f2 = all (\x -> f1 x == f2 x) chars
82−
83−instance Monoid CharSet where
84− mempty = CharSet $ const False
85− mappend (CharSet f1) (CharSet f2) = CharSet $ f1 ||^ f2
86−
87−instance Show CharSet where
88− show (CharSet p) = "[" ++ f chars ++ "]"
89− where
90− showChar x | x `elem` "[]-" = ['\\',x]
91− | otherwise = init $ tail $ show x
92− f xs = case dropWhile (not . p) xs of
93− [] -> []
94− x:xs -> case span p xs of
95− ([], rest) -> showChar x ++ f rest :: String
96− (ys, rest) -> showChar x ++ "-" ++ showChar (last ys) ++ f rest
97−
98−-- | Given we know the first argument are all true, can the second argument ever be False
99−implies :: [CharSet] -> CharSet -> Bool
100−implies known x = all (fromCharSet x) poss
101− where poss = filter (\c -> all (`fromCharSet` c) known) chars
102−
103−charSetC :: [CharSet] -> ([String], CharSet -> String)
104−charSetC sets =
105− (["static const char table[256] = {" ++ intercalate "," ("0":map disp chars) ++ "};"]
106− ,\x -> fromMaybe ("table[(int) *p] & " ++ showBit (bit $ fromJust $ elemIndex x hard)) $ simple x)
107− where
108− showBit x = "0x" ++ showHex (x :: Int) ""
109− disp c = showBit $ foldr (.|.) 0 [bit i | (i, h) <- zipFrom 0 hard, fromCharSet h c]
110−
111− hard = nub $ filter (isNothing . simple) sets
112−
113− simple x | x == CharSet (const True) = Just "*p"
114− | [x] <- filter (fromCharSet x) chars = Just $ "*p == " ++ show x
115− | otherwise = Nothing
116−
117−---------------------------------------------------------------------
118−-- THE UNDERLYING C TYPE
119−
120−data C o
121− = While CharSet [C o]
122− | Stmt o
123− | Next
124− | If CharSet [C o] [C o]
125− | Abort String
126− deriving Show
127−
128−-- Can you leave this statement successfully having consumed no characters?
129−empty :: P o -> Bool
130−empty Empty = True
131−empty (Choice a b) = empty b
132−empty Many{} = True
133−empty Out{} = True
134−empty Match{} = False
135−empty Panic{} = False
136−empty (Seq a b) = empty a && empty b
137−
138−-- What characters can come next and you commit
139−peek :: P o -> CharSet
140−peek Empty = mempty
141−peek Out{} = mempty
142−peek (Match f) = f
143−peek Panic{} = CharSet $ const True
144−peek (Choice a b) = peek a <> peek b
145−peek (Many x) = peek x
146−peek (Seq a b) = peek a <> (if empty a then peek b else mempty)
147−
148−toC :: P o -> [C o]
149−toC Empty = []
150−toC (Choice a b) = [If (peek a) (toC a) (toC b)]
151−toC (Match f) = [If f [Next] [Abort "Failed to match"]]
152−toC (Many x) = [While (peek x) (toC x)]
153−toC (Out x) = [Stmt x]
154−toC (Panic x) = [Abort x]
155−toC (Seq a b) = toC a ++ toC b
156−
157−runC :: [C o] -> String -> ([(Int, o)], (Int, String), Maybe String)
158−runC = f 0
159− where
160− f i [] s = ([], (i, s), Nothing)
161− f i (Abort x:_) s = ([], (i,s), Just x)
162− f i (Next:_) [] = ([], (i, []), Just "Next off the end")
163− f i (Next:r) (x:xs) = f (i+1) r xs
164− f i (Stmt x:r) xs = let (v1,v2,v3) = f i r xs in ((i,x):v1, v2, v3)
165− f i (If cond true false : r) xs
166− | x:_ <- xs, fromCharSet cond x = f i (true ++ r) xs
167− | otherwise = f i (false ++ r) xs
168− f i (While cond x:r) xs = f i (If cond (x ++ [While cond x]) [] : r) xs
169−
170−
171−prettyC :: Show o => [C o] -> [String]
172−prettyC xs =
173− prefix ++
174− concatMap f xs
175− where
176− (prefix, cond) = charSetC $ concatMap charSets xs
177− charSets (While c x) = c : concatMap charSets x
178− charSets (If c t f) = c : concatMap charSets t ++ concatMap charSets f
179− charSets _ = []
180−
181− fi = map (" "++) . concatMap f
182− f (Abort x) = ["P_Abort(" ++ show x ++ ");"]
183− f Next = ["p++;"]
184− f (Stmt x) = ["P_" ++ show x ++ ";"]
185− f (If c true []) = ["if (" ++ cond c ++ "){"] ++ fi true ++ ["}"]
186− f (If c true [false@If{}]) = ["if (" ++ cond c ++ "){"] ++ fi true ++ ["} else " ++ f1] ++ fs
187− where f1:fs = f false
188− f (If c true false) = ["if (" ++ cond c ++ "){"] ++ fi true ++ ["} else {"] ++ fi false ++ ["}"]
189− f (While c x) = ["while (" ++ cond c ++ "){"] ++ fi x ++ ["}"]
190−
191−optimiseC :: [C o] -> [C o]
192−optimiseC = f []
193− where
194− f known [] = []
195− f known (Abort x:_) = [Abort x]
196− f known (Next:xs) = Next : f [] xs
197− f known (Stmt x:xs) = Stmt x : f known xs
198− f known (If cond true false : xs)
199− | implies known cond = f known $ true ++ xs
200− | otherwise = If cond (f (cond:known) true) (f known false) : f [] xs -- might be a Next in body
201− f known (While cond x : xs) = While cond (f (cond:known) x) : f [] xs
gen/Main.hsdeleted−70
@@ -1,70 +0,0 @@
1−
2−module Main(main) where
3−
4−import Lib
5−import Parser
6−import Control.Monad.Extra
7−import System.Process.Extra
8−import System.Directory
9−import Data.Maybe
10−import System.IO.Extra
11−import System.FilePath
12−import System.Info.Extra
13−
14−
15−main :: IO ()
16−main = do
17− forM_ examples $ \(b, src) -> do
18− putStrLn ""
19− putStrLn src
20− let (res, trail, err) = run html src
21− print res
22− print (trail, err)
23− putStrLn $ rerender src res
24− writeFileChanged "test.h" $ unlines $ compile html
25− buildRule ["test.h","test.c"] ["gen" <.> (if isWindows then "exe" else "")] $
26− system_ "gcc test.c -o gen -Werror -Wall -Wextra -DTEST"
27− system_ "gen"
28−
29−writeFileChanged :: FilePath -> String -> IO ()
30−writeFileChanged file new = do
31− old <- ifM (doesFileExist file) (readFile' file) (pure "")
32− when (old /= new) $
33− writeFile file new
34−
35−buildRule :: [FilePath] -> [FilePath] -> IO () -> IO ()
36−buildRule from to act = do
37− let modTime x = ifM (doesFileExist x) (Just <$> getModificationTime x) (pure Nothing)
38− from <- mapM modTime from
39− to <- mapM modTime to
40− when (any isNothing (from ++ to) || maximum from >= minimum to)
41− act
42−
43−examples :: [(Bool, String)]
44−examples = take 1
45− [(True, "<test foo='123 xyz'><inner /> value</test>")
46− ,(True, "<test />")
47− ,(True, "<test id='bob'>here<extra/>there</test>")
48− ,(True, "<test /><close />")
49− ,(True, "<test /><!-- comment > --><close />")
50− ,(True, "<test id=\"bob value\" another-attr=\"test with <\">here </test> more text at the end<close />")
51− ,(False, "<test></more>")
52− ,(False, "<test")
53− ,(True, "<?xml version=\"1.1\"?>\n<greeting>Hello, world!</greeting>")
54− ]
55−
56−rerender :: String -> [(Int, Out)] -> String
57−rerender o = f
58− where
59− sub s e = take (e - s) $ drop s o
60−
61− f ((_,Tag):(_,TagComment):xs) = f xs
62− f ((_,Tag):xs) = "<" ++ f xs
63− f ((_,TagOpen):xs) = ">" ++ f xs
64− f ((_,TagOpenClose):xs) = "/>" ++ f xs
65− f ((s,NameStart):(e,NameEnd):(_,TagClose):xs) = "/" ++ sub s e ++ ">" ++ f xs
66− f ((s,NameStart):(e,NameEnd):xs) = sub s e ++ " " ++ f xs
67− f ((s,QuoteStart):(e,QuoteEnd):xs) = "=\"" ++ sub s e ++ "\" " ++ f xs
68− f ((_,AttribsStart):xs) = f xs
69− f ((_,AttribsEnd):xs) = f xs
70− f [] = []
gen/Parser.hsdeleted−76
@@ -1,76 +0,0 @@
1−
2−module Parser(Out(..), html) where
3−
4−import Lib
5−
6−data Out
7− = NameStart | NameEnd
8− | QuoteStart | QuoteEnd
9− | AttribsStart | AttribsEnd
10− | Tag
11− | TagComment | TagOpen | TagClose | TagOpenClose
12− deriving Show
13−
14−is lower upper x = x >= lower && x <= upper
15−isName1 x = x == ':' || x == '_' || is 'a' 'z' x || is 'A' 'Z' x
16−isName x = isName1 x || x == '-' || x == '.' || is '0' '9' x
17−isSpace x = x == ' ' || x == '\t' || x == '\r' || x == '\n'
18−
19−whitespace = many $ match isSpace
20−
21−name = do
22− out NameStart
23− match isName1
24− many $ match isName
25− out NameEnd
26− whitespace
27−
28−attrib = do
29− name
30− lit "=" `choice` abort "Expected = in attribute but missing"
31− whitespace
32− let quoted x = do
33− lit [x]
34− out QuoteStart
35− many $ match (/= x)
36− out QuoteEnd
37− lit [x] `choice` abort ("Couldn't find closing attribute quote (expected " ++ [x] ++ ")")
38− whitespace
39− choices
40− [quoted '\"'
41− ,quoted '\''
42− ,abort "Invalid attribute, expected quote (either ' or \")"]
43−
44−attribs = do
45− out AttribsStart
46− many attrib
47− out AttribsEnd
48−
49−tag = do
50− out Tag
51− lit "<"
52− let name_ = name `choice` abort "Missing tag name"
53− choices
54− [do lit "!--"
55− many $ match (/= '-')
56− -- FIXME: Not correct!!!
57− lit "-->"
58− out TagComment
59− ,do lit "?"
60− name_
61− attribs
62− lit "?>"
63− out TagOpenClose
64− ,do lit "/"
65− name_
66− lit ">"
67− out TagClose
68− ,do name_
69− attribs
70− choice
71− (lit "/>" >> out TagOpenClose)
72− (lit ">" >> out TagOpen)
73− ]
74−
75−
76−html = many $ choice tag $ match $ const True
gen/test.cdeleted−488
@@ -1,488 +0,0 @@
1−
2−#include "../cbits/hexml.h"
3−#include <stdlib.h>
4−#include <string.h>
5−#include <assert.h>
6−#include <stdio.h>
7−
8−/////////////////////////////////////////////////////////////////////
9−// TYPES
10−
11−static inline int end(str s) { return s.start + s.length; }
12−
13−static inline str start_length(int32_t start, int32_t length)
14−{
15− assert(start >= 0 && length >= 0);
16− str res;
17− res.start = start;
18− res.length = length;
19− return res;
20−}
21−
22−static inline str start_end(int32_t start, int32_t end)
23−{
24− return start_length(start, end - start);
25−}
26−
27−typedef struct
28−{
29− int size;
30− int used;
31− attr* attrs; // dynamically allocated buffer
32− attr* alloc; // what to call free on
33−} attr_buffer;
34−
35−typedef struct
36−{
37− // have a cursor at the front, which is all the stuff I have written out, final
38− // have a cursor at the end, which is stack scoped, so children write out, then I do
39− // when you commit, you copy over from end to front
40−
41− // nodes
42− int size;
43− int used_front; // front entries, stored for good
44− int used_back; // back entries, stack based, copied into front on </close>
45− // back entries which haven't yet been closed have nodes.length == -1
46− node* nodes; // dynamically allocated buffer
47− node* alloc; // what to call free on
48−} node_buffer;
49−
50−
51−struct document
52−{
53− const char* body; // pointer to initial argument, not owned by us
54− int body_len; // length of the body
55−
56− char* error_message;
57− node_buffer nodes;
58− attr_buffer attrs;
59−};
60−
61−
62−/////////////////////////////////////////////////////////////////////
63−// RENDER CODE
64−
65−typedef struct
66−{
67− const document* d; // Document, used to get at the body
68− char* buffer; // Buffer we are writing too
69− int length; // Length of buffer, do not write past the end.
70− // No character is reserved for a NULL terminator.
71− int cursor; // Cursor of where we are writing now
72−} render;
73−
74−static inline void render_char(render* r, char c)
75−{
76− if (r->cursor < r->length)
77− r->buffer[r->cursor] = c;
78− r->cursor++;
79−}
80−
81−static inline void bound(int idx, int mn, int mx)
82−{
83− assert(idx >= mn && idx <= mx);
84−}
85−
86−static inline void bound_str(str s, int mn, int mx)
87−{
88− assert(s.length >= 0);
89− bound(s.start, mn, mx);
90− bound(end(s), mn, mx);
91−}
92−
93−static void render_str(render* r, str s)
94−{
95− bound_str(s, 0, r->d->body_len);
96− for (int i = 0; i < s.length; i++)
97− render_char(r, r->d->body[s.start + i]);
98−}
99−
100−static void render_tag(render* r, const node* n);
101−
102−static void render_content(render* r, const node* n)
103−{
104− bound_str(n->inner, 0, r->d->body_len);
105− bound_str(n->nodes, 0, r->d->nodes.used_front);
106− bound_str(n->attrs, 0, r->d->attrs.used);
107−
108− int done = n->inner.start;
109− for (int i = 0; i < n->nodes.length; i++)
110− {
111− node* x = &r->d->nodes.nodes[n->nodes.start + i];
112− render_str(r, start_end(done, x->outer.start));
113− done = end(x->outer);
114− render_tag(r, x);
115− }
116− render_str(r, start_end(done, end(n->inner)));
117−}
118−
119−static void render_tag(render* r, const node* n)
120−{
121− render_char(r, '<');
122− render_str(r, n->name);
123− for (int i = 0; i < n->attrs.length; i++)
124− {
125− attr* x = &r->d->attrs.attrs[n->attrs.start + i];
126− render_char(r, ' ');
127− render_str(r, x->name);
128− render_char(r, '=');
129− render_char(r, '\"');
130− render_str(r, x->value);
131− render_char(r, '\"');
132− }
133− render_char(r, '>');
134− render_content(r, n);
135− render_char(r, '<');
136− render_char(r, '/');
137− render_str(r, n->name);
138− render_char(r, '>');
139−}
140−
141−int hexml_node_render(const document* d, const node* n, char* buffer, int length)
142−{
143− render r;
144− r.d = d;
145− r.buffer = buffer;
146− r.length = length;
147− r.cursor = 0;
148− // The root node (and only the root node) has an empty length, so just render its innards
149− if (n->name.length == 0)
150− render_content(&r, n);
151− else
152− render_tag(&r, n);
153− return r.cursor;
154−}
155−
156−
157−/////////////////////////////////////////////////////////////////////
158−// NOT THE PARSER
159−
160−char* hexml_document_error(const document* d){return d->error_message;}
161−node* hexml_document_node(const document* d){return &d->nodes.nodes[0];}
162−
163−void hexml_document_free(document* d)
164−{
165− free(d->error_message);
166− free(d->nodes.alloc);
167− free(d->attrs.alloc);
168− free(d);
169−}
170−
171−node* hexml_node_children(const document* d, const node* n, int* res)
172−{
173− *res = n->nodes.length;
174− return &d->nodes.nodes[n->nodes.start];
175−}
176−
177−attr* hexml_node_attributes(const document* d, const node* n, int* res)
178−{
179− *res = n->attrs.length;
180− return &d->attrs.attrs[n->attrs.start];
181−}
182−
183−attr* hexml_node_attribute(const document* d, const node* n, const char* s, int slen)
184−{
185− if (slen == -1) slen = (int) strlen(s);
186− const int limit = end(n->attrs);
187− for (int i = n->attrs.start; i < limit; i++)
188− {
189− attr* r = &d->attrs.attrs[i];
190− if (r->name.length == slen && memcmp(s, &d->body[r->name.start], slen) == 0)
191− return r;
192− }
193− return NULL;
194−}
195−
196−// Search for given strings within a node
197−node* hexml_node_child(const document* d, const node* parent, const node* prev, const char* s, int slen)
198−{
199− if (slen == -1) slen = (int) strlen(s);
200− int i = prev == NULL ? parent->nodes.start : (int) (prev + 1 - d->nodes.nodes);
201− const int limit = end(parent->nodes);
202− for (; i < limit; i++)
203− {
204− node* r = &d->nodes.nodes[i];
205− if (r->name.length == slen && memcmp(s, &d->body[r->name.start], slen) == 0)
206− return r;
207− }
208− return NULL;
209−}
210−
211−/////////////////////////////////////////////////////////////////////
212−// PARSING CODE
213−
214−static inline node* node_alloc(node_buffer* b)
215−{
216− int space = b->size - b->used_back - b->used_front;
217− if (space < 1)
218− {
219− int size2 = (b->size + 1000) * 2;
220− node* buf2 = malloc(size2 * sizeof(node));
221− assert(buf2);
222− memcpy(buf2, b->nodes, b->used_front * sizeof(node));
223− memcpy(&buf2[size2 - b->used_back], &b->nodes[b->size - b->used_back], b->used_back * sizeof(node));
224− free(b->alloc);
225− b->size = size2;
226− b->nodes = buf2;
227− b->alloc = buf2;
228− }
229− b->used_back++;
230− return &b->nodes[b->size - b->used_back];
231−}
232−
233−// buffer[from .. from+n] = reverse (buffer[from .. from+n])
234−// The ranges completely overlap
235−static inline void reverse(node* buffer, int from, int n)
236−{
237− for (int to = from + n - 1; to > from; from++, to--)
238− {
239− node temp = buffer[to];
240− buffer[to] = buffer[from];
241− buffer[from] = temp;
242− }
243−}
244−
245−// buffer[to .. to+n] = reverse (buffer[from .. from+n])
246−// The ranges may not overlap
247−static inline void memcpy_reverse(node* buffer, int from, int to, int n)
248−{
249− int end = from + n - 1;
250− for (int i = 0; i < n; i++)
251− buffer[to+i] = buffer[end-i];
252−}
253−
254−// buffer[to .. to+n] = reverse (buffer[from .. from+n])
255−// The ranges may overlap
256−static inline void memmove_reverse(node* buffer, int from, int to, int n)
257−{
258− assert(from >= to); // always the case in my usages
259− if (to + n < from)
260− memcpy_reverse(buffer, from, to, n);
261− else
262− {
263− int overlap = from - to;
264− memcpy_reverse(buffer, from, to + n - overlap, overlap);
265− reverse(buffer, to, n - overlap);
266− }
267−}
268−
269−static inline str node_commit(node_buffer* b)
270−{
271− // first search for an "open" node (nodes.length == -1)
272− int open;
273− for (open = b->size - b->used_back; open < b->size; open++)
274− {
275− if (b->nodes[open].nodes.length == -1)
276− break;
277− }
278−
279− int n = open - (b->size - b->used_back);
280− memmove_reverse(b->nodes, b->size - b->used_back, b->used_front, n);
281− b->used_back -= n;
282− b->used_front += n;
283− return start_length(b->used_front - n, n);
284−}
285−
286−static inline attr* attr_alloc(attr_buffer* b)
287−{
288− // Ensure there is at least one node left
289− int space = b->size - b->used;
290− if (space < 1)
291− {
292− int size2 = (b->size + 1000) * 2;
293− attr* buf2 = malloc(size2 * sizeof(attr));
294− assert(buf2);
295− memcpy(buf2, b->attrs, b->used * sizeof(attr));
296− free(b->alloc);
297− b->size = size2;
298− b->attrs = buf2;
299− b->alloc = buf2;
300− }
301− b->used++;
302− return &b->attrs[b->used - 1];
303−}
304−
305−static void set_error(document* d, const char* msg)
306−{
307− if (d->error_message != NULL) return; // keep the first error message
308− d->error_message = malloc(strlen(msg)+1);
309− assert(d->error_message);
310− strcpy(d->error_message, msg);
311−}
312−
313−static inline str gap(const char* ref, const char* start, const char* end)
314−{
315− return start_end(start - ref, end - ref);
316−}
317−
318−#define P_Abort(x) return x
319−#define P_Tag tag_start = p;
320−#define P_NameStart name_start = p
321−#define P_NameEnd name_end = p
322−#define P_AttribsStart \
323− me = node_alloc(&d->nodes); \
324− me->attrs.start = d->attrs.used; \
325− me->name = gap(d->body, name_start, name_end);
326−#define P_AttribsEnd \
327− me->attrs = start_end(me->attrs.start, d->attrs.used);
328−#define P_QuoteStart quote_start = p
329−#define P_QuoteEnd \
330− attr* attr = attr_alloc(&d->attrs); \
331− attr->name = gap(d->body, name_start, name_end); \
332− attr->value = gap(d->body, quote_start, p);
333−#define P_TagComment \
334− // No action required - comments just flow as normal text
335−#define P_TagOpen \
336− me->nodes.length = -1; \
337− me->inner = gap(d->body, p, p); \
338− me->outer = gap(d->body, tag_start, tag_start);
339−#define P_TagClose \
340− str nodes = node_commit(&d->nodes); \
341− if (d->nodes.used_back == 0) \
342− return "Closing tag with no openning"; \
343− me = &d->nodes.nodes[d->nodes.size - d->nodes.used_back]; \
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"; \
348− me->inner = gap(d->body, &d->body[me->inner.start], tag_start); \
349− me->outer = gap(d->body, &d->body[me->outer.start], p);
350−#define P_TagOpenClose \
351− me->nodes = start_end(0, 0); \
352− me->inner = gap(d->body, p, p); \
353− me->outer = gap(d->body, tag_start, p);
354−
355−// Given the parsed string, return either NULL (success) or an error message (failure)
356−static const char* parser(const char* p, document* d)
357−{
358− const char *tag_start; // Where the tag starts [<foo
359− const char *name_start, *name_end; // Where a name is <[foo] or <foo [bar]=
360− const char *quote_start; // Where an attribute quote is <foo bar='[123'>
361− node* me; // The current node I'm working on
362−# include "test.h"
363− return NULL;
364−}
365−
366−// Based on looking at ~50Kb XML documents, they seem to have ~700 attributes
367−// and ~300 nodes, so size appropriately to cope with that.
368−typedef struct
369−{
370− document document;
371− attr attrs[1000];
372− node nodes[500];
373−} buffer;
374−
375−document* hexml_document_parse(const char* s, int slen)
376−{
377− if (slen == -1) slen = (int) strlen(s);
378− assert(s[slen] == 0);
379−
380− buffer* buf = malloc(sizeof(buffer));
381− assert(buf);
382− document* d = &buf->document;
383− d->body = s;
384− d->body_len = slen;
385− d->error_message = NULL;
386− d->attrs.size = 1000;
387− d->attrs.used = 0;
388− d->attrs.attrs = buf->attrs;
389− d->attrs.alloc = NULL;
390− d->nodes.size = 500;
391− d->nodes.used_back = 0;
392− d->nodes.used_front = 1;
393− d->nodes.nodes = buf->nodes;
394− d->nodes.alloc = NULL;
395−
396− d->nodes.nodes[0].name = start_length(0, 0);
397− d->nodes.nodes[0].outer = start_length(0, slen);
398− d->nodes.nodes[0].inner = start_length(0, slen);
399− d->nodes.nodes[0].attrs = start_length(0, 0);
400−
401− // Introduce an intermediate result, otherwise behaviour is undefined
402− // because there is no guaranteed ordering between LHS and RHS evaluation
403− const char* res = parser(s, d);
404− if (res != NULL)
405− set_error(d, res);
406− else
407− {
408− d->nodes.nodes[0].nodes = node_commit(&d->nodes);
409− if (d->nodes.used_back != 0)
410− d->error_message = strdup("used_back failure");
411− }
412− return d;
413−}
414−
415−/////////////////////////////////////////////////////////////////////
416−// TEST HARNESS (temporary)
417−
418−#ifdef TEST
419−
420−static char* resolve(const document* d, str loc)
421−{
422− char* buf = malloc(loc.length + 3);
423− memcpy(&buf[1], &d->body[loc.start], loc.length);
424− buf[0] = '\"';
425− buf[loc.length+1] = '\"';
426− buf[loc.length+2] = 0;
427− return buf;
428−}
429−
430−static char* disp(str loc)
431−{
432− char* buf = malloc(100);
433− if (loc.length == 0)
434− strcpy(buf, "[]");
435− else
436− sprintf(buf, "[%i..%i]", loc.start, end(loc)-1);
437− return buf;
438−}
439−
440−int main()
441−{
442− setbuf(stdout, NULL);
443− const char* example = "<test foo='123 xyz' bar='t'><inner /> value</test>";
444− document* d = hexml_document_parse(example, -1);
445− if (d->error_message != NULL)
446− printf("Parse failure: %s\n", d->error_message);
447− else
448− {
449− char buf[1000];
450− int len = hexml_node_render(d, hexml_document_node(d), buf, sizeof(buf));
451− buf[len] = 0;
452− printf("Result = %s\n", buf);
453− printf("Used nodes = %i, attributes = %i\n", d->nodes.used_front, d->attrs.used);
454−
455− for (int i = 0; i < d->attrs.used; i++)
456− printf("Attr %i: %s=%s\n", i, resolve(d, d->attrs.attrs[i].name), resolve(d, d->attrs.attrs[i].value));
457− for (int i = 0; i < d->nodes.used_front; i++)
458− {
459− printf("Node %i: %s attrs=%s, nodes=%s\n", i, resolve(d, d->nodes.nodes[i].name), disp(d->nodes.nodes[i].attrs), disp(d->nodes.nodes[i].nodes));
460− printf(" inner %s\n outer %s\n", resolve(d, d->nodes.nodes[i].inner), resolve(d, d->nodes.nodes[i].outer));
461− }
462− }
463− hexml_document_free(d);
464− return 0;
465−}
466−
467−#endif
468−
469−/*
470−void main()
471−{
472− setbuf(stdout, NULL);
473− char* body = "<?xml version=\"1.0\" lang=\"ja\"?><foo><bar baz=\"qux\">quux</bar><bar baz=\"bar\">hoge</bar><piyo>&gt;piyopiyo&lt;</piyo></foo><foo2 />";
474− body = "<test>X</test> ";
475− document* doc = document_parse(body, -1);
476− char* err = document_error(doc);
477− if (err == NULL)
478− {
479− int len = document_render(doc, NULL, 0);
480− char* s = malloc(len + 1);
481− document_render(doc, s, len);
482− s[len] = 0;
483− printf("Parse successful, %s\n", s);
484− }
485− else
486− printf("Parse failed, %s\n", err);
487−}
488−*/
ghci.bat+1−2
@@ -1,2 +1 @@
1−gcc -O2 -std=c99 -c cbits\hexml.c -o cbits\hexml.o -Wall -Wextra -Werror && ghci.exe cbits\hexml.o %*
1+gcc -O3 -std=c99 -c cbits\hexml.c -o cbits\hexml.o && 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 %*
go.batnew file+2
@@ -0,0 +1,2 @@
1+rem sos -p .*.c -c "go 2>&1 | head -n12"
2+gcc -c hexml.c -O3 && gcc -c main.c -O3 && gcc -o hexml hexml.o main.o && hexml
hexml.cabaldeleted−55
@@ -1,55 +0,0 @@
1−cabal-version: >= 1.18
2−build-type: Simple
3−name: hexml
4−version: 0.3.4
5−license: BSD3
6−x-license: BSD-3-Clause OR Apache-2.0
7−license-file: LICENSE
8−category: XML
9−author: Neil Mitchell <ndmitchell@gmail.com>
10−maintainer: Neil Mitchell <ndmitchell@gmail.com>
11−copyright: Neil Mitchell 2016-2023
12−synopsis: XML subset DOM parser
13−description:
14− An XML DOM-style parser, that only parses a subset of XML, but is designed to be fast.
15−homepage: https://github.com/ndmitchell/hexml#readme
16−bug-reports: https://github.com/ndmitchell/hexml/issues
17−tested-with: GHC==9.6, GHC==9.4, GHC==9.2, GHC==9.0, GHC==8.10, GHC==8.8
18−extra-doc-files:
19− README.md
20− CHANGES.txt
21−extra-source-files:
22− cbits/*.h
23− cbits/*.c
24−
25−source-repository head
26− type: git
27− location: https://github.com/ndmitchell/hexml.git
28−
29−library
30− hs-source-dirs: src
31− default-language: Haskell2010
32−
33− build-depends:
34− base >= 4.5 && < 5,
35− bytestring,
36− extra >= 0.3
37−
38− c-sources: cbits/hexml.c
39− include-dirs: cbits
40− includes: hexml.h
41− install-includes: hexml.h
42− cc-options: -std=c99
43−
44− exposed-modules:
45− Text.XML.Hexml
46−
47−test-suite hexml-test
48− type: exitcode-stdio-1.0
49− main-is: src/Main.hs
50− default-language: Haskell2010
51−
52− build-depends:
53− base,
54− bytestring,
55− hexml
main.cnew file+22
@@ -0,0 +1,22 @@
1+#include "hexml.h"
2+#include <stdio.h>
3+#include <malloc.h>
4+
5+void main()
6+{
7+ setbuf(stdout, NULL);
8+ char* body = "<?xml version=\"1.0\" lang=\"ja\"?><foo><bar baz=\"qux\">quux</bar><bar baz=\"bar\">hoge</bar><piyo>&gt;piyopiyo&lt;</piyo></foo><foo2 />";
9+ body = "<test>X</test> ";
10+ document* doc = document_parse(body, -1);
11+ char* err = document_error(doc);
12+ if (err == NULL)
13+ {
14+ int len = document_render(doc, NULL, 0);
15+ char* s = malloc(len + 1);
16+ document_render(doc, s, len);
17+ s[len] = 0;
18+ printf("Parse successful, %s\n", s);
19+ }
20+ else
21+ printf("Parse failed, %s\n", err);
22+}
src/Main.hs+19−52
@@ -2,83 +2,50 @@
2
2
3module Main(main) where
3module Main(main) where
4
4
5−import Text.XML.Hexml as X
5+import Text.XML.Hexml
6import qualified Data.ByteString.Char8 as BS
6import qualified Data.ByteString.Char8 as BS
7import Control.Monad
7import Control.Monad
8import Data.Monoid
8import Data.Monoid
9import Data.Char
9import Data.Char
10−import Prelude
11
10
12
11
13examples :: [(Bool, BS.ByteString)]
12examples :: [(Bool, BS.ByteString)]
14examples =
13examples =
15− [(True, "<test id='bob'>here<extra/>there</test>")
14+ [(True, "<test id=bob>here<extra/>there</test>")
16 ,(True, "<test /><close />")
15 ,(True, "<test /><close />")
17− ,(True, "<test /><!-- comment > --><close />")
18 ,(True, "<test id=\"bob value\" another-attr=\"test with <\">here </test> more text at the end<close />")
16 ,(True, "<test id=\"bob value\" another-attr=\"test with <\">here </test> more text at the end<close />")
19 ,(False, "<test></more>")
17 ,(False, "<test></more>")
20 ,(False, "<test")
18 ,(False, "<test")
21− ,(True, "<?xml version=\"1.1\"?>\n<greeting>Hello, world!</greeting>")
22− ,(True, "<foo bar.baz=\"qux\"></foo>")
23 ]
19 ]
24
20
25main :: IO ()
21main :: IO ()
26main = do
22main = do
27− forM_ examples $ \(parses, src) ->
23+ forM_ examples $ \(parses, src) -> do
24+ print src
28− case parse src of
25+ case documentParse src of
29 Left err -> when parses $ fail $ "Unexpected parse failure, " ++ show err
26 Left err -> when parses $ fail $ "Unexpected parse failure, " ++ show err
30 Right doc -> do
27 Right doc -> do
31 unless parses $ fail "Unexpected parse success"
28 unless parses $ fail "Unexpected parse success"
32− checkFind doc
29+ let r = documentRender doc
33− let r = render doc
30+ print r
31+ print $ rerender doc
34− r === rerender doc
32+ when (r /= rerender doc) $ fail "Different rerender"
35− let Right d = parse r
33+ let Right d = documentParse r
36− r === render d
34+ when (r /= documentRender d) $ fail "Different after rerendering"
37
35
38− let Right doc = parse "<test id=\"1\" extra=\"2\" />\n<test id=\"2\" /><b><test id=\"3\" /></b><test id=\"4\" /><test />"
39− map name (children doc) === ["test","test","b","test","test"]
40− location (children doc !! 2) === (2,16)
41− length (childrenBy doc "test") === 4
42− length (childrenBy doc "b") === 1
43− length (childrenBy doc "extra") === 0
44− attributes (head $ children doc) === [Attribute "id" "1", Attribute "extra" "2"]
45− map (`attributeBy` "id") (childrenBy doc "test") === map (fmap (Attribute "id")) [Just "1", Just "2", Just "4", Nothing]
46
36
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' />"
49−
50− let attrs = ["usd:jpy","test","extra","more","stuff","jpy:usd","xxx","xxxx"]
51− Right doc <- pure $ parse $ "<test " <> BS.unwords [x <> "='" <> x <> "'" | x <- attrs] <> ">middle</test>"
52− [c] <- pure $ childrenBy doc "test"
53− forM_ attrs $ \a -> attributeBy c a === Just (Attribute a a)
54− forM_ ["missing","gone","nothing"] $ \a -> attributeBy c a === Nothing
55− putStrLn "\nSuccess"
56−
57−
58−checkFind :: Node -> IO ()
59−checkFind n = do
60− forM_ (attributes n) $ \a -> attributeBy n (attributeName a) === Just a
61− attributeBy n "xxx" === Nothing
62− let cs = children n
63− forM_ ("xxx":map name cs) $ \c ->
64− map outer (filter ((==) c . name) cs) === map outer (childrenBy n c)
65− mapM_ checkFind $ children n
66−
67−
68−a === b = if a == b then putChar '.' else fail $ "mismatch, " ++ show a ++ " /= " ++ show b
69−
70−rerender :: Node -> BS.ByteString
37+rerender :: Document -> BS.ByteString
71−rerender = inside
38+rerender = contents . documentNode
72 where
39 where
73− inside x = BS.concat $ map (either validStr node) $ contents x
40+ contents x = BS.concat $ map (either validStr node) $ nodeContents x
74− node x = "<" <> BS.unwords (validName (name x) : map attr (attributes x)) <> ">" <>
41+ node x = "<" <> BS.unwords (validName (nodeName x) : map attr (nodeAttributes x)) <> ">" <>
75− inside x <>
42+ contents x <>
76− "</" <> name x <> ">"
43+ "</" <> nodeName x <> ">"
77 attr (Attribute a b) = validName a <> "=\"" <> validAttr b <> "\""
44 attr (Attribute a b) = validName a <> "=\"" <> validAttr b <> "\""
78
45
79− validName x | BS.all (\x -> isAlphaNum x || x `elem` ("-.:_" :: String)) x = x
46+ validName x | BS.all (\x -> isAlphaNum x || x `elem` ("-:_" :: String)) x = x
80 | otherwise = error "Invalid name"
47 | otherwise = error "Invalid name"
81 validAttr x | BS.notElem '\"' x = x
48 validAttr x | BS.notElem '\"' x = x
82 | otherwise = error "Invalid attribute"
49 | otherwise = error "Invalid attribute"
83− validStr x | BS.notElem '<' x || BS.isInfixOf "<!--" x = x
50+ validStr x | BS.notElem '<' x = x
84 | otherwise = error $ show ("Invalid string", x)
51 | otherwise = error $ show ("Invalid string", x)
src/Text/XML/Hexml.hs+79−131
@@ -1,16 +1,13 @@
1−{-# LANGUAGE RecordWildCards, BangPatterns #-}
1+{-# LANGUAGE RecordWildCards #-}
2
2
3−-- | A module for fast first-approximation parsing of XML.
4−-- Note that entities, e.g. @&amp;@, are not expanded.
5module Text.XML.Hexml(
3module Text.XML.Hexml(
6− Node, Attribute(..),
4+ Document, Node, Attribute(..),
7− parse, render,
5+ documentParse, documentRender, documentNode,
8− location, name, inner, outer,
6+ nodeName, nodeInner, nodeOuter,
9− attributes, children, contents,
7+ nodeAttributes, nodeChildren, nodeContents,
10− attributeBy, childrenBy
8+ nodeAttributeBy, nodeChildrenBy
11 ) where
9 ) where
12
10
13−import Control.Applicative
14import Control.Monad
11import Control.Monad
15import Data.Int
12import Data.Int
16import Foreign.C
13import Foreign.C
@@ -24,7 +21,6 @@ import Data.Tuple.Extra
24import qualified Data.ByteString.Char8 as BS
21import qualified Data.ByteString.Char8 as BS
25import qualified Data.ByteString.Unsafe as BS
22import qualified Data.ByteString.Unsafe as BS
26import qualified Data.ByteString.Internal as BS
23import qualified Data.ByteString.Internal as BS
27−import Prelude
28
24
29data CDocument
25data CDocument
30data CNode
26data CNode
@@ -45,65 +41,53 @@ instance Storable Str where
45 peek p = Str <$> peekByteOff p 0 <*> peekByteOff p 4
41 peek p = Str <$> peekByteOff p 0 <*> peekByteOff p 4
46 poke p Str{..} = pokeByteOff p 0 strStart >> pokeByteOff p 4 strLength
42 poke p Str{..} = pokeByteOff p 0 strStart >> pokeByteOff p 4 strLength
47
43
48−foreign import ccall hexml_document_parse :: CString -> CInt -> IO (Ptr CDocument)
44+foreign import ccall document_parse :: CString -> CInt -> IO (Ptr CDocument)
49−foreign import ccall hexml_document_free :: Ptr CDocument -> IO ()
45+foreign import ccall document_free :: Ptr CDocument -> IO ()
50−foreign import ccall "&hexml_document_free" hexml_document_free_funptr :: FunPtr (Ptr CDocument -> IO ())
46+foreign import ccall "&document_free" document_free_funptr :: FunPtr (Ptr CDocument -> IO ())
51−foreign import ccall hexml_node_render :: Ptr CDocument -> Ptr CNode -> CString -> CInt -> IO CInt
47+foreign import ccall document_render :: Ptr CDocument -> CString -> CInt -> IO CInt
52−foreign import ccall unsafe hexml_document_error :: Ptr CDocument -> IO CString
48+foreign import ccall document_error :: Ptr CDocument -> IO CString
53−foreign import ccall unsafe hexml_document_node :: Ptr CDocument -> IO (Ptr CNode)
49+foreign import ccall document_node :: Ptr CDocument -> IO (Ptr CNode)
54
50
55−foreign import ccall unsafe hexml_node_children :: Ptr CDocument -> Ptr CNode -> Ptr CInt -> IO (Ptr CNode)
51+foreign import ccall node_children :: Ptr CDocument -> Ptr CNode -> Ptr CInt -> IO (Ptr CNode)
56−foreign import ccall unsafe hexml_node_attributes :: Ptr CDocument -> Ptr CNode -> Ptr CInt -> IO (Ptr CAttr)
52+foreign import ccall node_attributes :: Ptr CDocument -> Ptr CNode -> Ptr CInt -> IO (Ptr CAttr)
57
53
58−foreign import ccall unsafe hexml_node_child :: Ptr CDocument -> Ptr CNode -> Ptr CNode -> CString -> CInt -> IO (Ptr CNode)
54+foreign import ccall node_childBy :: Ptr CDocument -> Ptr CNode -> Ptr CNode -> CString -> CInt -> IO (Ptr CNode)
59−foreign import ccall unsafe hexml_node_attribute :: Ptr CDocument -> Ptr CNode -> CString -> CInt -> IO (Ptr CAttr)
55+foreign import ccall node_attributeBy :: Ptr CDocument -> Ptr CNode -> CString -> CInt -> IO (Ptr CAttr)
56+
57+data Document = Document BS.ByteString (ForeignPtr CDocument)
60
58
61−-- | A node in an XML document, created by 'parse', then calling functions such
62−-- as 'children' on that initial 'Node'.
63data Node = Node BS.ByteString (ForeignPtr CDocument) (Ptr CNode)
59data Node = Node BS.ByteString (ForeignPtr CDocument) (Ptr CNode)
64
60
65−-- | An XML attribute, comprising of a name and a value. As an example,
66−-- @hello=\"world\"@ would produce @Attribute \"hello\" \"world\"@.
67−data Attribute = Attribute
61+data Attribute = Attribute BS.ByteString BS.ByteString deriving Show
68− {attributeName :: BS.ByteString
62+
69− ,attributeValue :: BS.ByteString
63+instance Show Document where
70− } deriving (Show, Eq, Ord)
64+ show d = "Document " ++ show (BS.unpack $ nodeOuter $ documentNode d)
71
65
72instance Show Node where
66instance Show Node where
73− show d = "Node " ++ show (BS.unpack $ outer d)
67+ show d = "Node " ++ show (BS.unpack $ nodeOuter d)
74−
68+
75−
69+
76−touchBS :: BS.ByteString -> IO ()
77−touchBS = touchForeignPtr . fst3 . BS.toForeignPtr
78−
79−
80−-- | Parse a ByteString as an XML document, returning a 'Left' error message, or a 'Right' document.
81−-- Note that the returned node will have a 'name' of @\"\"@, no 'attributes', and 'contents' as per the document.
82−-- Often the first child will be the @\<?xml ... ?\>@ element. For documents which comprise an XML node and a single
83−-- root element, use @'children' n !! 1@.
84−parse :: BS.ByteString -> Either BS.ByteString Node
70+documentParse :: BS.ByteString -> Either BS.ByteString Document
85−parse src = do
71+documentParse src = unsafePerformIO $ BS.unsafeUseAsCStringLen (src <> BS.singleton '\0') $ \(str, len) -> do
86− let src0 = src <> BS.singleton '\0'
87− unsafePerformIO $ BS.unsafeUseAsCStringLen src0 $ \(str, len) -> do
88− doc <- hexml_document_parse str (fromIntegral len - 1)
72+ doc <- document_parse str (fromIntegral len - 1)
89− err <- hexml_document_error doc
73+ err <- document_error doc
90− if err /= nullPtr then do
74+ if err /= nullPtr then do
91− bs <- BS.packCString =<< hexml_document_error doc
75+ bs <- BS.packCString =<< document_error doc
92− hexml_document_free doc
76+ document_free doc
93− pure $ Left bs
77+ return $ Left bs
94− else do
78+ else do
95− node <- hexml_document_node doc
96− doc <- newForeignPtr hexml_document_free_funptr doc
79+ doc <- newForeignPtr document_free_funptr doc
97− pure $ Right $ Node src0 doc node
80+ return $ Right $ Document src doc
98−
81+
99−-- | Given a node, rerender it to something with an equivalent parse tree.
100−-- Mostly useful for debugging - if you want the real source document use 'outer' instead.
101−render :: Node -> BS.ByteString
82+documentRender :: Document -> BS.ByteString
102−render (Node src doc n) = unsafePerformIO $ withForeignPtr doc $ \d -> do
83+documentRender (Document _ doc) = unsafePerformIO $ withForeignPtr doc $ \d -> do
103− i <- hexml_node_render d n nullPtr 0
84+ i <- document_render d nullPtr 0
104− res <- BS.create (fromIntegral i) $ \ptr -> void $ hexml_node_render d n (castPtr ptr) i
85+ BS.create (fromIntegral i) $ \ptr -> void $ document_render d (castPtr ptr) i
105− touchBS src
86+
106− pure res
87+
88+documentNode :: Document -> Node
89+documentNode d@(Document src doc) = unsafePerformIO $ withForeignPtr doc $ \d -> do
90+ Node src doc <$> document_node d
107
91
108applyStr :: BS.ByteString -> Str -> BS.ByteString
92applyStr :: BS.ByteString -> Str -> BS.ByteString
109applyStr bs Str{..} = BS.take (fromIntegral strLength) $ BS.drop (fromIntegral strStart) bs
93applyStr bs Str{..} = BS.take (fromIntegral strLength) $ BS.drop (fromIntegral strStart) bs
@@ -118,88 +102,52 @@ attrPeek :: BS.ByteString -> ForeignPtr CDocument -> Ptr CAttr -> Attribute
118attrPeek src doc a = unsafePerformIO $ withForeignPtr doc $ \_ -> do
102attrPeek src doc a = unsafePerformIO $ withForeignPtr doc $ \_ -> do
119 name <- applyStr src <$> peekElemOff (castPtr a) 0
103 name <- applyStr src <$> peekElemOff (castPtr a) 0
120 val <- applyStr src <$> peekElemOff (castPtr a) 1
104 val <- applyStr src <$> peekElemOff (castPtr a) 1
121− pure $ Attribute name val
105+ return $ Attribute name val
122−
106+
123−-- | Get the name of a node, e.g. @\<test /\>@ produces @\"test\"@.
124−name :: Node -> BS.ByteString
107+nodeName :: Node -> BS.ByteString
125−name = nodeBS 0
108+nodeName = nodeBS 0
126−
109+
127−-- | Get the inner text, from inside the tag, e.g. @\<test /\>@ produces @\"\"@
128−-- and @\<test\>hello\</test\>@ produces @\"hello\"@.
129−-- The result will have identical layout/spacing to the source document.
130−inner :: Node -> BS.ByteString
110+nodeInner :: Node -> BS.ByteString
131−inner = nodeBS 1
111+nodeInner = nodeBS 1
132−
112+
133−-- | Get the outer text, including the tag itself, e.g. @\<test /\>@ produces @\"\<test /\>\"@
134−-- and @\<test\>hello\</test\>@ produces @\"\<test\>hello\</test\>\"@.
135−-- The result will have identical layout/spacing to the source document.
136−outer :: Node -> BS.ByteString
113+nodeOuter :: Node -> BS.ByteString
137−outer = nodeBS 2
114+nodeOuter = nodeBS 2
138−
115+
139−-- | Get the contents of a node, including both the content strings (as 'Left', never blank) and
140−-- the direct child nodes (as 'Right').
141−-- If you only want the child nodes, use 'children'.
142−contents :: Node -> [Either BS.ByteString Node]
116+nodeContents :: Node -> [Either BS.ByteString Node]
143−contents n@(Node src _ _) = f (strStart inner) outers
117+nodeContents n@(Node src _ _) = f (strStart inner) outers
144 where
118 where
145− f i [] = string i (strEnd inner)
119+ f i [] = string i (strEnd inner) ++ []
146 f i ((x, n):xs) = string i (strStart x) ++ Right n : f (strEnd x) xs
120 f i ((x, n):xs) = string i (strStart x) ++ Right n : f (strEnd x) xs
147
121
148 string start end | start == end = []
122 string start end | start == end = []
149 | otherwise = [Left $ applyStr src $ Str start (end - start)]
123 | otherwise = [Left $ applyStr src $ Str start (end - start)]
150 inner = nodeStr 1 n
124 inner = nodeStr 1 n
151− outers = map (nodeStr 2 &&& id) $ children n
125+ outers = map (nodeStr 2 &&& id) $ nodeChildren n
152
126
153−-- | Get the direct child nodes of this node.
154−children :: Node -> [Node]
127+nodeChildren :: Node -> [Node]
155−children (Node src doc n) = unsafePerformIO $ withForeignPtr doc $ \d ->
128+nodeChildren (Node src doc n) = unsafePerformIO $ withForeignPtr doc $ \d -> do
156 alloca $ \count -> do
129 alloca $ \count -> do
157− res <- hexml_node_children d n count
130+ res <- node_children d n count
158 count <- fromIntegral <$> peek count
131 count <- fromIntegral <$> peek count
159− pure [Node src doc $ plusPtr res $ i*szNode | i <- [0..count-1]]
132+ return [Node src doc $ plusPtr res $ i*szNode | i <- [0..count-1]]
160
133
161−-- | Get the attributes of this node.
162−attributes :: Node -> [Attribute]
134+nodeAttributes :: Node -> [Attribute]
163−attributes (Node src doc n) = unsafePerformIO $ withForeignPtr doc $ \d ->
135+nodeAttributes (Node src doc n) = unsafePerformIO $ withForeignPtr doc $ \d -> do
164 alloca $ \count -> do
136 alloca $ \count -> do
165− res <- hexml_node_attributes d n count
137+ res <- node_attributes d n count
166 count <- fromIntegral <$> peek count
138 count <- fromIntegral <$> peek count
167− pure [attrPeek src doc $ plusPtr res $ i*szAttr | i <- [0..count-1]]
139+ return [attrPeek src doc $ plusPtr res $ i*szAttr | i <- [0..count-1]]
168−
140+
169−-- | Get the direct children of this node which have a specific name.
170−-- A more efficient version of:
171−--
172−-- > childrenBy p s = filter (\n -> name n == s) $ children p
173−childrenBy :: Node -> BS.ByteString -> [Node]
141+nodeChildrenBy :: Node -> BS.ByteString -> [Node]
174−childrenBy (Node src doc n) str = go nullPtr
142+nodeChildrenBy (Node src doc n) str = go nullPtr
175 where
143 where
176 go old = unsafePerformIO $ withForeignPtr doc $ \d ->
144 go old = unsafePerformIO $ withForeignPtr doc $ \d ->
177 BS.unsafeUseAsCStringLen str $ \(bs, len) -> do
145 BS.unsafeUseAsCStringLen str $ \(bs, len) -> do
178− r <- hexml_node_child d n old bs $ fromIntegral len
146+ r <- node_childBy d n old bs $ fromIntegral len
179− touchBS src
180− pure $ if r == nullPtr then [] else Node src doc r : go r
147+ return $ if r == nullPtr then [] else Node src doc r : go r
181−
182−-- | Get the first attribute of this node which has a specific name, if there is one.
183−-- A more efficient version of:
184−--
185−-- > attributeBy n s = listToMaybe $ filter (\(Attribute a _) -> a == s $ attributes n
186−attributeBy :: Node -> BS.ByteString -> Maybe Attribute
187−attributeBy (Node src doc n) str = unsafePerformIO $ withForeignPtr doc $ \d ->
188− BS.unsafeUseAsCStringLen str $ \(bs, len) -> do
189− r <- hexml_node_attribute d n bs $ fromIntegral len
190− touchBS src
191− pure $ if r == nullPtr then Nothing else Just $ attrPeek src doc r
192−
193−-- | Find the starting location of a node, the @<@ character.
194−-- The first character will be reported as @(line 1,column 1)@, because thats
195−-- how error messages typically do it.
196−location :: Node -> (Int, Int)
197−location n@(Node src _ _) = BS.foldl' f (pair 1 1) $ BS.take (fromIntegral i) src
198− where
199− pair !a !b = (a,b)
200
148
201− i = strStart $ nodeStr 2 n
149+nodeAttributeBy :: Node -> BS.ByteString -> Maybe Attribute
202− f (!line, !col) c
150+nodeAttributeBy (Node src doc n) str = unsafePerformIO $ withForeignPtr doc $ \d ->
203− | c == '\n' = pair (line+1) 1
151+ BS.unsafeUseAsCStringLen str $ \(bs, len) -> do
204− | c == '\t' = pair line (col+8)
152+ r <- node_attributeBy d n bs $ fromIntegral len
205− | otherwise = pair line (col+1)
153+ return $ if r == nullPtr then Nothing else Just $ attrPeek src doc r
vs/hexml.slndeleted−28
@@ -1,28 +0,0 @@
1−
2−Microsoft Visual Studio Solution File, Format Version 12.00
3−# Visual Studio 14
4−VisualStudioVersion = 14.0.25420.1
5−MinimumVisualStudioVersion = 10.0.40219.1
6−Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hexml", "hexml.vcxproj", "{9DA5F6EE-A35E-4ED6-ABC2-A7707392E6EB}"
7−EndProject
8−Global
9− GlobalSection(SolutionConfigurationPlatforms) = preSolution
10− Debug|x64 = Debug|x64
11− Debug|x86 = Debug|x86
12− Release|x64 = Release|x64
13− Release|x86 = Release|x86
14− EndGlobalSection
15− GlobalSection(ProjectConfigurationPlatforms) = postSolution
16− {9DA5F6EE-A35E-4ED6-ABC2-A7707392E6EB}.Debug|x64.ActiveCfg = Debug|x64
17− {9DA5F6EE-A35E-4ED6-ABC2-A7707392E6EB}.Debug|x64.Build.0 = Debug|x64
18− {9DA5F6EE-A35E-4ED6-ABC2-A7707392E6EB}.Debug|x86.ActiveCfg = Debug|Win32
19− {9DA5F6EE-A35E-4ED6-ABC2-A7707392E6EB}.Debug|x86.Build.0 = Debug|Win32
20− {9DA5F6EE-A35E-4ED6-ABC2-A7707392E6EB}.Release|x64.ActiveCfg = Release|x64
21− {9DA5F6EE-A35E-4ED6-ABC2-A7707392E6EB}.Release|x64.Build.0 = Release|x64
22− {9DA5F6EE-A35E-4ED6-ABC2-A7707392E6EB}.Release|x86.ActiveCfg = Release|Win32
23− {9DA5F6EE-A35E-4ED6-ABC2-A7707392E6EB}.Release|x86.Build.0 = Release|Win32
24− EndGlobalSection
25− GlobalSection(SolutionProperties) = preSolution
26− HideSolutionNode = FALSE
27− EndGlobalSection
28−EndGlobal
vs/hexml.vcxprojdeleted−153
@@ -1,153 +0,0 @@
1−<?xml version="1.0" encoding="utf-8"?>
2−<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3− <ItemGroup Label="ProjectConfigurations">
4− <ProjectConfiguration Include="Debug|Win32">
5− <Configuration>Debug</Configuration>
6− <Platform>Win32</Platform>
7− </ProjectConfiguration>
8− <ProjectConfiguration Include="Release|Win32">
9− <Configuration>Release</Configuration>
10− <Platform>Win32</Platform>
11− </ProjectConfiguration>
12− <ProjectConfiguration Include="Debug|x64">
13− <Configuration>Debug</Configuration>
14− <Platform>x64</Platform>
15− </ProjectConfiguration>
16− <ProjectConfiguration Include="Release|x64">
17− <Configuration>Release</Configuration>
18− <Platform>x64</Platform>
19− </ProjectConfiguration>
20− </ItemGroup>
21− <ItemGroup>
22− <ClCompile Include="..\cbits\hexml.c" />
23− <ClInclude Include="..\cbits\hexml.h" />
24− <ClCompile Include="..\cbits\main.c" />
25− </ItemGroup>
26− <PropertyGroup Label="Globals">
27− <ProjectGuid>{9DA5F6EE-A35E-4ED6-ABC2-A7707392E6EB}</ProjectGuid>
28− <Keyword>Win32Proj</Keyword>
29− <RootNamespace>hexml</RootNamespace>
30− <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
31− </PropertyGroup>
32− <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
33− <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
34− <ConfigurationType>Application</ConfigurationType>
35− <UseDebugLibraries>true</UseDebugLibraries>
36− <PlatformToolset>v140</PlatformToolset>
37− <CharacterSet>Unicode</CharacterSet>
38− </PropertyGroup>
39− <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
40− <ConfigurationType>Application</ConfigurationType>
41− <UseDebugLibraries>false</UseDebugLibraries>
42− <PlatformToolset>v140</PlatformToolset>
43− <WholeProgramOptimization>true</WholeProgramOptimization>
44− <CharacterSet>Unicode</CharacterSet>
45− </PropertyGroup>
46− <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
47− <ConfigurationType>Application</ConfigurationType>
48− <UseDebugLibraries>true</UseDebugLibraries>
49− <PlatformToolset>v140</PlatformToolset>
50− <CharacterSet>Unicode</CharacterSet>
51− </PropertyGroup>
52− <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
53− <ConfigurationType>Application</ConfigurationType>
54− <UseDebugLibraries>false</UseDebugLibraries>
55− <PlatformToolset>v140</PlatformToolset>
56− <WholeProgramOptimization>true</WholeProgramOptimization>
57− <CharacterSet>Unicode</CharacterSet>
58− </PropertyGroup>
59− <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
60− <ImportGroup Label="ExtensionSettings">
61− </ImportGroup>
62− <ImportGroup Label="Shared">
63− </ImportGroup>
64− <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
65− <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
66− </ImportGroup>
67− <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
68− <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
69− </ImportGroup>
70− <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
71− <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
72− </ImportGroup>
73− <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
74− <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
75− </ImportGroup>
76− <PropertyGroup Label="UserMacros" />
77− <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
78− <LinkIncremental>true</LinkIncremental>
79− </PropertyGroup>
80− <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
81− <LinkIncremental>true</LinkIncremental>
82− </PropertyGroup>
83− <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
84− <LinkIncremental>false</LinkIncremental>
85− </PropertyGroup>
86− <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
87− <LinkIncremental>false</LinkIncremental>
88− </PropertyGroup>
89− <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
90− <ClCompile>
91− <PrecompiledHeader>
92− </PrecompiledHeader>
93− <WarningLevel>Level3</WarningLevel>
94− <Optimization>Disabled</Optimization>
95− <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions);_CRT_SECURE_NO_DEPRECATE</PreprocessorDefinitions>
96− </ClCompile>
97− <Link>
98− <SubSystem>Console</SubSystem>
99− <GenerateDebugInformation>true</GenerateDebugInformation>
100− </Link>
101− </ItemDefinitionGroup>
102− <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
103− <ClCompile>
104− <PrecompiledHeader>
105− </PrecompiledHeader>
106− <WarningLevel>Level3</WarningLevel>
107− <Optimization>Disabled</Optimization>
108− <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions);_CRT_SECURE_NO_DEPRECATE</PreprocessorDefinitions>
109− <AdditionalIncludeDirectories>..\cbits</AdditionalIncludeDirectories>
110− </ClCompile>
111− <Link>
112− <SubSystem>Console</SubSystem>
113− <GenerateDebugInformation>true</GenerateDebugInformation>
114− </Link>
115− </ItemDefinitionGroup>
116− <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
117− <ClCompile>
118− <WarningLevel>Level3</WarningLevel>
119− <PrecompiledHeader>
120− </PrecompiledHeader>
121− <Optimization>MaxSpeed</Optimization>
122− <FunctionLevelLinking>true</FunctionLevelLinking>
123− <IntrinsicFunctions>true</IntrinsicFunctions>
124− <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
125− </ClCompile>
126− <Link>
127− <SubSystem>Console</SubSystem>
128− <EnableCOMDATFolding>true</EnableCOMDATFolding>
129− <OptimizeReferences>true</OptimizeReferences>
130− <GenerateDebugInformation>true</GenerateDebugInformation>
131− </Link>
132− </ItemDefinitionGroup>
133− <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
134− <ClCompile>
135− <WarningLevel>Level3</WarningLevel>
136− <PrecompiledHeader>
137− </PrecompiledHeader>
138− <Optimization>MaxSpeed</Optimization>
139− <FunctionLevelLinking>true</FunctionLevelLinking>
140− <IntrinsicFunctions>true</IntrinsicFunctions>
141− <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
142− </ClCompile>
143− <Link>
144− <SubSystem>Console</SubSystem>
145− <EnableCOMDATFolding>true</EnableCOMDATFolding>
146− <OptimizeReferences>true</OptimizeReferences>
147− <GenerateDebugInformation>true</GenerateDebugInformation>
148− </Link>
149− </ItemDefinitionGroup>
150− <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
151− <ImportGroup Label="ExtensionTargets">
152− </ImportGroup>
153−</Project>
xml/example.xmldeleted−23
@@ -1,23 +0,0 @@
1−<?xml version="1.0"?>
2−<catalog>
3− <book id="bk101">
4− <author>Gambardella, Matthew</author>
5− <title>XML Developer's Guide</title>
6− <genre>Computer</genre>
7− <price>44.95</price>
8− <publish_date>2000-10-01</publish_date>
9− <description>An in-depth look at creating applications
10− with XML.</description>
11− </book>
12− <!-- more books -->
13− <book id="bk102">
14− <author>Ralls, Kim</author>
15− <title>Midnight Rain</title>
16− <genre>Fantasy</genre>
17− <price>5.95</price>
18− <publish_date>2000-12-16</publish_date>
19− <description>A former architect battles corporate zombies,
20− an evil sorceress, and her own childhood to become queen
21− of the world.</description>
22− </book>
23−</catalog>
xml/simple.xmldeleted−2
@@ -1,2 +0,0 @@
1−<a b="c">d</a>
2−