Parse the XML declaration's pseudo-attributes instead of skipping the <?xml?> processing instruction: - version, encoding, and standalone are surfaced on a new XMLDeclaration, returned from parseWithDocumentType and via PureXML.xmlDeclaration(_:). - The pseudo-attributes are validated: fixed order (version, encoding, standalone), known names only, and standalone must be yes or no. A malformed declaration is rejected. - The declared version is now readable, a prerequisite for XML 1.1. parseWithDocumentType returns a ParsedDocument struct (node, documentType, declaration) rather than a 2-tuple; the member names are unchanged so existing call sites are unaffected. Closes #95
51 lines
2.2 KiB
Swift
51 lines
2.2 KiB
Swift
public extension PureXML.Parsing {
|
|
/// A located problem found while reading a document. Unlike a thrown
|
|
/// ``ParseError``, a diagnostic does not abort the read: the recovering reader
|
|
/// records it, repairs as best it can, and continues, so an invalid document
|
|
/// still yields a best-effort tree alongside the list of what went wrong.
|
|
struct Diagnostic: Equatable, Sendable, CustomStringConvertible {
|
|
/// A human-readable, already-located description of the problem.
|
|
public var message: String
|
|
/// The source position, when known.
|
|
public var mark: Mark?
|
|
|
|
public init(message: String, mark: Mark? = nil) {
|
|
self.message = message
|
|
self.mark = mark
|
|
}
|
|
|
|
public var description: String {
|
|
message
|
|
}
|
|
}
|
|
}
|
|
|
|
extension PureXML.Parsing.Diagnostic {
|
|
/// Wraps a thrown ``PureXML/Parsing/ParseError`` as a diagnostic, keeping its
|
|
/// located message and structured position.
|
|
init(_ error: PureXML.Parsing.ParseError) {
|
|
self.init(message: error.description, mark: error.mark)
|
|
}
|
|
}
|
|
|
|
public extension PureXML.Parsing.ParseError {
|
|
/// The source position the error occurred at, when the case carries one.
|
|
var mark: PureXML.Parsing.Mark? {
|
|
switch self {
|
|
case .emptyDocument, .malformedEncoding, .notImplemented:
|
|
nil
|
|
case let .unexpectedEndOfInput(mark), let .unterminatedTag(mark), let .unterminatedComment(mark),
|
|
let .unterminatedCDATA(mark), let .expectedName(mark), let .expectedEquals(mark),
|
|
let .unquotedAttributeValue(mark), let .amplificationLimitExceeded(mark),
|
|
let .junkAfterDocumentElement(mark), let .unsupportedDoctype(mark), let .malformedDeclaration(mark):
|
|
mark
|
|
case let .unexpectedCharacter(_, mark), let .mismatchedEndTag(_, _, mark),
|
|
let .duplicateAttribute(_, mark), let .invalidReference(_, mark),
|
|
let .undefinedEntity(_, mark), let .recursiveEntity(_, mark),
|
|
let .unexpectedEndTag(_, mark), let .undefinedNamespacePrefix(_, mark),
|
|
let .nestingTooDeep(_, mark), let .nameTooLong(_, mark), let .contentTooLong(_, mark):
|
|
mark
|
|
}
|
|
}
|
|
}
|