feat(parsing): support multi-line flow mapping keys

YAML 1.2 lets a flow-context key be separated from its ':' value indicator
by line breaks, so a flow-level simple key must survive a new line (it is
still bounded to 1024 characters). libyaml invalidates every simple key on
a new line, which wrongly rejects keys like `{"foo"\n: "bar"}` and
multi-line quoted keys.

Canonical event conformance: 259 -> 268 of 308 (4MUZ/00-02, 5MUD, 9SA2,
K3WX, NJ66, UT92, VJP3/01). This passes the hand-rolled parser's 267 and
exceeds libyaml's own 252. 333 tests pass.
This commit is contained in:
Mihaela Mihaljevic 2026-06-24 17:46:16 +02:00
parent c906993f00
commit d9bf2bda26
2 changed files with 16 additions and 8 deletions

View File

@ -37,7 +37,17 @@ extension PureYAML.Parsing.LibYAML {
mutating func staleSimpleKeys() throws {
for index in simpleKeys.indices {
let key = simpleKeys[index]
guard key.possible, key.mark.line < mark.line || key.mark.index + 1024 < mark.index else {
guard key.possible else {
continue
}
// A block-context key (index 0) is single-line. A flow-context key
// (index >= 1) may be separated from its ':' by line breaks under
// YAML 1.2, so a new line alone does not invalidate it; both remain
// bounded to 1024 characters. libyaml invalidates every key on a new
// line, which wrongly rejects multi-line flow keys.
let tooFarBack = key.mark.index + 1024 < mark.index
let stale = index == 0 ? (key.mark.line < mark.line || tooFarBack) : tooFarBack
guard stale else {
continue
}
if key.required {

View File

@ -21,13 +21,11 @@ struct CanonicalEventConformanceTests {
// itself handles.
//
// false-rejects (parseEvents throws on valid input, as libyaml does)
"2JQS", "2SXE", "4ABK", "4MUZ/00", "4MUZ/01", "4MUZ/02", "58MP",
"5MUD", "5T43", "6BCT", "6CA3", "6M2F", "7Z25", "8XYN", "96NN/00",
"96NN/01", "9SA2", "A2M4", "CFD4", "DBG4", "DK3J", "DK95/00",
"DK95/04", "FP8R", "FRK4", "HM87/00", "HWV9", "K3WX", "M2N8/00",
"M7A3", "NHX8", "NJ66", "NKF9", "Q5MG", "QT73",
"R4YG", "S3PD", "SM9W/01", "UKK6/00", "UT92", "VJP3/01", "W4TN", "W5VH",
"Y79Y/001", "Y79Y/010",
"2JQS", "2SXE", "4ABK", "58MP", "5T43", "6BCT", "6CA3", "6M2F", "7Z25",
"8XYN", "96NN/00", "96NN/01", "A2M4", "CFD4", "DBG4", "DK3J", "DK95/00",
"DK95/04", "FP8R", "FRK4", "HM87/00", "HWV9", "M2N8/00", "M7A3", "NHX8",
"NKF9", "Q5MG", "QT73", "R4YG", "S3PD", "SM9W/01", "UKK6/00", "W4TN",
"W5VH", "Y79Y/001", "Y79Y/010",
// wrong event stream: the port matches libyaml but both deviate from the
// canonical events (libyaml quirks to be surpassed).
"652Z", "HM87/01", "JEF9/02", "Y2GN",