A document-end marker takes no node on its own line: only a comment or blank may follow. The scanner skipped the rest of the marker's line, so content such as the 'invalid' in '... invalid' was silently dropped and the document accepted. scanDocumentMarker now distinguishes the two markers after skipping spaces: a document start may carry the root node on its line and leaves it for the main loop, while a document end rejects any real content (a comment or blank still passes). A '...' not followed by an indicator boundary is not a marker at all and remains an ordinary plain scalar, so '...junk' still parses as content. Measured against the official YAML test-suite, parse-level conformance rises from 255/333 (76.6%) to 256/333 (76.9%) with no regression.
191 lines
8.7 KiB
Swift
191 lines
8.7 KiB
Swift
public extension PureYAML.Parsing {
|
|
/// Parser error with source line information when available.
|
|
enum ParseError: Swift.Error, Equatable, Sendable, CustomStringConvertible {
|
|
case unexpectedIndentation(line: Int)
|
|
case mixedCollectionStyles(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 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)
|
|
case commentNotPrecededByWhitespace(line: Int, column: Int)
|
|
case invalidEscape(sequence: String, line: Int)
|
|
case malformedDirective(name: String, line: Int)
|
|
case duplicateDirective(name: String, line: Int)
|
|
case directiveWithoutDocument(line: Int)
|
|
case contentAfterDocumentEnd(line: Int)
|
|
|
|
public var description: String {
|
|
switch self {
|
|
case let .unexpectedIndentation(line):
|
|
"unexpected indentation at line \(line)"
|
|
case let .mixedCollectionStyles(line):
|
|
"mapping and sequence entries are mixed 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 .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)"
|
|
case let .commentNotPrecededByWhitespace(line, column):
|
|
"a comment must be preceded by whitespace at line \(line), column \(column)"
|
|
case let .invalidEscape(sequence, line):
|
|
"invalid escape sequence '\(sequence)' at line \(line)"
|
|
case let .malformedDirective(name, line):
|
|
"malformed directive '\(name)' at line \(line)"
|
|
case let .duplicateDirective(name, line):
|
|
"duplicate directive '\(name)' at line \(line)"
|
|
case let .directiveWithoutDocument(line):
|
|
"a directive must be followed by a document at line \(line)"
|
|
case let .contentAfterDocumentEnd(line):
|
|
"no content may follow a document end marker at line \(line)"
|
|
}
|
|
}
|
|
|
|
public var diagnosticCode: String {
|
|
switch self {
|
|
case .unexpectedIndentation:
|
|
"unexpectedIndentation"
|
|
case .mixedCollectionStyles:
|
|
"mixedCollectionStyles"
|
|
case .expectedAnchorName:
|
|
"expectedAnchorName"
|
|
case .expectedAliasName:
|
|
"expectedAliasName"
|
|
case .expectedNode:
|
|
"expectedNode"
|
|
case .expectedScalarKey:
|
|
"expectedScalarKey"
|
|
case .incompatibleYAMLDirective:
|
|
"incompatibleYAMLDirective"
|
|
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"
|
|
case .commentNotPrecededByWhitespace:
|
|
"commentNotPrecededByWhitespace"
|
|
case .invalidEscape:
|
|
"invalidEscape"
|
|
case .malformedDirective:
|
|
"malformedDirective"
|
|
case .duplicateDirective:
|
|
"duplicateDirective"
|
|
case .directiveWithoutDocument:
|
|
"directiveWithoutDocument"
|
|
case .contentAfterDocumentEnd:
|
|
"contentAfterDocumentEnd"
|
|
}
|
|
}
|
|
|
|
public var sourceLine: Int? {
|
|
switch self {
|
|
case let .unexpectedIndentation(line),
|
|
let .mixedCollectionStyles(line),
|
|
let .expectedAnchorName(line),
|
|
let .expectedAliasName(line),
|
|
let .incompatibleYAMLDirective(line),
|
|
let .unterminatedTag(line),
|
|
let .unterminatedQuotedString(line),
|
|
let .unsupportedDirective(_, line),
|
|
let .unsupportedMultiDocumentStream(line),
|
|
let .invalidEscape(_, line),
|
|
let .malformedDirective(_, line),
|
|
let .duplicateDirective(_, line),
|
|
let .directiveWithoutDocument(line),
|
|
let .contentAfterDocumentEnd(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, _),
|
|
let .commentNotPrecededByWhitespace(line, _):
|
|
line
|
|
}
|
|
}
|
|
|
|
public var sourceColumn: Int? {
|
|
switch self {
|
|
case .unexpectedIndentation,
|
|
.mixedCollectionStyles,
|
|
.expectedAnchorName,
|
|
.expectedAliasName,
|
|
.incompatibleYAMLDirective,
|
|
.unterminatedTag,
|
|
.unterminatedQuotedString,
|
|
.unsupportedDirective,
|
|
.unsupportedMultiDocumentStream,
|
|
.invalidEscape,
|
|
.malformedDirective,
|
|
.duplicateDirective,
|
|
.directiveWithoutDocument,
|
|
.contentAfterDocumentEnd:
|
|
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),
|
|
let .commentNotPrecededByWhitespace(_, column):
|
|
column
|
|
}
|
|
}
|
|
}
|
|
}
|