A long <xs:simpleType> restriction chain is flat in the document, so the same base was resolved repeatedly: the named simple types were resolved by an N-pass fixpoint, and the facet-applicability variety check re-scanned the schema's children to find each base at every link of every type's chain (compiling a 600-link chain took ~4.4s). Named simple types are now resolved once in dependency order (XSDSimpleTypeOrder), and the variety check uses an O(1) name map and memoizes each type's variety (SchemaSimpleTypeVariety); the same chain now compiles in ~30ms. Behaviour-preserving, verified conformance-neutral over the full W3C XSTS corpus, the determinism differential, and the full suite.
62 lines
3.6 KiB
Swift
62 lines
3.6 KiB
Swift
extension PureXML.Schema.XSDParser {
|
|
/// The lookups a simple-type variety walk needs, bundled so the walk's base resolution
|
|
/// is O(1) (the name map) and threadable without a long parameter list.
|
|
struct VarietyLookup {
|
|
let byName: [String: XSDTree]
|
|
let bindings: [String: String]
|
|
let target: String?
|
|
|
|
/// Built from a schema element: its global `simpleType` definitions keyed by name
|
|
/// (first declaration wins, matching the prior first-match scan), so the variety
|
|
/// walk looks a base up in O(1) instead of re-scanning the schema's children at
|
|
/// every link of a `restriction` chain (the former O(n^3) on a long, hostile chain).
|
|
init(schema: XSDTree, bindings: [String: String], target: String?) {
|
|
var byName: [String: XSDTree] = [:]
|
|
for child in PureXML.Schema.XSDNode.elementChildren(schema) where PureXML.Schema.XSDNode.localName(child) == "simpleType" {
|
|
if let name = PureXML.Schema.XSDNode.attribute(child, "name"), byName[name] == nil { byName[name] = child }
|
|
}
|
|
self.byName = byName
|
|
self.bindings = bindings
|
|
self.target = target
|
|
}
|
|
}
|
|
|
|
/// The variety (`"list"`, `"union"`, or nil for atomic/unknown) a `restriction`
|
|
/// derives from: an inline `simpleType` base wins, otherwise the `base` QName.
|
|
/// `cache` memoizes each named type's variety, so a chain is walked once, not per type.
|
|
static func restrictionBaseVariety(_ restriction: XSDTree, _ lookup: VarietyLookup, _ visited: Set<String>, _ cache: inout [String: String?]) -> String? {
|
|
if let inlineBase = PureXML.Schema.XSDNode.firstChild(restriction, named: "simpleType") {
|
|
return simpleTypeVariety(inlineBase, lookup, visited, &cache)
|
|
}
|
|
guard let base = PureXML.Schema.XSDNode.attribute(restriction, "base") else { return nil }
|
|
return namedTypeVariety(base, lookup, visited, &cache)
|
|
}
|
|
|
|
private static func simpleTypeVariety(_ simpleType: XSDTree, _ lookup: VarietyLookup, _ visited: Set<String>, _ cache: inout [String: String?]) -> String? {
|
|
if PureXML.Schema.XSDNode.firstChild(simpleType, named: "list") != nil { return "list" }
|
|
if PureXML.Schema.XSDNode.firstChild(simpleType, named: "union") != nil { return "union" }
|
|
if let restriction = PureXML.Schema.XSDNode.firstChild(simpleType, named: "restriction") {
|
|
return restrictionBaseVariety(restriction, lookup, visited, &cache)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
/// The variety of a named base, resolved namespace-aware so a same-local-name
|
|
/// collision never flips the variety: a base in the XSD namespace is a built-in
|
|
/// (only the list datatypes are list-variety; the rest atomic, nil); a base in
|
|
/// the schema's own target namespace resolves to a declared global `simpleType`
|
|
/// (cycle-guarded); a base in any other namespace is external and skipped (nil).
|
|
private static func namedTypeVariety(_ base: String, _ lookup: VarietyLookup, _ visited: Set<String>, _ cache: inout [String: String?]) -> String? {
|
|
let (prefix, local) = splitQName(base)
|
|
let uri = prefix.map { lookup.bindings[$0] } ?? lookup.bindings[""]
|
|
if uri == xsdNamespace {
|
|
return builtinListTypes.contains(local) ? "list" : nil
|
|
}
|
|
if let cached = cache[local] { return cached }
|
|
guard uri == lookup.target, !visited.contains(local), let declared = lookup.byName[local] else { return nil }
|
|
let variety = simpleTypeVariety(declared, lookup, visited.union([local]), &cache)
|
|
cache[local] = variety
|
|
return variety
|
|
}
|
|
}
|