fix(parsing): parse an empty explicit key in a flow mapping
An explicit key (`?`) may have no key node: `? : v`, a trailing `? }` / `? ]` / `? ,`, or `?` at the end of a flow mapping all denote a null key. The parser now emits an empty key in those positions instead of failing to find a node before the value indicator or terminator. Fixes suite case DFF7 (parse-level conformance 316 -> 317); adds tests.
This commit is contained in:
parent
8112954f4c
commit
990a2d2e42
26
Sources/Parsing/TokenEventParser.ExplicitKey.swift
Normal file
26
Sources/Parsing/TokenEventParser.ExplicitKey.swift
Normal file
@ -0,0 +1,26 @@
|
||||
extension PureYAML.Parsing.TokenEventParser {
|
||||
/// The key node of an explicit (`?`) mapping entry. The key may be empty: `? : v`, a trailing
|
||||
/// `? }` / `? ]` / `? ,`, or `?` at stream end all denote a null key. With no node and no
|
||||
/// properties to anchor, emit a null scalar rather than failing to find a node.
|
||||
mutating func parseExplicitMappingKey(
|
||||
properties: PureYAML.Parsing.NodeProperties,
|
||||
) throws -> PureYAML.Parsing.NodeResult {
|
||||
guard properties == .none, isAtEmptyExplicitKey else {
|
||||
return try parseNode(properties: properties)
|
||||
}
|
||||
let mark = cursor.current?.mark ?? cursor.previous?.endMark ?? .start
|
||||
try emit(.scalar(value: "", anchor: nil, tag: nil, style: .plain, mark: mark))
|
||||
return PureYAML.Parsing.NodeResult(kind: .scalar, endMark: mark)
|
||||
}
|
||||
|
||||
/// Whether an explicit key has no key node: the next token is the value indicator, a flow
|
||||
/// terminator (`}`, `]`, `,`), or the stream has ended.
|
||||
var isAtEmptyExplicitKey: Bool {
|
||||
switch cursor.current?.kind {
|
||||
case nil, .mappingValue, .flowMappingEnd, .flowSequenceEnd, .flowEntry:
|
||||
true
|
||||
default:
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -8,7 +8,7 @@ extension PureYAML.Parsing.TokenEventParser {
|
||||
var keyProperties = firstKeyProperties
|
||||
try keyProperties.mergeUnset(from: consumeProperties())
|
||||
_ = try expect("mapping key") { $0.isMappingKey }
|
||||
let key = try parseNode(properties: keyProperties)
|
||||
let key = try parseExplicitMappingKey(properties: keyProperties)
|
||||
// An explicit key (`? key`) with no following `:` has an implicit null value, so several
|
||||
// `? key` entries in a row (a set, for instance) each map to null rather than being an error.
|
||||
guard case .mappingValue = cursor.current?.kind else {
|
||||
|
||||
33
Tests/FlowExplicitEmptyKeyTests.swift
Normal file
33
Tests/FlowExplicitEmptyKeyTests.swift
Normal file
@ -0,0 +1,33 @@
|
||||
@testable import PureYAML
|
||||
import Testing
|
||||
|
||||
/// An explicit key (`?`) in a flow mapping may have no key node: `? : v`, a trailing `? }` or
|
||||
/// `? ]` or `? ,`, all denote a null key. The parser emits an empty key rather than failing to
|
||||
/// find a node before the value indicator or terminator.
|
||||
@Suite("Flow explicit empty key")
|
||||
struct FlowExplicitEmptyKeyTests {
|
||||
@Test("An explicit key with a value but no key node is a null key")
|
||||
func test_explicitEmptyKeyWithValue() throws {
|
||||
#expect(try PureYAML.parse("{ ? : v }") == .mapping(.init([
|
||||
.init(keyNode: .string(""), value: .string("v")),
|
||||
])))
|
||||
}
|
||||
|
||||
@Test("A trailing explicit key with no node maps a null key to null")
|
||||
func test_trailingExplicitEmptyKey() throws {
|
||||
#expect(try PureYAML.parse("{ a, ? }") == .mapping(.init([
|
||||
.init(key: "a", value: .null),
|
||||
.init(keyNode: .string(""), value: .null),
|
||||
])))
|
||||
}
|
||||
|
||||
@Test("Suite case DFF7: a flow mapping ending with an empty explicit key")
|
||||
func test_dff7() throws {
|
||||
let yaml = "{\n? explicit: entry,\nimplicit: entry,\n?\n}\n"
|
||||
#expect(try PureYAML.parse(yaml) == .mapping(.init([
|
||||
.init(key: "explicit", value: .string("entry")),
|
||||
.init(key: "implicit", value: .string("entry")),
|
||||
.init(keyNode: .string(""), value: .null),
|
||||
])))
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user