Rewrite literal (|) and folded (>) block scalar content to YAML 1.2 section 8.1: preserve interior blank lines, apply 8.1.3 line folding (a single break between equally indented non-empty lines folds to a space, while blank lines and more-indented lines keep their breaks), preserve tabs in content (only block-indentation columns are stripped), and support the + keep chomping indicator alongside clip and strip. The block scalar is now scanned as a single unit (header line plus all content lines) with indentation auto-detection in Scanner.BlockScalar.swift, mirroring the reference scanner, instead of being reassembled from a lossy per-line token array. Flips 24 official YAML test suite cases to passing (220/402). Updates the gap manifest, vendored fixtures, count assertions, and the conformance roadmap.
106 lines
3.5 KiB
Swift
106 lines
3.5 KiB
Swift
extension PureYAML.Parsing.Scanner {
|
|
func scanIndentation(_ state: inout State) throws {
|
|
let start = state.reader.mark
|
|
let width = try scanIndentationWidth(&state)
|
|
guard shouldProcessIndentation(state) else {
|
|
return
|
|
}
|
|
|
|
let end = state.reader.mark
|
|
let current = state.indentation.last ?? 0
|
|
|
|
if width > current {
|
|
applyIncreasedIndentation(width: width, start: start, end: end, state: &state)
|
|
} else if width < current {
|
|
try applyDedentation(width: width, start: start, end: end, state: &state)
|
|
}
|
|
|
|
pruneImplicitIndentation(&state)
|
|
}
|
|
|
|
func scanIndentationWidth(_ state: inout State) throws -> Int {
|
|
var width = 0
|
|
while let character = state.reader.peek() {
|
|
if character == " " {
|
|
width += 1
|
|
state.reader.advance()
|
|
} else if character == "\t" {
|
|
width = nextTabStop(after: width)
|
|
state.reader.advance()
|
|
} else {
|
|
return width
|
|
}
|
|
}
|
|
return width
|
|
}
|
|
|
|
func nextTabStop(after width: Int) -> Int {
|
|
((width / 4) + 1) * 4
|
|
}
|
|
|
|
func shouldProcessIndentation(_ state: State) -> Bool {
|
|
guard !state.reader.isAtEnd, !isLineBreak(state.reader.peek()) else {
|
|
return false
|
|
}
|
|
// A leading '#' starts a comment line, which must not drive indent/dedent
|
|
// processing. Block scalar content (including lines that begin with '#') is
|
|
// consumed in full by the block scalar scanner before control returns here,
|
|
// so this check no longer needs to special-case block scalars.
|
|
if state.reader.peek() == "#" {
|
|
return false
|
|
}
|
|
return state.flowDepth == 0
|
|
}
|
|
|
|
func applyIncreasedIndentation(
|
|
width: Int,
|
|
start: PureYAML.Parsing.Mark,
|
|
end: PureYAML.Parsing.Mark,
|
|
state: inout State,
|
|
) {
|
|
state.indentation.append(width)
|
|
state.append(.indent(width: width), mark: start, endMark: end)
|
|
}
|
|
|
|
func applyDedentation(
|
|
width: Int,
|
|
start: PureYAML.Parsing.Mark,
|
|
end: PureYAML.Parsing.Mark,
|
|
state: inout State,
|
|
) throws {
|
|
while state.indentation.count > 1, width < (state.indentation.last ?? 0) {
|
|
state.indentation.removeLast()
|
|
state.append(.dedent(width: state.indentation.last ?? 0), mark: end, endMark: end)
|
|
}
|
|
if canUseImplicitIndentation(width: width, state: state) {
|
|
state.indentation.append(width)
|
|
state.append(.indent(width: width), mark: start, endMark: end)
|
|
}
|
|
guard width == state.indentation.last else {
|
|
throw PureYAML.Parsing.ParseError.unexpectedIndentation(line: end.line)
|
|
}
|
|
}
|
|
|
|
func canUseImplicitIndentation(
|
|
width: Int,
|
|
state: State,
|
|
) -> Bool {
|
|
width != state.indentation.last
|
|
&& width > (state.indentation.last ?? 0)
|
|
&& state.validImplicitIndentation.contains(width)
|
|
}
|
|
|
|
func pruneImplicitIndentation(_ state: inout State) {
|
|
let current = state.indentation.last ?? 0
|
|
state.validImplicitIndentation = state.validImplicitIndentation.filter { $0 <= current }
|
|
}
|
|
|
|
func closeIndentation(_ state: inout State) {
|
|
let mark = state.reader.mark
|
|
while state.indentation.count > 1 {
|
|
state.indentation.removeLast()
|
|
state.append(.dedent(width: state.indentation.last ?? 0), mark: mark, endMark: mark)
|
|
}
|
|
}
|
|
}
|