- Eval-time prefix bindings: Query.evaluate(over:namespaces:) and
value(at:...:namespaces:) supply prefix to URI bindings, so a name test
like q:item resolves q to a URI and matches by namespace regardless of
the document's own prefix. The match is namespace-aware only when
bindings are present, so existing unprefixed/prefix-string queries are
unchanged.
- XPointer schemes: xpath1() evaluates an XPath expression (like
xpointer()), and xmlns(p=uri) binds a prefix for the expression parts
that follow it, threaded into the XPath evaluation.
The range() scheme (and so XInclude range inclusion) remains.
Part of #88
Three XInclude gaps from the audit:
- accept / accept-language / encoding are surfaced to the loader via a new
XIncludeRequest and a process(_:base:loading:) overload, so a loader can
honor content negotiation. The existing loadingURI form is kept.
- Cycle detection tracks the visited (resolved-URI) set along the current
inclusion chain and raises XIncludeError.cycle, instead of relying only
on the depth backstop; a non-cyclic diamond include is not flagged.
- parse="text" combined with an xpointer is rejected
(XIncludeError.textWithFragment), since a fragment cannot select within
unparsed text.
xpointer range() inclusion still depends on the XPointer range() scheme
(tracked in the XPointer issue).
Part of #87
Two ISO Schematron gaps, the most validator-relevant first:
- Dynamic messages: an assert/report message is now a template, so
<value-of select=> renders the string value of its XPath at the context
node and <name> renders the node's name. A finding can report actual
values ("Age 120 must be under 100") instead of static text.
- Phases: <phase id><active pattern=> scopes which patterns run.
validate(_:phase:) honors a requested phase or the schema's
defaultPhase; nil or #ALL runs every pattern.
let variables, abstract rules/extends, diagnostics, and key()/document()
remain (the last depends on XSLT-style function injection).
Part of #80
Two OASIS catalog gaps from the audit:
- systemSuffix and uriSuffix entries: an identifier ending in the rule's
suffix maps to its URI, longest suffix winning, tried after exact and
rewrite matches.
- prefer=public|system on the catalog: a system match always wins, and
the public identifier is consulted as a fallback only when prefer is
public or no system identifier was given (the OASIS rule). The default
stays prefer=public.
xml:base / RFC 3986 relative-URI resolution of entry URIs and the legacy
SGML catalog format remain.
Part of #86
Broaden the serializer and writer options from the audit:
- attributeQuote selects single or double quotes; the active quote is
escaped in values (apostrophe as ' in single-quote mode).
- lineEnding controls the inserted newline (set to CRLF for \r\n output).
- xml:space=preserve suppresses pretty-print indentation for an element
and its subtree; xml:space=default re-enables it.
- Writer gains writeStartElementNS(prefix:localName:namespaceURI:) and
writeNamespace(prefix:uri:), so callers no longer hand-format prefixes
and xmlns attributes.
CDATA-vs-escape and entity re-escaping strategies are left as a
follow-up; the current escaping is the safe default.
Closes#91
Add the DOM/tree editing primitives from the audit that the editor use
case needs:
- Attribute helpers: attributeValue(_:), setAttribute(_:_:) (update in
place or append), removeAttribute(_:).
- shallowCopy() copies a node alone, complementing the deep copy().
- precedes(_:) compares two nodes in document order (ancestor before
descendant, earlier sibling before later; unrelated nodes compare false).
- normalize() coalesces adjacent text children, drops empty text, and
recurses into elements; CDATA is left intact.
First-class namespace/DOCTYPE/entity-reference nodes (an invasive
TreeNodeKind change) and cross-document adoption/ownerDocument (no
document-identity model) are left for a follow-up.
Closes#90
Close the two XSD-regex gaps from the audit:
- \p{...} and \P{...} now resolve Unicode general categories (one-letter
groups like \p{L} and two-letter codes like \p{Lu}/\p{Nd}) through
Swift's own scalar generalCategory, so no character tables are vendored.
A curated set of well-known \p{Is<Block>} blocks is supported; an
unknown category or block name is a compile error rather than a guess.
- Character-class subtraction [a-z-[aeiou]] removes the subtrahend from a
class, and nests ([a-z-[b-d-[c]]]) and composes with categories.
Categories and subtraction work both as standalone atoms and inside
character classes, so XSD pattern facets that use them now validate.
Closes#89
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
Broaden the TextReader pull cursor toward libxml2's xmlTextReader:
- Attribute nodes: moveToFirstAttribute/moveToNextAttribute/
moveToAttribute(_:)/moveToElement reposition the cursor onto each
attribute as an .attribute node and back.
- Namespace accessors: namespaceURI, localName, prefix from the current
element's or attribute's resolved name.
- xml:lang: inherited down the tree, overridable per element, reset when
it goes out of scope.
- Position: lineNumber/columnNumber report where each node begins, so a
streaming consumer can locate findings in the source. EventReader now
records each node's start mark.
DOCTYPE and entity-reference nodes are not surfaced (the DTD is exposed
through documentType, and entities are expanded inline); whitespace is
not split into significant/insignificant (needs the content model);
baseURI needs a document URI. The accurate positioning path for editor
findings remains readTree with source ranges.
Closes#98
Parse the XML declaration's pseudo-attributes instead of skipping the
<?xml?> processing instruction:
- version, encoding, and standalone are surfaced on a new XMLDeclaration,
returned from parseWithDocumentType and via PureXML.xmlDeclaration(_:).
- The pseudo-attributes are validated: fixed order (version, encoding,
standalone), known names only, and standalone must be yes or no. A
malformed declaration is rejected.
- The declared version is now readable, a prerequisite for XML 1.1.
parseWithDocumentType returns a ParsedDocument struct (node,
documentType, declaration) rather than a 2-tuple; the member names are
unchanged so existing call sites are unaffected.
Closes#95
Deepen DTD processing in the doctype scanner:
- Conditional sections are processed, not skipped: INCLUDE sections have
their declarations scanned, IGNORE sections are discarded, the keyword
may be a parameter-entity reference, and nested sections are handled.
- <!NOTATION> declarations are parsed and stored on DocumentType.
- Unparsed entities (<!ENTITY ... NDATA n>) are parsed and stored with
their notation, distinct from general entities.
- External parameter entities are loaded through the injected resolver so
their declarations expand; the default refusing resolver loads nothing,
keeping XXE closed.
- NOTATION attributes are a distinct type, validated against the declared
notations (value must be listed and the notation declared).
Tracking unparsed entities also lets ENTITY/ENTITIES attribute values be
validated against the declared NDATA entities, closing the gap left when
#73 could only check name syntax.
Closes#72
Complete the DTD attribute handling found in the audit:
- Default and #FIXED attribute values are applied to the parsed tree.
parseApplyingInternalDTDDefaults returns a tree where every element
carries the attributes its <!ATTLIST> defaults, so an editor sees the
same effective attributes a validating processor would.
- NMTOKEN, NMTOKENS, ENTITY, ENTITIES are validated: NMTOKEN(S) must be
XML name tokens, ENTITY/ENTITIES must be XML names. Previously all fell
through to unconstrained CDATA.
- DTD default values are normalized per XML 1.0: character and predefined
entity references are expanded, literal whitespace becomes a space, and
non-CDATA values are collapsed and trimmed.
ENTITY/ENTITIES check name syntax only; that the named entity is a
declared unparsed entity is not verified, since NDATA entity tracking is
not yet modeled.
Closes#73
Make XSD validation namespace-aware and enforce the form defaults and
xs:all structural constraints that were previously ignored:
- targetNamespace is tracked: a schema's global elements are in its
target namespace, and an instance's root element must be in it.
- elementFormDefault / attributeFormDefault (and per-declaration form)
qualify local element and attribute declarations. The content-model
matcher already compares namespace URIs, so qualified declarations
now match only same-namespace instance nodes; attribute matching
became namespace-aware too.
- xs:all groups are checked at compile time: every member must be an
element occurring at most once, the group may occur at most once, and
nested model groups inside an all are rejected (a SchemaError).
When a schema has no target namespace the stamping is a no-op, so
unqualified schemas validate exactly as before.
Cross-namespace type references through xs:import resolve by local name
only; full multi-namespace type tables are not modeled.
Closes#78
Read and enforce the XSD 1.0 derivation controls that were previously
ignored:
- abstract elements may not appear in an instance directly, only through
a substitution member; an element of an abstract complex type must
supply an xsi:type naming a concrete derived type.
- block on a type forbids the disallowed xsi:type substitutions; block on
an element drops substitution-group members that derive by a blocked
method.
- final on a type rejects, at compile time, any type that derives from it
by a final method.
- xs:redefine requires the redefined type to derive from itself.
Track each named complex type's base and derivation method as the
backbone these checks walk. Also compose complexContent extension and
restriction content models, which were previously dropped: an extension
now inherits its base's attributes and content (base first, then the
extension, in sequence).
Complex-type restriction is not verified to be a structurally valid
subset of its base (Particle Valid (Restriction)); that limit is
documented at its sites.
Closes#76
Add xs:any and xs:anyAttribute wildcards with namespace and
processContents constraints (skip/lax/strict), and resolve the
list built-in datatypes (IDREFS, ENTITIES, NMTOKENS) to their
item types so each list item validates.
WildcardNamespace models ##any/##other/enumerated URI sets;
Wildcard.admits gates undeclared attributes and wildcard-matched
children. ContentNFA.follow exposes the follow set for completions.
Closes#77
Slice 4 of #94 (completes the epic). Quick-fixes are derived from the structured
schema completions, never by parsing a diagnostic message:
- TreeNode gains contentRange (the span between an element's start and end tags),
captured by the ranged reader, so an inserted attribute or child is placed
exactly.
- QuickFix and TextEdit model an editor edit (a replacement over a source range;
an insertion is a zero-width range).
- QuickFixEngine turns a Completions into fixes: a required-but-absent attribute
becomes an insertion before the start tag's >, and a still-expected required
child becomes an insertion before the end tag. Schema.Document.quickFixes(at:in:)
is the entry over a ranged tree.
Tests apply a fix and re-validate: adding the required id, or inserting the
required qty, yields XML the schema then accepts; a complete element offers none.
#94 (editor integration) is complete: ranged recovering read, unified lint,
schema completions, and quick-fixes.
Slice 3 of #94. From the compiled schema, compute exactly what is allowed at an
element, for autocomplete and "what's missing".
- ContentNFA.follow(after:) returns the precise follow-set: the element-name
labels the content automaton can accept next after the children already
present, plus whether the content may legally end there.
- Schema.Completions reports the allowed next elements, the complete flag (false
means a required child is still expected), a wildcard flag, and each declared
attribute with its required and present status.
- CompletionEngine resolves an element's declared type along a coding path (the
global declaration, then each parent's content model) and computes the
completions; Schema.Document.completions(at:in:) is the entry over a ranged
tree.
Tests pin the follow-set as the children accumulate (name then qty then the
optional note, complete flipping true), the attribute required/present status,
and resolution for a nested element by its coding path.
Slice 2 of #94. One call for an editor: PureXML.lint reads the document once into
the ranged, best-effort tree (never throwing), then merges the parse diagnostics
and the validation findings into a single source-ranged, severity-tagged list in
document order.
- LintDiagnostic carries message, severity, and the SourceRange to highlight.
- lint(_:validate:) takes a node-based validate closure that runs over the
RECOVERED tree, so validation works even when the document is not well-formed:
a truncated, schema-invalid document yields both a parse diagnostic and the
validation finding, each with a range.
- Schema.Document gains validate(_ node:) so a caller validates the same tree it
is editing; validate(_ xml:) delegates to it.
Tests cover a clean lint, well-formedness-only lint, a range-located validation
finding, and the both-truncated-and-invalid editor case.
Slice 1 of #94 (editor integration). The keystone: a located validation finding
can now be mapped back to a source range.
- TreeNode gains a sourceRange (start/end Mark); SourceRange is a half-open span.
- PureXML.readTree builds a mutable, parent-aware, best-effort tree whose every
node carries its span (an element from the < of its start tag to past its end
tag; leaves over the text they consumed). Never throws, recovers in place,
returns located diagnostics.
- TreeNode.node(at:) navigates a validation coding path to its node using the
same canonical convention the validators build paths with (a sibling index
only when a name repeats), and sourceRange(at:) returns the span. So
Document.validate's ValidationError.codingPath resolves to characters in the
document.
Tests prove the spans, the navigation, the end-to-end bridge (an XSD finding to
a source range), and that ranged reading of invalid input never crashes.
Closes#75, on the validation framework. XSD instance-level features:
- nillable elements: an xsi:nil="true" element is accepted (its content model is
not checked) only when the element is declared nillable and carries no content;
xsi:nil on a non-nillable element, or a nilled element with content, is a
located error.
- fixed value constraints: a present fixed attribute must equal its fixed value,
and a fixed element's text must match.
- default/fixed are parsed onto AttributeUse (per use) and collected per element
name (nillable set + value constraints), threaded through XSDContext to the
ComplexValidator.
All findings are located ValidationErrors. Collection and helper methods move to
same-file extensions to stay within type-body limits.
Slice 5 of #92. The last validator moves onto the framework and the legacy
Issue type is removed, so all validation now flows through one error model.
- ValidationError gains a severity (error by default, warning for advisory
findings), chosen so Schematron's assert/report distinction survives in the
unified type.
- Schematron is a Validation<Node, Void> over the document root; a failed assert
is an error finding, a matched report a warning. Each finding is located at the
context node its rule fired on (built from the element chain, with a sibling
index only when a name repeats), not the document root.
- Schematron.validate returns [ValidationError] and runs through a Validator.
- Validation.Issue and its Severity-based result are deleted; Severity now backs
ValidationError.
Every validation layer (structural, DTD, XSD content and identity, Schematron)
now produces located ValidationError values through the shared framework.
Slice 4 of #92. The XSD complex-type and identity-constraint validators move
onto the validation framework, producing located results.
- ComplexValidator threads a coding path through its (inherently recursive)
descent, so every content-model, attribute, and simple-type violation is now
a ValidationError located by path ("... at path: order/qty"). Child steps
carry a sibling index only when a name repeats.
- IdentityValidator returns ValidationError instead of Issue.
- A new XSD rules enum wraps both as Validation<Node, XSDContext> values gated to
the document root, combined by Validator<XSDContext>. XSDContext carries the
type table, the identity constraints, and the root declaration.
- Schema.Document.validate builds the context and runs the validator, returning
[ValidationError].
XSD content-model validation is recursive by nature (a child's type depends on
its position), so the rules delegate to the recursion while contributing to one
error collection. Schematron is the last validator on Issue and migrates next.
Slice 3 of #92. The DTD validator's imperative walk becomes declared Validation
values over a DTDSchema document:
- DTDSchema is now just the schema (content models + attribute declarations) and
serves as the rules' Document context; its validate/walk methods are gone.
- contentModel and attributeDeclarations are Validation<Element, DTDSchema>, so
every content-model and attribute violation is now located by coding path
("... at path: r/a"). undeclaredElement is added only in strict mode.
- identifierIntegrity is a Validation<Node, DTDSchema> gated to the root, doing
the document-wide ID-uniqueness and IDREF-resolution checks in one pass.
- DTD.validator(strict:) assembles the rule set; validateAgainstInternalDTD runs
it and returns [ValidationError] instead of [Issue].
A new test pins the located result. Schema (XSD) and Schematron validators still
use Issue and migrate in the following slices.
Closes#93. Recovery moves into a recovering mode on EventReader, so malformed
input is repaired in place rather than dropped to the next markup boundary:
- A duplicate attribute is recorded and the later occurrence dropped, keeping the
element. <a id='1' id='2'>y</a> now reads as <a id="1">y</a> with one
diagnostic (was an empty tree with three).
- A mismatched or unexpected end tag pops the open stack to the matching ancestor,
implicitly closing the elements nested inside it. <a><b><c>x</b></a> reads as
<a><b><c>x</c></b></a> with a single diagnostic.
- Pre-root junk reports "unexpected character" instead of the misleading
"content after the root element".
In recovering mode next() no longer throws: it records a Diagnostic and emits
best-effort events, popping to a match on a mismatch and stopping on truncation
so the builder closes what is open. The strict path is unchanged. readRecovering
becomes a plain non-throwing drain that takes the reader's diagnostics. Recovery
stays deterministic; new tests pin the kept-element and pop-to-match results.
Slice 2 of #92. Add PureXML.read, a deterministic recovering reader: it never
throws and never traps, returning the maximal best-effort tree plus one located
Diagnostic per problem.
- ReadResult { node, diagnostics } is the Equatable result type, so a
deterministic read can be compared whole.
- Diagnostic carries a message and a structured source Mark; a thrown ParseError
maps to one directly (ParseError.mark exposes the position each case carries).
- The reader resynchronizes after a failure (EventReader.recover(from:)):
progress-aware, so a failure that already consumed the bad construct retries
from there, while a stuck failure skips to the next markup start; it always
advances when input remains, guaranteeing termination.
- The tree builder assembles leniently: elements left open by truncation are
closed, an end tag with no open element is dropped. Well-formed input yields
exactly parse(_:)'s tree with no diagnostics.
Tests pin exact trees and diagnostics for truncation, mismatched tags, and stray
junk, prove determinism (same bytes give an identical ReadResult), and fuzz 400
seeded inputs (arbitrary plus truncated valid docs) asserting no crash.
Recovery here is resync-based; finer in-reader recovery (keep the element on a
duplicate attribute, pop to a matching open tag) is the next slice.
Slice 1 of #92. Introduce the OpenAPIKit-idiom validation framework, replacing
the imperative if-tree structural validator:
- ValidationError (reason + coding path) renders "<reason> at path: root/row/cell";
ValidationErrorCollection gathers every failure and is thrown once.
- Validation<Subject, Document> is the atomic, composable rule value, with the
multi-error and single-error Bool inits (the latter auto-forms "Failed to
satisfy: <description>"). Predicate-gated, type-directed via an erased
AnyValidation that guards an optional from matching a non-optional rule.
- The framework is parameterized over both Subject and Document so the structural,
DTD, XSD, and Schematron layers share one machinery (Document = Void for rules
that need only their subject).
- Validator<Document> walks the node tree, building the coding path, with blank /
default factories and fluent validating / withoutValidating.
- The combinator algebra (&&, ||, comparison lifts, take, lift, unwrap, all) as
free functions returning closures.
The structural duplicate-attribute rule is reborn as Validation<Element, Void>.
Combinators and the erased wrapper are unit-tested as pure functions per the rule.
The legacy Validation.Issue stays only for the not-yet-migrated schema validators
and is retired in a later slice.
Close the remaining documented parity edges:
- Canonical XML 2.0 TrimTextNodes: an option that strips leading and trailing
whitespace from each text node (dropping all-whitespace nodes), exposed as
Options.canonical2.
- XML Catalog delegation and chaining: delegatePublic, delegateSystem,
delegateURI, and nextCatalog entries are parsed, and resolveSystem/
resolvePublic/resolveURI gain a loadingCatalog overload that follows the
longest matching delegate prefix and then each nextCatalog, with a
visited-set guard against cycles.
- XPath number formatting: the canonical string form now expands any exponent
in the shortest double rendering to a plain decimal, since XPath 1.0 forbids
scientific notation.
Closes#70
Add parseDocument, which builds a full HTML document by the HTML5
tree-construction insertion modes (initial, before html, before head, in head,
after head, in body):
- the result always has an html root containing a head and a body, whether or
not the source spelled those elements out.
- head-only elements (title, meta, link, base, style, script, and the rest)
are routed into the head; flow content starts and fills the body.
- explicit html, head, and body tags and their attributes are honored.
The existing fragment parser (parse) is unchanged, so tag-soup fragments still
round-trip without an implied wrapper.
Closes#69
Compile RELAX NG from both syntaxes and across documents:
- A compact-syntax parser (lexer plus recursive descent) produces the same
pattern algebra the XML syntax compiles to. It covers element and attribute
declarations with name classes and namespace resolution, the , | & combinators
and ? * + cardinality, text/empty/notAllowed, list and mixed, datatypes and
value literals, grammars with start and named definitions (= |= &=), nested
grammars, and include/external references.
- The XML-syntax parser gains include (merging another grammar's definitions,
with the include's own nested definitions overriding) and externalRef
(resolving to an external schema's pattern), both through an injected loader.
Both syntaxes reach the loader through a new schemaLoader parameter that
returns nil by default, so compilation never touches the filesystem or network
unless a loader is supplied.
Closes#68
Validate XSD identity constraints over the instance document in a pass that
walks the tree alongside type validation:
- xs:unique requires the selected nodes' field tuples to be distinct, excluding
any node that is missing a field.
- xs:key additionally requires every field to be present.
- xs:keyref requires each tuple to equal a tuple of the named key or unique
visible in an enclosing scope, resolved through a stack of scope frames so a
keyref and the key it refers to may sit on the same element.
The selector and field XPaths run on the existing XPath engine, relative to the
declaring element and each selected node. Constraints are gathered for every
element declaration at any depth, keyed by element name.
Closes#67
Compose schemas across documents and resolve types at the instance:
- xs:include, xs:import, and xs:redefine pull a transitive closure of schema
documents through an injected loader (resolved by schemaLocation), base
schemas first so the including schema wins; a redefine's nested definitions
override the schema it includes.
- substitution groups: an xs:element ref to a head also admits every element
that substitutes for it, expanded at parse time into a choice over the head
and its transitive members.
- xsi:type overrides an element's declared type with the named type at the
instance; XML Schema instance attributes are exempt from attribute checks.
The schema parser splits across XSDContext (shared tree helpers, document
collection, parsing context), XSDSimpleParser (simple types and facets), and
XSDParser (elements, complex types, model groups) to keep each within bounds.
Closes#66
Extend the schema parser toward full structural parity:
- list simple types validate each whitespace-separated item against the item
type and count items for the length facets; union simple types admit a value
valid for any member type. A restriction over a named list or union inherits
its variety.
- Named attribute groups are expanded (transitively) into every complex type
that references them.
- Named model groups are expanded at each xs:group ref, with a cycle guard;
element refs resolve to the referenced global element's declared type,
through a reserved type-table key so forward and recursive refs settle at
validation time.
Named simple types now resolve over repeated passes so they may restrict or
list one another regardless of document order. Simple-type and facet parsing
moves into a sibling XSDSimpleParser to keep each parser within bounds.
Closes#65
Round out the XSLT 1.0 stylesheet and output controls:
- xsl:include folds another stylesheet's templates, keys, globals, and output
at the same import precedence; xsl:import folds them one precedence lower,
so the importing stylesheet's rules win conflicts. Both resolve their href
through the injected document loader, never the filesystem.
- xsl:output drives serialization: method (xml or text), indent,
omit-xml-declaration, encoding, version, and standalone, layered over the
caller's emitter options. The text method emits character data only.
- xsl:comment and xsl:processing-instruction produce comment and PI nodes
from their instantiated bodies.
Templates now carry an import precedence that the matcher ranks ahead of
priority and document order.
Closes#64
Reach full XSLT 1.0 key, numbering, and document parity:
- xsl:key declarations are compiled into an index over the source tree;
the key() function looks nodes up by key name and value.
- format-number() renders a number against a picture string (digit places,
decimal point, grouping, percent), Foundation-free.
- xsl:number generates the context node's position rendered as arabic,
alphabetic, or roman numerals.
- document() resolves an external source through an injected loader so the
transform never reaches the filesystem or network by default.
These hang off an XPath function-table injection mechanism: the evaluator
merges an engine-supplied FunctionTable, letting XSLT register current(),
key(), format-number(), and document() per evaluation.
Closes#63
Honor xsl:param defaults, xsl:with-param on apply-templates and
call-template, and mode on templates and apply-templates. A passed parameter
overrides the declared default; a mode routes nodes only to templates of that
mode, and the built-in rules recurse in the same mode. Add the parity-
completion epic to the roadmap.
Closes#62
Use let for never-mutated bindings in DateTimeParser, HTMLEntities, and the
XSLT parser, and drop the redundant Unicode.Scalar Comparable conformance
that the standard library already provides. No behavior change.
Add a resumable XML scanner that reports a token only once its terminator is
fully buffered and otherwise signals need-more (the Expat XML_TOK_PARTIAL
model), and PushParser with feed(_:)/finish() driving a SAXHandler while
retaining only the current incomplete token plus the open-element stack.
events(feeding:) exposes an AsyncThrowingStream of events over any async
sequence of text chunks. Splitting a document at any byte boundary, down to
one character per feed, yields identical events. Collapse the completed
roadmap into the legend summary.
Closes#4Closes#5Closes#6Closes#7Closes#1
Add the PureXML.HTML namespace: a lenient tokenizer for tag-soup input
(case-insensitive tags; quoted, unquoted, and boolean attributes; comments;
doctype; character references), a tree builder handling void elements,
optional end tags (li, p, table rows and cells), raw-text elements (script,
style), and unmatched end tags, and a serializer that writes void elements
unclosed and raw-text content unescaped.
Closes#36Closes#37Closes#38Closes#39Closes#20
Add the XSLT execution engine: template matching against the source tree
(highest priority, then last declared, with the built-in rules), the
apply-templates processing model, and instantiation of the sequence
constructor. Implements apply-templates with sort, value-of, for-each, if,
choose, element, attribute, copy, copy-of, call-template, variable, literal
result elements, and attribute value templates, over the XPath engine. The
identity transform round-trips a document including attributes. Collapse the
completed Transformation roadmap block into the shipped summaries.
Closes#15Closes#16Closes#17Closes#18Closes#3
Add the PureXML.XSLT namespace with a stylesheet model (template rules with
match patterns and computed default priorities, the instruction sequence
constructor, attribute value templates) and a parser compiling an
xsl:stylesheet document into it. Recognizes the XSLT vocabulary by namespace
or xsl prefix; other elements become literal result elements. Covers
apply-templates, value-of, for-each, if, choose, element, attribute, copy,
copy-of, call-template, variable, text, and sort.
Closes#14
Add the RELAX NG pattern algebra (Pattern, NameClass), a parser for the XML
syntax into a grammar of start plus named defines, and James Clark's
derivative validation algorithm. The pattern is derived through each
start-tag, attribute, text, and end-tag event with simplifying constructors;
the document is valid when the residual pattern is nullable. Supports
element, attribute, text, empty, notAllowed, group, choice, interleave,
optional, zeroOrMore, oneOrMore, mixed, list, ref/define, data, value, and
the name classes; handles interleave and recursive grammars, reusing the XSD
datatype library for data and value.
Closes#11Closes#12
Add Schema.Document compiling an xs:schema into global element declarations
and a named-type table, and validate(_:) checking instance documents end to
end. The parser supports global and local element declarations, named and
inline simple and complex types, sequence/choice/all groups with occurrence,
attribute uses, simple content (restriction/extension), the full facet set,
and recursive types via named references resolved during validation. Extends
ComplexType with a typeReference case and ComplexValidator with a type table
so recursive schemas are representable.
Closes#10
Add ComplexValidator validating an element against a ComplexType: attribute
uses (required, typed, unknown-attribute rejection) and the content model
(empty, simpleContent, elementOnly, mixed). Particles compile to a Thompson
NFA over element names honoring minOccurs/maxOccurs with sequence and choice
compositors; all groups are validated order-independently by counting. Child
elements are validated recursively against their declared types.
Closes#9
Add the PureXML.Schema namespace validating XSD Part 2 built-in datatypes
and constraining facets. Built-ins span string/boolean/decimal, the bounded
integer family with intrinsic bounds, float/double, duration, the eight
date/time types, hexBinary, base64Binary, anyURI, QName, and the
Name/NCName/NMTOKEN/language family. Facets: length/minLength/maxLength,
pattern (via the regex engine), enumeration (value-space comparison),
whiteSpace, min/maxInclusive/Exclusive, totalDigits, fractionDigits.
Decimals compare exactly with no floating-point loss; date/time fields are
range-validated with leap years, 24:00:00, and timezone bounds.
Closes#8
Add the PureXML.Regex namespace: a recursive-descent parser for the XSD
regex grammar (literals, ., character classes with ranges and negation, the
\d \w \s family and negations, single-char escapes, grouping, alternation,
and the ?, *, +, {n}, {n,}, {n,m} quantifiers) compiled to a Thompson NFA.
matches(_:) is whole-string as xs:pattern requires, and runs linear in the
input with no backtracking blow-up. \p{...} category escapes are not yet
supported.
Closes#59Closes#60Closes#30
Add the PureXML.Canonical namespace rendering the C14N form: namespaces
sorted by prefix (default first), attributes by namespace URI then local
name, empty elements expanded to start/end pairs, CDATA folded into escaped
text, and the C14N escaping rules. Inclusive mode renders every in-scope
namespace; exclusive mode renders only visibly-used namespaces plus an
InclusiveNamespaces prefix list. Comments omitted unless requested. For
whole-tree canonicalization C14N 1.0 and 1.1 coincide.
Closes#54Closes#55Closes#56Closes#26
Add the PureXML.XInclude namespace: RFC 3986 reference resolution
(URIReference with dot-segment removal) and an xi:include processor over the
value tree. href resolves against the base and in-scope xml:base; parse=xml
includes the document element or an xpointer() fragment (via PureXML.XPointer),
parse=text includes raw text, and a failed load uses xi:fallback or errors.
Includes nest. No I/O is done internally: fetching is only through the
injected loadingURI closure, so the default fetches nothing.
Closes#48Closes#49Closes#50Closes#51Closes#24
Add the PureXML.Catalog namespace: parse an OASIS XML catalog (public,
system, uri, rewriteSystem, rewriteURI, group/catalog by local name) into a
Resolver mapping public/system identifiers and URI names to URIs, with
longest-prefix rewriting and system-over-public precedence. entityResolver
(loadingURI:) builds a Parsing.EntityResolver that resolves an external
identifier through the catalog and loads the URI via an injected closure, so
a catalog plus loader wires entities and DTDs in while the default keeps XXE
closed.
Closes#57Closes#58Closes#27
Add PureXML.Validation.Schematron: compile a Schematron schema (matching
schema/pattern/rule/assert/report by local name, so the ISO and legacy
namespaces both work) and validate a document. A rule's context XPath
selects the nodes it fires on; each assert/report test is evaluated relative
to the node via the XPath context-node API. A failed assert is an error, a
matched report a warning; within a pattern a node fires the first matching
rule. Adds XPath.Query.nodes(over:) returning matched tree nodes so one tree
can be queried repeatedly.
Closes#52Closes#53Closes#25
Add the PureXML.XPointer namespace resolving the shorthand bare-name form
(id), the element() scheme (child-element positions from an id or the
document root), and the xpointer() scheme (a full XPath expression). Scheme
parts chain with the XPointer fallback rule: the first non-empty selection
wins. element() data is translated to the equivalent XPath and evaluated by
PureXML.XPath, so no separate evaluator is needed.
Closes#46Closes#47Closes#23