fix(parsing): fold line breaks in multi-line single-quoted scalars
Single-quoted scalars share the same flow line folding as double-quoted,
which the previous commit added: a single break folds to a space, a blank
line to one newline, whitespace adjacent to a break is stripped, and the
first-line leading and last-line trailing whitespace are kept. The
single-quoted decoder kept its raw breaks; it now reuses the shared
foldFlowScalarLineBreaks helper, while doubled quotes ('') still escape to
one quote.
Measured against the official YAML test-suite, value-level conformance
rises from 143/169 (84.6%) to 147/169 (87.0%) with no regression.
Parse-level is unchanged: these documents already parsed, just to the
wrong tree.
This commit is contained in:
parent
08e1979085
commit
27e8defac6
@ -23,6 +23,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
### Fixed
|
||||
|
||||
- A multi-line single-quoted scalar now folds its line breaks, like the double-quoted fix
|
||||
below; both share the same flow line folding, so the fold logic is now one helper. Lifts
|
||||
official test-suite value-level conformance from 143/169 (84.6%) to 147/169 (87.0%), with
|
||||
no regression.
|
||||
- A multi-line double-quoted scalar now folds its line breaks instead of keeping them raw.
|
||||
The decoder appended real line breaks and continuation-line indentation verbatim, so
|
||||
`"a\n b"` came back as `"a\n b"` rather than `"a b"`. It now applies flow folding: a
|
||||
|
||||
@ -112,17 +112,22 @@ extension PureYAML.Parsing.Parser {
|
||||
}
|
||||
let inner = text.dropFirst().dropLast()
|
||||
var output = ""
|
||||
var iterator = inner.makeIterator()
|
||||
while let character = iterator.next() {
|
||||
if character == "'", let next = iterator.next() {
|
||||
if next == "'" {
|
||||
output.append("'")
|
||||
} else {
|
||||
output.append(character)
|
||||
output.append(next)
|
||||
var index = inner.startIndex
|
||||
while index < inner.endIndex {
|
||||
let character = inner[index]
|
||||
if character == "'" {
|
||||
// A doubled quote escapes to a single quote; a lone quote (not normally present
|
||||
// inside the stripped body) is kept as-is.
|
||||
inner.formIndex(after: &index)
|
||||
output.append("'")
|
||||
if index < inner.endIndex, inner[index] == "'" {
|
||||
inner.formIndex(after: &index)
|
||||
}
|
||||
} else if character == "\n" || character == "\r" {
|
||||
foldFlowScalarLineBreaks(in: inner, index: &index, to: &output)
|
||||
} else {
|
||||
output.append(character)
|
||||
inner.formIndex(after: &index)
|
||||
}
|
||||
}
|
||||
return output
|
||||
@ -148,7 +153,7 @@ extension PureYAML.Parsing.Parser {
|
||||
}
|
||||
appendDoubleQuotedEscape(in: inner, index: &index, to: &output)
|
||||
} else if character == "\n" || character == "\r" {
|
||||
foldDoubleQuotedLineBreaks(in: inner, index: &index, to: &output)
|
||||
foldFlowScalarLineBreaks(in: inner, index: &index, to: &output)
|
||||
} else {
|
||||
output.append(character)
|
||||
inner.formIndex(after: &index)
|
||||
@ -157,12 +162,12 @@ extension PureYAML.Parsing.Parser {
|
||||
return output
|
||||
}
|
||||
|
||||
/// Flow line folding for a double-quoted scalar at an unescaped line break. Whitespace adjacent
|
||||
/// to the break is stripped (the current line's trailing, each following line's leading), then a
|
||||
/// single break folds to one space and a run of breaks (blank lines) folds to one fewer newline.
|
||||
/// The scalar's first-line leading whitespace and last-line trailing whitespace are untouched,
|
||||
/// since they sit next to a quote, not a break.
|
||||
func foldDoubleQuotedLineBreaks(
|
||||
/// Flow line folding for a quoted (single- or double-) scalar at a line break. Whitespace
|
||||
/// adjacent to the break is stripped (the current line's trailing, each following line's
|
||||
/// leading), then a single break folds to one space and a run of breaks (blank lines) folds to
|
||||
/// one fewer newline. The scalar's first-line leading whitespace and last-line trailing
|
||||
/// whitespace are untouched, since they sit next to a quote, not a break.
|
||||
func foldFlowScalarLineBreaks(
|
||||
in text: Substring,
|
||||
index: inout Substring.Index,
|
||||
to output: inout String,
|
||||
|
||||
21
Tests/SingleQuotedFoldingTests.swift
Normal file
21
Tests/SingleQuotedFoldingTests.swift
Normal file
@ -0,0 +1,21 @@
|
||||
@testable import PureYAML
|
||||
import Testing
|
||||
|
||||
@Suite("Single-quoted scalar folding")
|
||||
struct SingleQuotedFoldingTests {
|
||||
@Test("A multi-line single-quoted scalar folds breaks like a double-quoted one")
|
||||
func test_singleQuotedLineFolding() throws {
|
||||
let parsed = try PureYAML.parse("' 1st non-empty\n\n 2nd non-empty \n\t3rd non-empty '")
|
||||
#expect(parsed == .string(" 1st non-empty\n2nd non-empty 3rd non-empty "))
|
||||
}
|
||||
|
||||
@Test("A doubled quote escapes to one while real breaks fold")
|
||||
func test_singleQuotedEscapeAndFold() throws {
|
||||
#expect(try PureYAML.parse("'it''s a test\n folded line'") == .string("it's a test folded line"))
|
||||
}
|
||||
|
||||
@Test("A single-line single-quoted scalar is unchanged")
|
||||
func test_singleQuotedSingleLine() throws {
|
||||
#expect(try PureYAML.parse("'single'") == .string("single"))
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user