diff --git a/CHANGELOG.md b/CHANGELOG.md index 71a531a..c6bcd71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 sequences (`- :`, `- ? : x`), and mixed empty-key/value entries. Surpasses libyaml on these seven test-suite cases (2JQS, 6M2F, M2N8/00, NHX8, S3PD, SM9W/01, UKK6/00). +- Parse flow mappings with empty (null) keys. The same `:`-without-a-key entry + is valid inside `{ ... }`, where libyaml hands the bare VALUE token to its node + parser and fails with "did not find expected node content". The flow-mapping-key + parser now emits the empty scalar key for a leading `:`, so `{ : }` and + `{ key: value, : empty key }` parse, completing the multi-document NKF9 case. - Emit no document for a stream whose only content is comments or whitespace. A comment-only or empty stream has zero documents in YAML 1.2 (matching libyaml and the test suite), but the event stream was emitting a spurious empty null diff --git a/Sources/Parsing/LibYAML.Parser.Flow.swift b/Sources/Parsing/LibYAML.Parser.Flow.swift index 466c997..28638b6 100644 --- a/Sources/Parsing/LibYAML.Parser.Flow.swift +++ b/Sources/Parsing/LibYAML.Parser.Flow.swift @@ -96,6 +96,14 @@ extension PureYAML.Parsing.LibYAML.Parser { if isKey(token) { return try flowMappingKeyEvent() } + // YAML 1.2 permits an empty (null) key in a flow mapping: a `:` with + // no preceding key denotes an entry whose key is empty. libyaml hands + // the VALUE token to `parse_node` and fails; instead emit the empty + // scalar key and let `parseFlowMappingValue` consume the indicator. + if isValue(token) { + state = .flowMappingValue + return processEmptyScalar(at: token.startMark) + } if !isFlowMappingEnd(token) { states.append(.flowMappingEmptyValue) return try parseNode(block: false, indentlessSequence: false) diff --git a/Tests/CanonicalEventConformanceTests.swift b/Tests/CanonicalEventConformanceTests.swift index 6dd3031..2d4b87b 100644 --- a/Tests/CanonicalEventConformanceTests.swift +++ b/Tests/CanonicalEventConformanceTests.swift @@ -12,7 +12,7 @@ struct CanonicalEventConformanceTests { /// Valid suite cases whose event stream PureYAML does NOT yet emit exactly. /// This is the authoritative gap set: a fix removes an id (and the test fails /// until it is removed), a regression adds one (and the test fails). Of these, - /// 23 currently throw on valid input and 3 emit a wrong event stream. + /// 22 currently throw on valid input and 3 emit a wrong event stream. static let knownEventGaps: Set = [ // Gap set for the ported libyaml engine. Verified against the libyaml // reference dumper: on all but four of these the port matches libyaml @@ -24,7 +24,7 @@ struct CanonicalEventConformanceTests { "4ABK", "58MP", "5T43", "6BCT", "6CA3", "96NN/00", "96NN/01", "A2M4", "CFD4", "DBG4", "DK3J", "DK95/00", "DK95/04", "FP8R", "FRK4", "HM87/00", "M7A3", - "NKF9", "Q5MG", "R4YG", "W4TN", + "Q5MG", "R4YG", "W4TN", "Y79Y/001", "Y79Y/010", // wrong event stream: the port matches libyaml but both deviate from the // canonical events (libyaml quirks to be surpassed).