Commit Graph

70 Commits

Author SHA1 Message Date
46131bff1b feat(model): first-class doctype/entity-reference/namespace nodes, ownerDocument, adoption (#90) 2026-06-09 03:43:24 +02:00
a71178ba9b docs: move closed Schematron #80 to done in roadmap 2026-06-09 03:31:29 +02:00
5c9c6366bd docs: refresh test count to 666 2026-06-09 03:30:44 +02:00
e1a0b5698c docs: move closed RELAX NG #79 to done in roadmap 2026-06-09 03:21:37 +02:00
259aa251fd docs: refresh test count to 662 2026-06-09 03:20:48 +02:00
45001f3422 docs: bring the shipped-overview roadmap node current with catalog #86 and XSLT #81 #82 2026-06-09 03:16:05 +02:00
0865fac3f9 docs: move closed XSLT #82 to done in roadmap 2026-06-09 03:14:01 +02:00
47d4390020 docs: refresh test count to 658 2026-06-09 03:12:50 +02:00
a65668d1fc docs: refresh test count to 654 2026-06-09 03:04:56 +02:00
aa65842bcc docs: refresh test count to 649 2026-06-09 02:58:35 +02:00
9b07a5901d docs: refresh test count to 645 2026-06-09 02:53:51 +02:00
9545802be6 docs: refresh test count to 642 2026-06-09 02:50:24 +02:00
73f8fb74af docs: refresh test count to 639 2026-06-09 02:48:01 +02:00
20d7b0e748 docs: refresh test count to 635 2026-06-09 02:37:43 +02:00
46393b3aa2 docs: document the pull/push/SAX/tree consumption modes
Make explicit that input is consumed step by step with bounded memory via
the pull cursor (EventReader/TextReader), push/feed, and SAX, and that only
the tree mode materializes the whole document.
2026-06-09 02:24:02 +02:00
943c9e9b09 docs: refresh test count to 632 2026-06-09 02:20:36 +02:00
f34120a70e docs: move closed XSLT elements #81 to done in roadmap 2026-06-09 02:11:13 +02:00
ae847c32d6 docs: refresh test count to 627 2026-06-09 02:10:17 +02:00
26ebeed155 docs: refresh test count to 624 2026-06-09 01:59:40 +02:00
2f79be18e1 docs: refresh test count to 619 2026-06-09 01:52:22 +02:00
0dae8e5670 docs: refresh test count to 615 2026-06-09 01:49:34 +02:00
75ed549c33 docs: refresh test count to 613 2026-06-09 01:39:46 +02:00
0a78822845 docs: refresh test count to 609 2026-06-09 01:29:38 +02:00
ff1cdfd244 docs: refresh test count to 606 2026-06-09 01:23:01 +02:00
0a78aa76a1 docs: refresh test count to 602 2026-06-09 01:11:54 +02:00
2dc066b573 docs: refresh test count to 601 2026-06-09 01:09:30 +02:00
3021615b66 docs: refresh test count to 600 2026-06-09 00:59:26 +02:00
3e3ca0f971 docs: refresh test count to 596 2026-06-09 00:55:43 +02:00
bbd293521a docs: refresh test count to 592 2026-06-09 00:41:31 +02:00
5eeb29d10a docs: move closed catalog #86 to done in roadmap, refresh test count 2026-06-08 18:06:13 +02:00
e0e498c4bb docs: record #79 RELAX NG work in changelog and refresh test count
Adds the Unreleased changelog entry for the datatypeLibrary resolution,
value-space <value> comparison, nsName subtraction, and compact-syntax
annotations/div/concatenation work, and updates the README suite count to
573 tests in 84 suites.
2026-06-08 17:45:42 +02:00
293625d41d docs(readme): chain roadmap nodes with directed edges for reliable vertical layout
Invisible ~~~ links did not reliably rank nodes vertically in GitHub's
Mermaid renderer, leaving the shipped nodes spread horizontally. Chain
every node with --> so flowchart TB stacks them in a single top-to-bottom
column.
2026-06-08 16:12:49 +02:00
6e971ac7e6 docs(readme): rewrite stale roadmap and make mermaid diagrams vertical
The roadmap diagrams and status text were badly out of date. Rewrite the
second mermaid block from a single unreadable node into an accurate
#71-audit roadmap, refresh the shipped block, and chain every node into a
single vertical column so flowchart TB renders top-to-bottom rather than
fanning out wide.

Also correct the flagrantly stale prose: test count (135 to 506), the
false 'XSD/RELAX NG/Schematron remain open epics' and 'XPath 1.0 subset'
claims (all shipped), and replace the obsolete open-epics table with the
current #71 depth-gap work.
2026-06-08 16:10:28 +02:00
fe11bd0e56 docs(roadmap): collapse #92 and #94 to shipped, #71 the open epic
The validation framework + recovering reader (#92) and editor integration (#94)
are complete; they fold into the shipped legend. The beyond-parity libxml2 gaps
(#71) are the remaining open epic.
2026-06-08 13:37:21 +02:00
4659ea33c8 feat(editor): source spans on the tree, mapping findings to positions
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.
2026-06-08 13:14:29 +02:00
1fbe359ada feat(validation): composable validation framework + structural rule
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.
2026-06-08 12:00:34 +02:00
6ac3804ba1 docs(roadmap): collapse #61 to done, add #71 beyond-parity gap epic
The documented-subset parity epic #61 is complete, so it folds into the
shipped legend. A conformance audit surfaced the remaining depth gaps versus
libxml2; they are tracked under epic #71 with per-subsystem children #72-#91.
2026-06-08 11:36:46 +02:00
7ce6b4da73 feat(xslt): template parameters and modes
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
2026-06-08 07:48:05 +02:00
4e5debd557 feat(parsing): resumable push / feed streaming API
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 #4
Closes #5
Closes #6
Closes #7
Closes #1
2026-06-08 07:21:22 +02:00
cfba3ed7d2 feat(html): lenient HTML parser and serializer
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 #36
Closes #37
Closes #38
Closes #39
Closes #20
2026-06-08 07:12:42 +02:00
75e511481c feat(xslt): transformation engine completing XSLT 1.0
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 #15
Closes #16
Closes #17
Closes #18
Closes #3
2026-06-08 07:06:49 +02:00
8d6c3446ab test(schema): schema conformance fixtures, completing XSD and RELAX NG
Add a cross-cutting conformance suite validating realistic schemas against
conforming and non-conforming instances: a patterned, bounded, ID-keyed XSD
purchase order and a RELAX NG contact card using interleave, attribute value
choices, and optional/repeatable elements, plus datatype boundary fixtures.
Collapse the completed Validation roadmap block into the shipped summary.

Closes #13
Closes #2
2026-06-08 06:52:43 +02:00
951b808f02 feat(regex): XML Schema regular-expression engine
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 #59
Closes #60
Closes #30
2026-06-08 06:18:50 +02:00
a9be5253cf feat(canonical): Canonical XML (C14N) serialization
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 #54
Closes #55
Closes #56
Closes #26
2026-06-08 06:13:02 +02:00
85f2ad2ebd feat(xinclude): XInclude, URI resolution, and xml:base
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 #48
Closes #49
Closes #50
Closes #51
Closes #24
2026-06-08 06:08:31 +02:00
634270d529 feat(catalog): OASIS XML Catalog resolution
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 #57
Closes #58
Closes #27
2026-06-08 06:03:02 +02:00
80fffd8127 feat(validation): Schematron rule validation
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 #52
Closes #53
Closes #25
2026-06-08 05:58:55 +02:00
ef973e570b Merge epic/23-xpointer: element() and xpointer() schemes 2026-06-08 05:54:38 +02:00
627c035c2a feat(xpointer): element() and xpointer() schemes
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 #46
Closes #47
Closes #23
2026-06-08 05:52:23 +02:00
f7fe89023c feat(pattern): streaming xmlPattern matching
Add the PureXML.Pattern namespace: a compiler for the streamable XPath
subset (names, *, prefix:*, /, //, leading absolute /, trailing @attr) into
a Matcher, with predicates and . / .. rejected. The backward-matching
algorithm tests an element's ancestor-or-self path. Pattern.matches(_:in:)
streams a document through the pull parser and returns matching node paths
in document order from the open-element stack alone, no tree built.

Closes #44
Closes #45
Closes #22
2026-06-08 05:49:13 +02:00