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.
76 lines
3.2 KiB
Swift
76 lines
3.2 KiB
Swift
/// Content markup dispatch, in its own file to keep the reader's primary type
|
|
/// body under the length cap. Behavior is part of EventReader proper.
|
|
extension PureXML.Parsing.EventReader {
|
|
/// At a '<' in element content, dispatches to the matching markup scanner.
|
|
/// The caller has confirmed the lead is '<' and set the event start. Shared
|
|
/// by the byte and Character dispatch paths so the two cannot diverge.
|
|
///
|
|
/// On the byte fast path the single byte after '<' selects the branch ('/'
|
|
/// end tag, '?' PI, '!' a declaration, otherwise a start tag), so an
|
|
/// element no longer runs up to five literal string comparisons before the
|
|
/// start-tag branch wins. The Character path below is the exact fallback for
|
|
/// a streaming source or spliced entity text, and a '<!' that matches none
|
|
/// of comment/CDATA/DOCTYPE still falls through to the start-tag scanner,
|
|
/// which reports the same `expectedName` error as before.
|
|
mutating func scanContentMarkup() throws -> Event {
|
|
switch reader.peekByte(1) {
|
|
case 0x2F: // '/'
|
|
try scanEndTag()
|
|
case 0x3F: // '?'
|
|
try scanContentProcessingInstruction()
|
|
case 0x21: // '!'
|
|
try scanContentDeclaration()
|
|
case .some:
|
|
try scanStartTag()
|
|
case nil:
|
|
try scanContentMarkupViaCharacters()
|
|
}
|
|
}
|
|
|
|
/// The Character-path markup dispatch, used off the byte fast path (a
|
|
/// streaming source or spliced entity text). Equivalent to the byte switch
|
|
/// in `scanContentMarkup`.
|
|
private mutating func scanContentMarkupViaCharacters() throws -> Event {
|
|
if reader.matches("</") {
|
|
return try scanEndTag()
|
|
}
|
|
if reader.matches("<?") {
|
|
return try scanContentProcessingInstruction()
|
|
}
|
|
if reader.matches("<!") {
|
|
return try scanContentDeclaration()
|
|
}
|
|
return try scanStartTag()
|
|
}
|
|
|
|
/// Scans a `<!...` construct in element content: a comment, a CDATA
|
|
/// section, or a rejected DOCTYPE. A `<!` matching none of these falls
|
|
/// through to the start-tag scanner, which reports the same `expectedName`
|
|
/// error as before.
|
|
private mutating func scanContentDeclaration() throws -> Event {
|
|
if reader.matches("<!--") {
|
|
recordEmptyElementContent("a comment")
|
|
return try scanComment()
|
|
}
|
|
if reader.matches("<![CDATA[") {
|
|
return try scanCDATA()
|
|
}
|
|
if reader.matches("<!DOCTYPE") {
|
|
throw PureXML.Parsing.ParseError.unsupportedDoctype(reader.mark)
|
|
}
|
|
return try scanStartTag()
|
|
}
|
|
|
|
/// Scans a processing instruction in element content, rejecting the
|
|
/// reserved `xml` target (legal only as the document's leading declaration).
|
|
private mutating func scanContentProcessingInstruction() throws -> Event {
|
|
let mark = reader.mark
|
|
recordEmptyElementContent("a processing instruction")
|
|
let instruction = try scanProcessingInstruction()
|
|
if instruction.target.lowercased() == "xml" {
|
|
throw PureXML.Parsing.ParseError.reservedProcessingInstructionTarget(mark)
|
|
}
|
|
return .processingInstruction(target: instruction.target, data: instruction.data)
|
|
}
|
|
}
|