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.
The rules-swift concurrency rule's acceptance check requires every `@unchecked
Sendable` and `nonisolated(unsafe)` to carry an adjacent comment explaining why
the race is acceptable; an unjustified one is a violation. An audit found six
escape hatches whose comment explained the type but not the race:
- The four XPath `FunctionTable` statics (`CoreFunctions`, `StringFunctions`,
`NodeFunctions`, `Evaluator.library`): each is an immutable, build-once lookup
table never mutated after initialization, so concurrent reads race on nothing;
`FunctionTable` is un-`Sendable` only because it stores closures.
- `Validation.Validator`: immutable after `init` (every stored validation list is
a `let`); the erased `AnyValidation` closures are simply not provably `Sendable`.
- The test `ResultBox`: written once by the worker thread before its semaphore
signals and read only after the wait, so the write happens-before the read.
Comments only; no behaviour change. `LoaderBox` and `ElementStorage` were already
justified. swiftformat/swiftlint --strict and the full suite stay green.
Node-set ordering previously recomputed both nodes' root paths on every
precedes comparison (treePath is O(depth x siblings), so sorts were
O(n log n x depth)). Add decorate-sort-undecorate helpers,
sortedByDocumentOrder() and firstInDocumentOrder(), that compute each node's
order key once, and switch the evaluator's order-and-dedup, the EXSLT set
functions, string-value coercion, and NodeFunctions' target lookup to them.
set:leading/trailing also decorate once, reusing the keys for the pivot
filter and the final sort. precedes stays for pairwise use, sharing the
key comparator (Node.ordered). Behavior unchanged, all suites green.
Closes#113
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
Add the string functions (concat, starts-with, contains, substring-before,
substring-after, substring, string-length, normalize-space, translate), the
number functions (sum, floor, ceiling, round), and the node functions
(local-name, name, namespace-uri, id, lang), merged into the evaluation
library. All pure Swift, no Foundation: substring search is hand-rolled.
Without a DTD, id() matches an attribute named id or xml:id.
Closes#41