PureXML/Sources/Schema/GroupParticleMemo.swift
Mihaela Mihaljevic e81e4b4df6
feat(schema): carry a stable group-definition identity on shared expansions (#363)
Step 1 of the GSS content-matcher build (docs/design/counted-content-automaton.md).
A multiply-referenced `<xs:group>` expands to one copy-on-write-shared `Particle`
(GroupParticleMemo), but `Group`/`Term` are value types with no exposed identity,
so a content-model compiler cannot tell two references apart and inlines the group
per reference (the 2^K build the GSS matcher will close).

Group now carries a `definitionID: Int?`, stamped from a per-compile counter on
GroupParticleMemo when an expansion is first compiled, so every reference sharing
the memo key reuses the same stamped term and distinct definitions differ. It is
metadata only: Group is not Equatable/Hashable, so it perturbs no determinism or
restriction comparison, and it is nil for an inline group.

FP-neutral by construction (no consumer yet). Full in-repo suite green; the XSTS
buckets are unchanged (valid-schemas-rejected 0, invalid-schemas-accepted 8,
valid-instances-rejected 0, invalid-instances-accepted 14); swiftformat/swiftlint
clean. Step 2 (a call/return content-model build keyed on this id, alongside the
inlining oracle) consumes it.
2026-06-28 01:53:58 +02:00

32 lines
1.5 KiB
Swift

extension PureXML.Schema {
/// The key under which an `<xs:group>` reference's expansion is memoized: the
/// group DEFINITION node and the cycle-visiting set at the reference site. The
/// expansion is determined by these (the namespace scope is fixed per
/// definition), so two references that share them share the result.
struct GroupParticleMemoKey: Hashable {
let definition: ObjectIdentifier
let visiting: Set<String>
}
/// Memoizes group-reference expansion during one schema compile. A
/// multiply-referenced nested group (`g0=(g1,g1), g1=(g2,g2), …`) otherwise
/// rebuilds each subtree per reference, an exponential `2^K` compile; sharing the
/// expansion across sibling references drops the build to `O(groups)`. The
/// resulting `Particle` is semantically identical (a value type, so the shared
/// content term is copy-on-write), only built once. A reference type so it is
/// shared across the value copies `XSDContext.scoped`/`visiting` make; one
/// instance per compile.
final class GroupParticleMemo {
var store: [GroupParticleMemoKey: PureXML.Schema.Particle] = [:]
/// Source of the stable `Group.definitionID` stamped on each distinct
/// expansion as it is first compiled, so every reference sharing the
/// expansion carries the same id and distinct definitions differ.
private var nextID = 0
func allocateDefinitionID() -> Int {
defer { nextID += 1 }
return nextID
}
}
}