Commit Graph

10 Commits

Author SHA1 Message Date
380160cc2b refactor(validation): decompose XSD instance validation into composable rules
The two monolithic XSD content/identity rules only satisfied the OpenAPIKit
validation idiom at the wrong granularity: no per-constraint removability,
no isolable failing/near-miss tests. Split them into 32 named rules, each
finding tagged at its origin and cached per XSDContext so the walk still
runs once per document (the #101 decision against duplicating XSD's
recursive type resolution still holds). Wired the streaming path's ID/IDREF
and identity checks through the same framework instead of calling the
validators imperatively, and added the unwrapAndLookup combinator the rule
already named as required.

Also includes the mandated repo-wide swiftformat/swiftlint pass.
2026-07-12 14:12:17 +02:00
c628682086 feat(relaxng): QName values with dual-context resolution, instance level clean
A value of type QName compiles to its (namespace, local) pair via the
schema's xmlns/ns scope; the derivative engine threads the instance
element's in-scope bindings to value comparison in both the tree walk
and the streaming state (frames carry their bindings), so instance
prefixes and the default namespace resolve against the instance
document and unbound prefixes match nothing. Streaming also gains the
tree walk's empty-element rule. The spec suite's instance level is
fully clean: valid 289/289, invalid 291/291; only the asserted
schema-correctness class remains.
2026-06-10 10:57:14 +02:00
5e7cc292f9 feat(relaxng): grammar scoping, parentRef, include overrides, div flattening
Each grammar element opens a define scope (scope-mangled names in the
flat table) with parentRef reaching the enclosing scope; includes are
transitive and include-level defines replace the included grammar's
same-name defines (4.7 override); div wrappers flatten while keeping
ns/datatypeLibrary inheritance (4.11); value text stays untrimmed so
whitespace-preserving datatypes compare exactly (6.2.9); dataExcept
recurses through choices. Spec suite: valid 286/289, invalid 287/291;
the six remaining instance cases are all the QName datatype.
2026-06-10 10:34:44 +02:00
b8fa90ccf5 feat(relaxng): ns inheritance, QNames, include-start, combine, data-except
The compiler implements simplification 4.9-4.10 (ns inheritance across
ancestors and document boundaries; QName resolution against in-scope
xmlns declarations; the attribute name= shorthand stays namespace-less),
include brings in the included grammar's start with overrides (4.7),
multiple starts and defines combine with a single omitted combine
merging by the named method (4.17), data/except lands as a dataExcept
pattern in the derivative engine (4.12), an empty element matches data
whose lexical space admits the empty string, and whitespace-only
attribute values match empty-matching patterns. Spec suite: valid
274/289, invalid 279/291.
2026-06-10 10:24:46 +02:00
4130d905c8 refactor(parsing): drop slice copies; document the expansion bounds
The whole-buffer ISO-2022-JP, GB18030, and EUC-TW decoders index the input
ArraySlice directly instead of materializing an Array first (one avoidable
O(n) allocation per decode each); declaredEncoding's subsequence scan uses
elementsEqual instead of allocating an Array per candidate offset. Document
the two analysis-flagged bounds: entity expansion is doubly bounded (the
character budget caps output; the visiting set caps recursion depth at the
number of declared entities), and the RELAX NG derivative engine carries no
memoization by design (the simplifying constructors keep real grammars
small; adversarial nested-choice grammars can grow the residual, so treat
untrusted schemas accordingly). Behavior unchanged, all suites green.

Closes #118
2026-06-10 04:01:16 +02:00
051ef02fdb feat(validation): streaming RELAX NG validation
Add RelaxNG.validate(streaming:), driving the Brzozowski derivative directly
from the parse-event stream (the libxml2 xmlTextReader model). The derivative
is already incremental: the after-threaded pattern chain is the validation
stack, so only the residual pattern and a light per-element frame (for the
childrenDeriv ignorable-whitespace rule) are retained, never the tree. A
differential test asserts streaming agrees with the trusted tree engine across
valid, invalid, whitespace, and mixed-content documents. XSD streaming remains.

Part of #107, #105
2026-06-09 10:35:42 +02:00
ae271d65f6 feat(relaxng): located, recovering validation errors for editor use
Adds RelaxNG.errors(in:) reporting every way a document fails the schema
as individual located errors with coding paths, recovering past each
failure so all of a faulty document's problems surface at once instead of
only the first. Errors carry recovery hints naming the element types
accepted at the failure point, and distinguish unexpected elements,
invalid or missing attributes, invalid text/datatype content, and missing
required content. RelaxNG.validation() exposes the schema as a
Validation<Node, Void>, composing with the same framework as the XSD,
DTD, and Schematron rules. The boolean derivative engine remains the
authority on validity; the diagnostic walk only places and explains the
failures, recovering each step so it never cascades. Splits the engine
and the diagnostics into separate files.
2026-06-09 00:40:56 +02:00
79f424321c feat(relaxng): compare typed <value> in the datatype value space
A <value> pattern now carries its datatype, so matching compares the
instance and the literal in that type's value space rather than purely
lexically. Numeric and date/time types compare by value (1 equals 01,
1.5 equals 1.50); booleans treat 1/true and 0/false as equal; string and
token compare by their whitespace-normalized lexical form, as before.
Untyped values default to the built-in token type, and a bare string
literal in the compact syntax is token-typed. Durations and binary types
fall under the lexical rule.
2026-06-08 17:36:16 +02:00
28cbed0a70 feat(relaxng): <param> facets under <data>
A <data type=> pattern now carries a SimpleType (base + facets) rather
than a bare built-in, and its <param name=>value</param> children
populate the facets (minLength, maxLength, length, pattern, min/max
inclusive/exclusive, total/fraction digits), reusing the XSD datatype
facet validation. So a constrained datatype like xsd:string with
minLength rejects too-short values.

datatypeLibrary resolution, typed <value> comparison, and the RNC
datatype parameter blocks remain.

Part of #79
2026-06-08 16:48:24 +02:00
b19e48a6eb feat(schema): RELAX NG pattern model, parser, and derivative validation
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 #11
Closes #12
2026-06-08 06:49:37 +02:00