A line that begins with a document marker ('---' or '...') followed by a
boundary is forbidden inside a multi-line scalar, since it is ambiguous
with a document boundary. A multi-line quoted scalar folded such a line
into its value instead of rejecting it.
scanQuotedScalar now checks, on crossing each line break, whether the next
line begins a document marker, and rejects the scalar if so. The check
requires the marker at the line start with a following boundary, so inline
dashes ('a --- b'), '---' with no boundary ('---b'), an indented '---',
and dots not at a line start stay ordinary content.
Measured against the official YAML test-suite, parse-level conformance
rises from 268/333 (80.5%) to 270/333 (81.1%) with no regression.
233 lines
11 KiB
Swift
233 lines
11 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)
|
|
case blockSequenceAsInlineMappingValue(line: Int)
|
|
case multilineImplicitKey(line: Int)
|
|
case commentInMultilineScalar(line: Int)
|
|
case aliasWithProperties(line: Int, column: Int)
|
|
case inlineBlockSequenceAfterProperty(line: Int, column: Int)
|
|
case documentMarkerInScalar(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)"
|
|
case let .blockSequenceAsInlineMappingValue(line):
|
|
"a block sequence cannot start on the same line as a mapping value at line \(line)"
|
|
case let .multilineImplicitKey(line):
|
|
"an implicit mapping key must not span multiple lines at line \(line)"
|
|
case let .commentInMultilineScalar(line):
|
|
"a comment cannot interrupt a multi-line plain scalar at line \(line)"
|
|
case let .aliasWithProperties(line, column):
|
|
"an alias node cannot have an anchor or tag at line \(line), column \(column)"
|
|
case let .inlineBlockSequenceAfterProperty(line, column):
|
|
"a block sequence cannot start on the same line as its anchor or tag at line \(line), column \(column)"
|
|
case let .documentMarkerInScalar(line):
|
|
"a document marker cannot appear inside a multi-line scalar 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"
|
|
case .blockSequenceAsInlineMappingValue:
|
|
"blockSequenceAsInlineMappingValue"
|
|
case .multilineImplicitKey:
|
|
"multilineImplicitKey"
|
|
case .commentInMultilineScalar:
|
|
"commentInMultilineScalar"
|
|
case .aliasWithProperties:
|
|
"aliasWithProperties"
|
|
case .inlineBlockSequenceAfterProperty:
|
|
"inlineBlockSequenceAfterProperty"
|
|
case .documentMarkerInScalar:
|
|
"documentMarkerInScalar"
|
|
}
|
|
}
|
|
|
|
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 .documentMarkerInScalar(line),
|
|
let .invalidEscape(_, line),
|
|
let .malformedDirective(_, line),
|
|
let .duplicateDirective(_, line),
|
|
let .directiveWithoutDocument(line),
|
|
let .contentAfterDocumentEnd(line),
|
|
let .blockSequenceAsInlineMappingValue(line),
|
|
let .multilineImplicitKey(line),
|
|
let .commentInMultilineScalar(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, _),
|
|
let .aliasWithProperties(line, _),
|
|
let .inlineBlockSequenceAfterProperty(line, _):
|
|
line
|
|
}
|
|
}
|
|
|
|
public var sourceColumn: Int? {
|
|
switch self {
|
|
case .unexpectedIndentation,
|
|
.mixedCollectionStyles,
|
|
.expectedAnchorName,
|
|
.expectedAliasName,
|
|
.incompatibleYAMLDirective,
|
|
.unterminatedTag,
|
|
.unterminatedQuotedString,
|
|
.unsupportedDirective,
|
|
.unsupportedMultiDocumentStream,
|
|
.documentMarkerInScalar,
|
|
.invalidEscape,
|
|
.malformedDirective,
|
|
.duplicateDirective,
|
|
.directiveWithoutDocument,
|
|
.contentAfterDocumentEnd,
|
|
.blockSequenceAsInlineMappingValue,
|
|
.multilineImplicitKey,
|
|
.commentInMultilineScalar:
|
|
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),
|
|
let .aliasWithProperties(_, column),
|
|
let .inlineBlockSequenceAfterProperty(_, column):
|
|
column
|
|
}
|
|
}
|
|
}
|
|
}
|