fix(parsing): let a flow plain key span lines
A plain scalar continuation stops before a line ending at `:`, since a block
implicit key may not span lines and the next line is a new key. A flow key may
span lines, so the same rule wrongly split `{ multi\n line: v }`: the scalar
returned just `multi` and the parser mis-read `line: v` as a separate entry.
Fold across the `:` in flow as well, leaving the `:` to be scanned as the
value indicator, so the key folds to `multi line`. A block implicit key keeps
the previous behaviour and still stops before the `:`.
Raises yaml-test-suite parse-level conformance to 301/333 (90.4%) and
value-level to 193/195, with the should-error rejection rate unchanged.
476 tests pass.
This commit is contained in:
parent
95c5ff3966
commit
5c99086a38
@ -84,6 +84,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
### Fixed
|
||||
|
||||
- A plain scalar key in a flow collection may now span lines. A flow key, unlike a block implicit
|
||||
key, is allowed to be multi-line, so `{ multi` / ` line: v }` is the key `multi line`. The scalar
|
||||
continuation stopped at the `:` value indicator in every non-explicit case, dropping the second
|
||||
line, so the parser mis-split the entry. Folding across the `:` now also applies in flow, while a
|
||||
block implicit key still stops before such a `:`. Raises yaml-test-suite parse-level conformance to
|
||||
301/333 (90.4%) and value-level to 193/195, with the should-error rate unchanged.
|
||||
|
||||
- An explicit key (`?`) inside a flow collection is now parsed correctly. The flow `?` did not mark an
|
||||
explicit key, so the following scalar synthesized its own implicit key: `{? foo : bar}` came out as
|
||||
`{{foo: bar}: null}` rather than `{foo: bar}`, and `[? foo : bar]` likewise. The explicit-key state
|
||||
|
||||
@ -172,14 +172,12 @@ extension PureYAML.Parsing.Scanner {
|
||||
let continuation = scanPlainScalarContinuationLine(&continuationState)
|
||||
if isAtMappingValueIndicator(state: continuationState) {
|
||||
// A continuation line ending at `:` is normally a new implicit key, so the scalar
|
||||
// stops before it. Under an explicit key (`? foo` then `bar :`), the `:` is the key's
|
||||
// value indicator instead, so the line is still key content: fold it in and stop, and
|
||||
// the `:` is scanned next as the value indicator. The explicit key is tracked on the
|
||||
// block flag or, in flow, on the current flow context.
|
||||
let inExplicitKey = state.flowDepth > 0
|
||||
? state.flowContexts.last?.hasExplicitKey == true
|
||||
: state.pendingExplicitKey
|
||||
guard inExplicitKey else {
|
||||
// stops before it. Two cases keep folding instead, with the `:` left to be scanned as
|
||||
// the value indicator: a block explicit key (`? foo` then `bar :`), where the line is
|
||||
// still the key's content, and any flow scalar, since a flow key may span lines
|
||||
// (`{ multi`/`line: v }`) where a block implicit key may not.
|
||||
let foldsAcrossMappingValue = state.flowDepth > 0 || state.pendingExplicitKey
|
||||
guard foldsAcrossMappingValue else {
|
||||
return value
|
||||
}
|
||||
state = continuationState
|
||||
|
||||
37
Tests/MultilineFlowKeyTests.swift
Normal file
37
Tests/MultilineFlowKeyTests.swift
Normal file
@ -0,0 +1,37 @@
|
||||
@testable import PureYAML
|
||||
import Testing
|
||||
|
||||
/// A plain scalar key in a flow collection may span lines: the continuation folds across the line
|
||||
/// break even when the line ends at the `:` value indicator, since a flow key, unlike a block implicit
|
||||
/// key, is allowed to be multi-line. A block implicit key still stops before such a `:`, treating the
|
||||
/// next line as a new key.
|
||||
@Suite("Multiline flow key")
|
||||
struct MultilineFlowKeyTests {
|
||||
@Test("A flow mapping key folds across a line break")
|
||||
func test_flowMappingKey() throws {
|
||||
#expect(try PureYAML.parse("{ multi\n line: v }\n") == .mapping(.init([
|
||||
.init(key: "multi line", value: .string("v")),
|
||||
])))
|
||||
}
|
||||
|
||||
@Test("A flow sequence plain element still folds, unchanged")
|
||||
func test_flowSequenceElement() throws {
|
||||
#expect(try PureYAML.parse("[ multi\n line ]\n") == .sequence([.string("multi line")]))
|
||||
}
|
||||
|
||||
@Test("An ordinary single-line flow mapping is unchanged")
|
||||
func test_singleLineFlowMapping() throws {
|
||||
#expect(try PureYAML.parse("{a: 1, b: 2}\n") == .mapping(.init([
|
||||
.init(key: "a", value: .int(1)),
|
||||
.init(key: "b", value: .int(2)),
|
||||
])))
|
||||
}
|
||||
|
||||
@Test("A block implicit key does not span lines")
|
||||
func test_blockImplicitKeyDoesNotSpan() {
|
||||
// `foo` then `bar: baz` is a value and a new key, not the key `foo bar`, so this is an error.
|
||||
#expect(throws: PureYAML.Parsing.ParseError.self) {
|
||||
try PureYAML.parse("foo\n bar: baz\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user