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
23 lines
1.2 KiB
Swift
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)
|
|
}
|
|
}
|