Three root causes: (1) a '%' begins a directive only in directive position (before document content, outside flow); everywhere else it is plain content, fixing misfires inside plain scalars, flow mappings, and block scalars. (2) %YAML accepts any 1.x (a higher minor is treated as 1.2 without error), rejects major != 1 and duplicate %YAML, and requires a following '---'; reserved and %TAG directives are accepted and ignored. A directive not followed by a document start is rejected with the new directiveWithoutDocument error. (3) An explicit '...' end marker closes the current document so following content begins a fresh document in parseStream; single-document parse still rejects multi-document input at compose time. Tests/UnsupportedYAMLGapsFixtures.swift, StreamParsingTests, ScannerTests, and ParsingErrorTests were updated in lockstep to assert the now-correct behavior (percent-after-content is plain text; '...' then content is two documents; %YAML 1.3 is valid while 2.0 and duplicate %YAML are rejected); no test was weakened and multi-document rejection for single-document parse is still covered. Flips 15 official YAML test suite cases (295/402); SF5V, 9MMA, B63P, H7TQ now correctly rejected. 6LVF and W4TN are reclassified to json-mismatch (blocked by other scalar gaps). Updates the manifest, fixtures, counts, and roadmap.
178 lines
7.8 KiB
Swift
178 lines
7.8 KiB
Swift
public extension PureYAML.Parsing {
|
|
/// Parser error with source line information when available.
|
|
enum ParseError: Swift.Error, Equatable, Sendable, CustomStringConvertible {
|
|
case emptyDocument
|
|
case tabIndentation(line: Int)
|
|
case unexpectedIndentation(line: Int)
|
|
case mixedCollectionStyles(line: Int)
|
|
case expectedMappingKey(line: Int)
|
|
case expectedAnchorName(line: Int)
|
|
case expectedAliasName(line: Int)
|
|
case expectedNode(line: Int, column: Int)
|
|
case expectedScalarKey(line: Int, column: Int)
|
|
case incompatibleYAMLDirective(line: Int)
|
|
case directiveWithoutDocument(line: Int)
|
|
case invalidTaggedScalar(tag: String, value: String, line: Int, column: Int)
|
|
case invalidMergeValue(line: Int, column: Int)
|
|
case unterminatedTag(line: Int)
|
|
case unterminatedQuotedString(line: Int)
|
|
case unsupportedDirective(name: String, line: Int)
|
|
case unsupportedMultiDocumentStream(line: Int)
|
|
case undefinedAlias(anchor: String, line: Int, column: Int)
|
|
case unexpectedEvent(expected: String, actual: String, line: Int, column: Int)
|
|
case unexpectedToken(expected: String, actual: String, line: Int, column: Int)
|
|
case nestingTooDeep(line: Int, column: Int)
|
|
|
|
public var description: String {
|
|
switch self {
|
|
case .emptyDocument:
|
|
"document is empty"
|
|
case let .tabIndentation(line):
|
|
"tabs are not allowed for indentation at line \(line)"
|
|
case let .unexpectedIndentation(line):
|
|
"unexpected indentation at line \(line)"
|
|
case let .mixedCollectionStyles(line):
|
|
"mapping and sequence entries are mixed at line \(line)"
|
|
case let .expectedMappingKey(line):
|
|
"expected a mapping key at line \(line)"
|
|
case let .expectedAnchorName(line):
|
|
"expected an anchor name at line \(line)"
|
|
case let .expectedAliasName(line):
|
|
"expected an alias name at line \(line)"
|
|
case let .expectedNode(line, column):
|
|
"expected a YAML node at line \(line), column \(column)"
|
|
case let .expectedScalarKey(line, column):
|
|
"expected a scalar mapping key at line \(line), column \(column)"
|
|
case let .incompatibleYAMLDirective(line):
|
|
"incompatible YAML directive at line \(line)"
|
|
case let .directiveWithoutDocument(line):
|
|
"directive is not followed by a document start at line \(line)"
|
|
case let .invalidTaggedScalar(tag, value, line, column):
|
|
"invalid scalar '\(value)' for tag '\(tag)' at line \(line), column \(column)"
|
|
case let .invalidMergeValue(line, column):
|
|
"expected merge value to be a mapping or sequence of mappings at line \(line), column \(column)"
|
|
case let .unterminatedTag(line):
|
|
"unterminated tag at line \(line)"
|
|
case let .unterminatedQuotedString(line):
|
|
"unterminated quoted string at line \(line)"
|
|
case let .unsupportedDirective(name, line):
|
|
"unsupported directive '\(name)' at line \(line)"
|
|
case let .unsupportedMultiDocumentStream(line):
|
|
"multi-document streams are not supported at line \(line)"
|
|
case let .undefinedAlias(anchor, line, column):
|
|
"undefined alias '\(anchor)' at line \(line), column \(column)"
|
|
case let .unexpectedEvent(expected, actual, line, column):
|
|
"expected \(expected) at line \(line), column \(column), found \(actual)"
|
|
case let .unexpectedToken(expected, actual, line, column):
|
|
"expected \(expected) at line \(line), column \(column), found \(actual)"
|
|
case let .nestingTooDeep(line, column):
|
|
"nesting is too deep at line \(line), column \(column)"
|
|
}
|
|
}
|
|
|
|
public var diagnosticCode: String {
|
|
switch self {
|
|
case .emptyDocument:
|
|
"emptyDocument"
|
|
case .tabIndentation:
|
|
"tabIndentation"
|
|
case .unexpectedIndentation:
|
|
"unexpectedIndentation"
|
|
case .mixedCollectionStyles:
|
|
"mixedCollectionStyles"
|
|
case .expectedMappingKey:
|
|
"expectedMappingKey"
|
|
case .expectedAnchorName:
|
|
"expectedAnchorName"
|
|
case .expectedAliasName:
|
|
"expectedAliasName"
|
|
case .expectedNode:
|
|
"expectedNode"
|
|
case .expectedScalarKey:
|
|
"expectedScalarKey"
|
|
case .incompatibleYAMLDirective:
|
|
"incompatibleYAMLDirective"
|
|
case .directiveWithoutDocument:
|
|
"directiveWithoutDocument"
|
|
case .invalidTaggedScalar:
|
|
"invalidTaggedScalar"
|
|
case .invalidMergeValue:
|
|
"invalidMergeValue"
|
|
case .unterminatedTag:
|
|
"unterminatedTag"
|
|
case .unterminatedQuotedString:
|
|
"unterminatedQuotedString"
|
|
case .unsupportedDirective:
|
|
"unsupportedDirective"
|
|
case .unsupportedMultiDocumentStream:
|
|
"unsupportedMultiDocumentStream"
|
|
case .undefinedAlias:
|
|
"undefinedAlias"
|
|
case .unexpectedEvent:
|
|
"unexpectedEvent"
|
|
case .unexpectedToken:
|
|
"unexpectedToken"
|
|
case .nestingTooDeep:
|
|
"nestingTooDeep"
|
|
}
|
|
}
|
|
|
|
public var sourceLine: Int? {
|
|
switch self {
|
|
case .emptyDocument:
|
|
nil
|
|
case let .tabIndentation(line),
|
|
let .unexpectedIndentation(line),
|
|
let .mixedCollectionStyles(line),
|
|
let .expectedMappingKey(line),
|
|
let .expectedAnchorName(line),
|
|
let .expectedAliasName(line),
|
|
let .incompatibleYAMLDirective(line),
|
|
let .directiveWithoutDocument(line),
|
|
let .unterminatedTag(line),
|
|
let .unterminatedQuotedString(line),
|
|
let .unsupportedDirective(_, line),
|
|
let .unsupportedMultiDocumentStream(line):
|
|
line
|
|
case let .expectedNode(line, _),
|
|
let .expectedScalarKey(line, _),
|
|
let .invalidTaggedScalar(_, _, line, _),
|
|
let .invalidMergeValue(line, _),
|
|
let .undefinedAlias(_, line, _),
|
|
let .unexpectedEvent(_, _, line, _),
|
|
let .unexpectedToken(_, _, line, _),
|
|
let .nestingTooDeep(line, _):
|
|
line
|
|
}
|
|
}
|
|
|
|
public var sourceColumn: Int? {
|
|
switch self {
|
|
case .emptyDocument,
|
|
.tabIndentation,
|
|
.unexpectedIndentation,
|
|
.mixedCollectionStyles,
|
|
.expectedMappingKey,
|
|
.expectedAnchorName,
|
|
.expectedAliasName,
|
|
.incompatibleYAMLDirective,
|
|
.directiveWithoutDocument,
|
|
.unterminatedTag,
|
|
.unterminatedQuotedString,
|
|
.unsupportedDirective,
|
|
.unsupportedMultiDocumentStream:
|
|
nil
|
|
case let .expectedNode(_, column),
|
|
let .expectedScalarKey(_, column),
|
|
let .invalidTaggedScalar(_, _, _, column),
|
|
let .invalidMergeValue(_, column),
|
|
let .undefinedAlias(_, _, column),
|
|
let .unexpectedEvent(_, _, _, column),
|
|
let .unexpectedToken(_, _, _, column),
|
|
let .nestingTooDeep(_, column):
|
|
column
|
|
}
|
|
}
|
|
}
|
|
}
|