Normalize line endings in the Reader as the spec requires, which was not
done before:
- CR and CRLF fold to a single line feed (XML 1.0), so every downstream
scan sees line feeds only.
- When the declaration names version 1.1, NEL (U+0085) and LINE SEPARATOR
(U+2028), and a CR followed by NEL, also fold to a line feed; in 1.0
those characters are preserved as data.
The XML 1.1 name-character productions are identical to XML 1.0 Fifth
Edition, which the character classifier already implements, so names need
no change. The 1.1 character range (C0 controls permitted only as
references) is not yet enforced.
Closes#96
Add PureXML.Parsing.XMLCharacter as the single source of truth for character
classes, implementing the exact XML 1.0 (Fifth Edition) Char, NameStartChar, and
NameChar productions as public scalar predicates, plus isValidName. The scanner's
name recognition now delegates to these exact ranges (replacing a loose isLetter
check) and handles precomposed and combining-mark names. Closes#35.
Reimplemented from the XML grammar; no upstream code copied. 4 tests; 98 total.
Surface the parsed <!DOCTYPE> internal subset's <!ELEMENT> declarations from the
parser and validate a tree against them. Reimplemented from the DTD content-model
spec; no upstream code copied.
- DoctypeScanner captures <!ELEMENT name model> declarations; the parser surfaces
them as Parsing.DocumentType (via parseWithDocumentType).
- Validation.ContentModel + ContentModelParser parse a model string (EMPTY, ANY,
(#PCDATA), mixed, and element content with sequence/choice and ?/*/+).
- ContentModelMatcher matches the child element-name sequence against a model as
a regular language (NFA-position matching).
- Validation.DTDSchema.validate reports issues for EMPTY/PCDATA/mixed violations,
element content with stray text, and child sequences that do not match.
- PureXML.validateAgainstInternalDTD(_:) parses and validates in one call.
- ATTLIST validation is out of scope. 9 tests; 70 total.
Replace the notImplemented stub with a real parser built on a pull-based,
streaming event core that never requires the whole document in memory.
- Parsing.EventReader: pulls characters through a source closure with a bounded
lookahead buffer and emits one Parsing.Event at a time, holding only an
open-element stack. Drives chunked/streamed input.
- Parsing.Parser: builds a Model.Node tree iteratively over the event core
(explicit stack, no recursion).
- Reader: streaming character cursor (closure-backed, bounded buffer) replacing
the previous whole-string Array.
- EntityDecoder: the five predefined entities and decimal/hex char references.
- Handles elements, attributes, text, comments, CDATA, processing instructions,
and an XML declaration in the prolog. Rejects <!DOCTYPE> by default (no
DTD/XXE/entity-expansion exposure).
- Top-level API: parse(_:), parse(pulling:), events(_:), events(pulling:).
- 21 tests including event-stream and chunked-source coverage.