Commit Graph

8 Commits

Author SHA1 Message Date
2db9915fb2 feat(schematron): support key(), document(), and current() in tests
Schematron tests, lets, and message expressions can now use key() backed
by xsl:key declarations parsed from the schema, document() loading an
external file through an injected documentLoader, and current() resolving
to the rule context node. A node-keyed function-table factory threads
through the evaluation. Completes the Schematron audit.
2026-06-09 03:30:43 +02:00
d7a40b9a7a feat(schematron): instantiate abstract patterns with is-a/param
A <pattern abstract="true"> is a template whose rule queries carry $name
references; a <pattern is-a="…"> with <param> children instantiates it,
substituting each parameter into the template's context, test, <let>
value, and <value-of select=> before the XPath is compiled. The
substitution is a single left-to-right pass, so a substituted value is
never rescanned and a short name never captures a longer one; $name
references that are not parameters stay as written. The template itself
contributes no rules.
2026-06-08 17:58:33 +02:00
b3d535c77e feat(schematron): schema- and pattern-level let
<let> may now appear at schema and pattern level, not just in a rule.
Schema lets are evaluated once at the document root; pattern lets extend
them (also at the root); rule lets extend those at each context node. A
narrower-scope binding overrides a wider one of the same name.

Part of #80
2026-06-08 16:52:16 +02:00
a76e9b3da3 feat(schematron): diagnostics
An assert/report may reference <diagnostic> messages by id through its
diagnostics= attribute; each referenced diagnostic is rendered (with the
same value-of/name/let substitution) and appended to the finding for
extra per-failure detail. <diagnostics> is collected schema-wide.

Schema/pattern-level let, abstract patterns, and key()/document() remain.

Part of #80
2026-06-08 16:30:13 +02:00
7e04f6056c feat(schematron): abstract rules and extends
A <rule abstract="true" id="x"> is a template that does not fire on its
own; a concrete rule pulls in its let bindings and assertions with
<extends rule="x"/>, prepended so the concrete rule extends rather than
replaces them.

Part of #80
2026-06-08 16:26:30 +02:00
d0671e40ba feat(schematron): rule-level let variable bindings
<rule> may now declare <let name= value=> bindings, evaluated at each
context node in order (so a later binding can reference an earlier one
through $name). The bindings are available to the rule's assert/report
tests and to <value-of select="$name"> in messages, using the XPath
engine's existing variable support.

Schema- and pattern-level let, abstract rules/extends, diagnostics, and
key()/document() remain.

Part of #80
2026-06-08 16:23:26 +02:00
68122ad887 feat(schematron): dynamic message content and phase support
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
2026-06-08 15:45:27 +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