fix(parsing): allow a flow mapping entry with no value
A flow mapping entry may omit its `:` value indicator, in which case its key
maps to null: `{ a }` and `{ a, b }` are flow sets. parseFlowMappingPair
required a `:` after every key, so it rejected these entries, and rejected a
valueless key that spanned lines (`{ multi\n line, a: b}`) with them.
Parse a value only when a `:` follows the key; otherwise emit a null value for
the key. A unit test that asserted `{ a, }` was an error is corrected to expect
the set `{a: null}`, the behaviour the yaml-test-suite cases 8KB6 and 9BXH
require.
Raises yaml-test-suite parse-level conformance to 304/333 (91.3%) and
value-level to 195/197, with the should-error rejection rate unchanged.
480 tests pass.
This commit is contained in:
parent
5c99086a38
commit
31da488055
@ -84,6 +84,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
### Fixed
|
||||
|
||||
- A flow mapping entry may now omit its `:` value indicator, mapping its key to null: `{ a }` and
|
||||
`{ a, b }` are flow sets. The parser required a `:` after every flow mapping key and rejected these,
|
||||
and a multi-line key with no value (`{ multi`/`line, a: b}`) with it. A value now parses only when a
|
||||
`:` follows the key; otherwise the key maps to null. A test that asserted `{ a, }` was an error was
|
||||
corrected, since it is a valid set. Raises yaml-test-suite parse-level conformance to 304/333
|
||||
(91.3%) and value-level to 195/197, with the should-error rate unchanged.
|
||||
|
||||
- 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
|
||||
|
||||
@ -138,11 +138,19 @@ extension PureYAML.Parsing.TokenEventParser {
|
||||
// both resolve to a null key, so emit one rather than requiring a node before the `:`. This is
|
||||
// valid only in flow mapping key position, so the empty-node allowance stays local here.
|
||||
let keyProperties = try consumeProperties()
|
||||
let keyEndMark: PureYAML.Parsing.Mark
|
||||
if case .mappingValue = cursor.current?.kind {
|
||||
let mark = keyProperties.mark ?? cursor.current?.mark ?? .start
|
||||
try emit(.scalar(value: "", anchor: keyProperties.anchor, tag: keyProperties.tag, style: .plain, mark: mark))
|
||||
keyEndMark = mark
|
||||
} else {
|
||||
_ = try parseNode(properties: keyProperties)
|
||||
keyEndMark = try parseNode(properties: keyProperties).endMark
|
||||
}
|
||||
// A flow mapping entry may omit its `:` value indicator: `{ a }` and `{ a, b }` are sets whose
|
||||
// keys map to null. A value parses only when a `:` follows the key.
|
||||
guard case .mappingValue = cursor.current?.kind else {
|
||||
try emit(.scalar(value: "", anchor: nil, tag: nil, style: .plain, mark: keyEndMark))
|
||||
return PureYAML.Parsing.NodeResult(kind: .scalar, endMark: keyEndMark)
|
||||
}
|
||||
let valueIndicator = try expect("mapping value") { kind in
|
||||
if case .mappingValue = kind {
|
||||
|
||||
@ -42,10 +42,12 @@ struct FlowMappingEmptyKeyTests {
|
||||
])))
|
||||
}
|
||||
|
||||
@Test("A flow mapping entry with no colon at all is still an error")
|
||||
func test_entryWithoutColonRejected() {
|
||||
#expect(throws: PureYAML.Parsing.ParseError.self) {
|
||||
try PureYAML.parse("{ a, }\n")
|
||||
}
|
||||
@Test("A flow mapping entry with no colon is a set-style key mapping to null")
|
||||
func test_entryWithoutColonIsNullValue() throws {
|
||||
// `{ a, }` is a flow set: the entry has no `:`, so its key maps to null. The trailing comma
|
||||
// is allowed.
|
||||
#expect(try PureYAML.parse("{ a, }\n") == .mapping(.init([
|
||||
.init(key: "a", value: .null),
|
||||
])))
|
||||
}
|
||||
}
|
||||
|
||||
39
Tests/FlowMappingNoValueTests.swift
Normal file
39
Tests/FlowMappingNoValueTests.swift
Normal file
@ -0,0 +1,39 @@
|
||||
@testable import PureYAML
|
||||
import Testing
|
||||
|
||||
/// A flow mapping entry may omit its `:` value indicator, in which case the key maps to null: `{ a }`
|
||||
/// and `{ a, b }` are flow sets. Entries with and without values mix freely, and a key without a value
|
||||
/// may itself span lines (`{ multi`/`line, a: b}`).
|
||||
@Suite("Flow mapping no value")
|
||||
struct FlowMappingNoValueTests {
|
||||
@Test("A single key with no value maps to null")
|
||||
func test_singleKey() throws {
|
||||
#expect(try PureYAML.parse("{ a }\n") == .mapping(.init([
|
||||
.init(key: "a", value: .null),
|
||||
])))
|
||||
}
|
||||
|
||||
@Test("Several keys with no value form a set")
|
||||
func test_set() throws {
|
||||
#expect(try PureYAML.parse("{ a, b }\n") == .mapping(.init([
|
||||
.init(key: "a", value: .null),
|
||||
.init(key: "b", value: .null),
|
||||
])))
|
||||
}
|
||||
|
||||
@Test("Valued and valueless entries mix")
|
||||
func test_mixed() throws {
|
||||
#expect(try PureYAML.parse("{ a: 1, b }\n") == .mapping(.init([
|
||||
.init(key: "a", value: .int(1)),
|
||||
.init(key: "b", value: .null),
|
||||
])))
|
||||
}
|
||||
|
||||
@Test("A valueless key may span lines")
|
||||
func test_multilineValuelessKey() throws {
|
||||
#expect(try PureYAML.parse("{ multi\n line, a: b}\n") == .mapping(.init([
|
||||
.init(key: "multi line", value: .null),
|
||||
.init(key: "a", value: .string("b")),
|
||||
])))
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user