PureXML/Sources/Schema/RelaxNGRestrictions.swift
Mihaela Mihaljevic 3415ff3d9a feat(relaxng): schema-correctness validation, spec suite complete
A RELAX NG schema document is validated before pattern interpretation
in three layers: the section-3 grammar check (vocabulary, attributes,
arity, text, QNames, datatypeLibrary URIs, hrefs, 4.16 excepts, xmlns
prohibitions), compiler checks (4.17 combine rules, unresolved refs,
parentRef scope, startless grammars, include/externalRef cycles and
overrides, grammar-checked loaded documents, unknown datatypes,
built-in-library params), and the 4.19/7.1-7.4 restrictions over the
compiled algebra after 4.20/4.21 normalization (prohibited paths,
content types, duplicate attributes via representative-name overlap,
infinite-class attributes outside oneOrMore, interleave constraints),
with recursion detection deliberately before normalization. Incorrect
schemas: 213 compiling down to 7, all the pre-Fifth-Edition name-class
class (count asserted). Two leniency tests updated to spec behavior.

Closes #131
2026-06-10 11:38:45 +02:00

23 lines
1.2 KiB
Swift

/// The RELAX NG restrictions of spec sections 4.19 and 7, checked over the
/// compiled pattern algebra (refs resolved through the define table, elements
/// as context boundaries): prohibited paths (7.1), computable content types
/// (7.2), duplicate attributes (7.3), interleave constraints (7.4), the xmlns
/// name-class prohibition (4.16), and ref recursion not guarded by an element
/// (4.19).
enum RelaxNGRestrictions {
typealias Pattern = PureXML.Schema.Pattern
typealias NameClass = PureXML.Schema.NameClass
static func violation(start: Pattern, defines: [String: Pattern]) -> String? {
// Recursion detection runs on the raw patterns: it happens before the
// notAllowed normalization (4.19 precedes 4.20).
if let problem = RelaxNGRestrictionChecker(defines: defines).recursionProblem(start: start) {
return problem
}
let checker = RelaxNGRestrictionChecker(defines: defines.mapValues(RelaxNGRestrictionChecker.normalized))
let normalizedStart = RelaxNGRestrictionChecker.normalized(start)
if let problem = checker.startProblem(normalizedStart) { return problem }
return checker.elementProblems(normalizedStart)
}
}