Read element-vs-choice Particle Valid (Restriction) through effective total range: an element validly restricts a base choice when its leaf is a NameAndTypeOK restriction of an element the choice admits and its effective occurrence range is within the choice's. This fixes particlesZ001 (the RecurseAsIfGroup rule its own test calls ambiguous; W3C reads it valid) while keeping particlesM002/003 valid and particlesL rejected. Named, bounded under-rejection (exact in-branch sequencing unmodelled), the production-sanctioned direction: invalid-schemas-accepted 128->130, invalid-instances-accepted 132->133 (particlesZ001.i, a masked gap now exposed). XSTS valid-schemas-rejected 1->0. With valid-instances-rejected already 0, false positives are now 0 in both buckets (production stopper #1).
52 lines
2.4 KiB
Swift
52 lines
2.4 KiB
Swift
extension PureXML.Schema.ParticleRestriction {
|
|
/// Every element-declaration term reachable as a leaf of `group` (recursing
|
|
/// into nested groups; wildcards are not elements). Used to test whether a
|
|
/// restricting element names something the base group admits.
|
|
static func elementLeaves(of group: PureXML.Schema.Group) -> [PureXML.Schema.Term] {
|
|
group.particles.flatMap { particle -> [PureXML.Schema.Term] in
|
|
switch particle.term {
|
|
case .element: [particle.term]
|
|
case let .group(inner): elementLeaves(of: inner)
|
|
case .wildcard: []
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Validation strength of a wildcard's `processContents`, for the Wildcard
|
|
/// Subset rule: `strict` is stronger than `lax` is stronger than `skip`.
|
|
static func processContentsRank(_ processContents: PureXML.Schema.ProcessContents) -> Int {
|
|
switch processContents {
|
|
case .skip: 0
|
|
case .lax: 1
|
|
case .strict: 2
|
|
}
|
|
}
|
|
|
|
/// NameAndTypeOK value-constraint clause: when the base element is `fixed`, the
|
|
/// restricting element must also be `fixed` to the same value (compared in the
|
|
/// base type's value space, so `1` and `01` match). A base with no fixed value,
|
|
/// or a `default`, imposes no constraint on the restriction.
|
|
static func fixedValueOK(
|
|
_ restrictedValue: PureXML.Schema.ValueConstraint?,
|
|
_ baseValue: PureXML.Schema.ValueConstraint?,
|
|
_ baseType: PureXML.Schema.ElementType?,
|
|
_ types: [String: PureXML.Schema.ElementType],
|
|
) -> Bool {
|
|
guard let baseFixed = baseValue?.fixedValue else { return true }
|
|
guard let restrictedFixed = restrictedValue?.fixedValue else { return false }
|
|
return atomicType(baseType, types)?.valueMatches(restrictedFixed, literal: baseFixed) ?? (restrictedFixed == baseFixed)
|
|
}
|
|
|
|
/// The atomic simple type an element's type resolves to (following a
|
|
/// `typeReference` through the types map, bounded against cycles), or nil for a
|
|
/// complex, list, or union type.
|
|
static func atomicType(_ type: PureXML.Schema.ElementType?, _ types: [String: PureXML.Schema.ElementType], _ depth: Int = 0) -> PureXML.Schema.SimpleType? {
|
|
guard depth < 32 else { return nil }
|
|
switch type {
|
|
case let .simple(simple): return simple
|
|
case let .typeReference(key): return atomicType(types[key], types, depth + 1)
|
|
default: return nil
|
|
}
|
|
}
|
|
}
|