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.
A >> follow-annotation after a pattern (a foreign element name with
optional [ ... ] content) is skipped, chaining and combining with leading
annotations; neither carries schema semantics. Namespace declarations
inside annotations were already dropped by the annotation skipper, so the
RNC compact syntax is now fully covered.
A document() reference with a #fragment selects a subset of the loaded
document via XPointer; a node-set first argument unions the documents
named by each node's string value; and a relative URI is resolved against
a caller-supplied baseURI (the loader is wrapped so xsl:include/xsl:import
resolve the same way). Closes the XSLT function/output audit.
An xsl:variable with a body now binds a queryable document fragment (its
instantiated result converted to a TreeNode document) instead of a plain
string, so count($rtf/*), $rtf/child, and for-each over $rtf/... work.
It still has its concatenated text as a string value, so existing string
uses are unchanged. Part of #82.
The text content of the named result elements is emitted in CDATA
sections rather than escaped: the result tree's text children of those
elements are converted to CDATA nodes before serialization, leaving
nested and other elements unaffected. Part of #82.
When doctype-system (optionally with doctype-public) is set on xsl:output,
a <!DOCTYPE> for the result's root element is emitted before the
serialized output: PUBLIC when both identifiers are given, SYSTEM
otherwise. Works for the XML and HTML output methods. Part of #82.
key(name, node-set), including key('k', .), now unions the indexed nodes
for every value node's string value and de-duplicates, instead of using
only the first node's string value. A string value still works directly.
Part of #82.
An xsl:output method=html result is serialized through PureXML.HTML
(void elements emitted without a self-closing slash, raw-text elements
like script/style left unescaped) instead of as XML. Part of #82.
case-order=upper-first/lower-first makes the string comparison
case-insensitive, with case used only to break ties among otherwise-equal
strings; without it the default codepoint order is unchanged. Part of #82.
generate-id returns a stable per-node identifier (the context node when
no argument is given, the empty string for an empty node-set);
system-property reports the XSLT version and vendor; element-available and
function-available answer against the implemented instruction and function
sets; unparsed-entity-uri is a stub. Part of #82.
A literal result element and its attributes in a stylesheet namespace
bound by stylesheet-prefix are rewritten on output to the namespace and
prefix bound by result-prefix (#default supported), so a stylesheet can
emit literal xsl: elements. Prefixes resolve through the in-scope xmlns
declarations; aliases fold across include/import. Splits AVT parsing into
XSLTAVT.swift and trims the transformer to stay within length budgets.
format-number now honors named and default decimal formats: the decimal
separator, grouping separator, percent, minus sign, zero-digit (for
non-Latin digit sets), NaN, and infinity symbols come from the chosen
xsl:decimal-format, in both the picture and the output. FormatNumber is
parameterized over the symbol set and the formats thread through the
function table; folded across include/import.
An unrecognized XSLT element instantiates its xsl:fallback children in its
place, or is dropped when it has none; a fallback under a supported
instruction is ignored. The fallback content is handled in the instantiate
loop alongside variables, so the evaluate dispatch stays within budget.
Re-applies templates to the current node in the current mode, considering
only templates of lower import precedence than the one being instantiated,
falling back to the built-in rule when none match. Threads the current
mode and import precedence through the evaluation context, and moves the
transformer runtime types into XSLTRuntime.swift (namespaced, via private
aliases) to stay within the file-length and namespacing gates.
Only the static deduplicated helper moves to XSLTElements.swift (it needs
no private types); attributeSetAttributes stays in the transformer so the
evaluation context and result-item types remain file-private, satisfying
the one-file-scope-type namespacing gate.
Named attribute sets apply to a literal result element or xsl:element via
use-attribute-sets; sets may include other sets recursively with a cycle
guard, set attributes are lowest precedence so the element's own
attributes override them, and same-named attributes collapse to the last.
Folded across xsl:include/xsl:import. Splits the element-building helpers
into XSLTElements.swift to stay within the file-length budget.
xsl:message instantiates its body as diagnostic text and produces no
result-tree output; terminate=yes records the text and aborts the
transform with an XSLTError.terminated, halting further instantiation.
Splits the transform entry points into XSLTTransform.swift and moves the
message case to keep within the complexity and file-length budgets.
Whitespace-only text nodes are removed from the source tree for elements
named by strip-space before transformation, with preserve-space and a
more specific name test winning over *, and xml:space=preserve on a
source element or ancestor overriding the stripping. The name sets fold
across xsl:include/xsl:import. Refactors the stylesheet compile loop
through a Parts accumulator to absorb the new top-level elements within
the complexity budget.
A RELAX NG attribute failure now reports an undeclared attribute
("attribute @b is not allowed on <n>") separately from a declared
attribute carrying a bad value ("attribute @count on <n>: 'abc' is not a
valid integer"), quoting the value and naming its datatype by reusing the
text detail. The declared value pattern is found by matching the
attribute name through the residual.
A RELAX NG text/datatype mismatch now reports the rich datatype reason
(quoting the value and naming the type, e.g. 'abc' is not a valid
integer) and a value mismatch quotes both the actual text and the
required literal, instead of a generic "text content is not valid". The
detail is peeled from the content pattern at the failure point through
choice, group, list, and ref.
An element whose children break its content model now names each stray
child outside the model's alphabet individually, and reports a pure
order/count violation once with the allowed elements as a hint, instead
of a single opaque "do not match its content model". Adds an alphabet
extractor to the content-model matcher. This brings DTD to the same
located standard as the XSD, RELAX NG, and Schematron validators, so all
four schema languages give editor-grade diagnostics.
A content-model violation is now placed at the offending child with a
recovery hint naming what was expected, instead of one opaque "content
does not match the content model" per element. The sequence/choice
automaton is walked child by child via its follow-set to flag the first
child the model rejects, or the incomplete content at the end; an xs:all
group recovers past each stray child and reports every missing required
member. Well-placed children are still validated for their own content,
so an editor sees every problem at once. Brings XSD to the same located,
recovering standard as the RELAX NG, DTD, and Schematron validators.
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.
A prefer attribute on a <group> (or nested <catalog>) overrides the
catalog-wide preference for the <public> entries within it, inherited
through nested groups. The resolver records each public entry's effective
preference and consults it when falling back from an unmatched system
identifier, so two entries in one catalog can prefer differently. A system
match still always wins.
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.
A string literal split with ~ ("a" ~ "b") is the single literal "ab",
the way long values and namespace URIs are wrapped across lines. The fold
applies wherever a string literal is read: value literals and the URIs of
namespace/datatypes/include/external declarations.
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.
Adds a datatypeLibrary-aware datatype resolver to the XML-syntax parser:
the in-scope library is inherited from the nearest ancestor, the default
(empty) library defines only string and token, and the W3C XML Schema
library defines the full built-in set. A type the in-scope library does
not define is an unknown datatype, so the <data>/<value> pattern matches
nothing rather than silently validating.
Adds an nsNameExcept name-class case so nsName with an <except> child
(XML syntax) and prefix:* - name subtraction (compact syntax) narrow a
namespace wildcard. The compact lexer splits prefix:* into a trailing-
colon word plus a star symbol, which the parser now recognizes.
Existing RELAX NG facet tests declared XSD types without the library and
relied on lenient resolution; they now declare the XSD datatypeLibrary,
matching what a conformant processor requires.
The compact-syntax parser now skips [ ... ] documentation/grammar
annotations (balanced over nesting) wherever they may appear, before a
define or before a pattern, and treats div { ... } as transparent
grammar grouping that contributes its defines and start directly. A
define literally named div still parses when assigned. Splits the
name-class and token helpers into a companion file to stay within the
type and file length budget.
Resolver(sgml:) parses the legacy SGML catalog format: PUBLIC, SYSTEM,
DELEGATE, CATALOG, BASE, and OVERRIDE entries with -- comment --
delimiters. Replacement URIs resolve against BASE and the supplied
baseURI; OVERRIDE YES/NO maps to the prefer-public fallback. Unknown
keywords are skipped tolerantly. Feeds the same Resolver as the XML
catalog.
Part of #86
The compact-syntax name class * - nameClass now parses as anyName except
the subtracted class (the model already had anyNameExcept). ns:* - name
would need an nsName-except model case and is left as a refinement.
Part of #79
The RNC datatype parameter block xsd:int { minInclusive = "5" } is now
parsed into facets (previously skipped), matching the XML-syntax <param>
support. The facet mapping is shared by both parsers through a new
RelaxNGFacets helper.
Part of #79
<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
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
The validation framework already conforms to the OpenAPIKit idiom: named
composable Validation values, a Validator with blank/validating/
withoutValidating, located errors, parameterized over both subject and
document. The DTD layer is already four named rules.
XSD content validation stays a single recursive Validation
(contentValidity) rather than a rule per constraint, because XSD is a
recursive type system: a child's type is resolved from its parent's
content model and position, then rewritten by xsi:type and the derivation
controls mid-walk, so the constraint categories are interdependent and
cannot be split into independent rules without changing semantics or
duplicating the recursion. That is the sanctioned multi-error form.
To keep each category verifiable on its own (the idiom's testability),
add XSDCategoryTests exercising ComplexValidator directly per category
(attributes, content, nillable, fixed); derivation, wildcards, and
namespaces are already isolation-tested through crafted schema strings.
Closes#101
- cdataAsText emits a CDATA section's content as escaped text instead of
a <![CDATA[ ... ]]> section.
- asciiOnly escapes every non-ASCII character as a numeric character
reference, so the output is pure ASCII (libxml2's character-reference
encoding).
Both apply to the Serializer and the incremental Writer through the
shared Escaping rules.
Closes#91
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