The two monolithic XSD content/identity rules only satisfied the OpenAPIKit validation idiom at the wrong granularity: no per-constraint removability, no isolable failing/near-miss tests. Split them into 32 named rules, each finding tagged at its origin and cached per XSDContext so the walk still runs once per document (the #101 decision against duplicating XSD's recursive type resolution still holds). Wired the streaming path's ID/IDREF and identity checks through the same framework instead of calling the validators imperatively, and added the unwrapAndLookup combinator the rule already named as required. Also includes the mandated repo-wide swiftformat/swiftlint pass.
58 lines
3.3 KiB
Swift
58 lines
3.3 KiB
Swift
extension PureXML.Regex {
|
|
/// Unicode general categories as XSD 1.0 regular expressions see them
|
|
/// (Datatypes Appendix F). Category and block escapes (`\p{...}`) and the
|
|
/// multi-character escapes `\d`, `\w`, and friends are defined over the
|
|
/// Unicode database. The W3C conformance corpus (XSTS) is internally
|
|
/// consistent only at Unicode 3.2, so the repertoire is pinned there (see
|
|
/// ``assignedInUnicode32``): a code point unassigned in 3.2 reports `Cn` and
|
|
/// so matches `\D`/`\W`, even where the running platform now assigns it.
|
|
///
|
|
/// For code points whose 3.2-era category differs from the running platform's
|
|
/// (for example the ceiling and floor brackets, `Sm` in 3.2 but `Ps`/`Pe`
|
|
/// today), ``categoryOverrides`` pins the 3.2 classification; every other
|
|
/// assigned code point uses the platform category.
|
|
enum XSDCategory {
|
|
/// Code points whose XSD-era general category differs from the running
|
|
/// platform's. The value is the XSD-era two-letter category.
|
|
private static let categoryOverrides: [UInt32: String] = [
|
|
0x00A7: "So", 0x00B6: "So",
|
|
0x06DD: "Me",
|
|
0x0F14: "So",
|
|
0x166D: "Po", 0x17D7: "Po", 0x17DC: "Po",
|
|
0x180B: "Cf", 0x180C: "Cf", 0x180D: "Cf",
|
|
// Ethiopic digits ONE..NINE were Nd at Unicode 3.2 (each carried a
|
|
// decimal value); a later Unicode reclassified them to No. XSD `\d`
|
|
// is `\p{Nd}` over the pinned 3.2 repertoire, so they must match `\d`
|
|
// (and not `\D`). This is the complete set of code points that were
|
|
// Nd at 3.2 and are not Nd today (XSTS reS17/reS38/reT17/reT38/reZ004).
|
|
0x1369: "Nd", 0x136A: "Nd", 0x136B: "Nd", 0x136C: "Nd", 0x136D: "Nd",
|
|
0x136E: "Nd", 0x136F: "Nd", 0x1370: "Nd", 0x1371: "Nd",
|
|
0x2308: "Sm", 0x2309: "Sm", 0x230A: "Sm", 0x230B: "Sm",
|
|
]
|
|
|
|
/// The two-letter XSD category abbreviation for each Unicode general
|
|
/// category. A flat translation table, not branching logic.
|
|
private static let categoryCodes: [Unicode.GeneralCategory: String] = [
|
|
.uppercaseLetter: "Lu", .lowercaseLetter: "Ll", .titlecaseLetter: "Lt",
|
|
.modifierLetter: "Lm", .otherLetter: "Lo",
|
|
.nonspacingMark: "Mn", .spacingMark: "Mc", .enclosingMark: "Me",
|
|
.decimalNumber: "Nd", .letterNumber: "Nl", .otherNumber: "No",
|
|
.connectorPunctuation: "Pc", .dashPunctuation: "Pd", .openPunctuation: "Ps",
|
|
.closePunctuation: "Pe", .initialPunctuation: "Pi", .finalPunctuation: "Pf",
|
|
.otherPunctuation: "Po",
|
|
.mathSymbol: "Sm", .currencySymbol: "Sc", .modifierSymbol: "Sk", .otherSymbol: "So",
|
|
.spaceSeparator: "Zs", .lineSeparator: "Zl", .paragraphSeparator: "Zp",
|
|
.control: "Cc", .format: "Cf", .surrogate: "Cs", .privateUse: "Co",
|
|
.unassigned: "Cn",
|
|
]
|
|
|
|
static func generalCategoryCode(_ scalar: Unicode.Scalar) -> String {
|
|
if let override = categoryOverrides[scalar.value] {
|
|
return override
|
|
}
|
|
guard assignedInUnicode32(scalar.value) else { return "Cn" }
|
|
return categoryCodes[scalar.properties.generalCategory] ?? "Cn"
|
|
}
|
|
}
|
|
}
|