From 5ea6fa668ba62f7c69fe08fdd0959880628ccb27 Mon Sep 17 00:00:00 2001 From: Mihaela M Date: Mon, 29 Jun 2026 04:48:47 +0200 Subject: [PATCH] fix(parsing): end a tagged zero-indent sequence at a sibling key A block sequence value carrying an inline property (`sequence: !!seq` then `- entry` at the key's column) is a zero-indent sequence: its entries sit at the mapping key's own indentation, so a following sibling key ends it. The untagged form was already parsed as a zero-indent sequence that stops at a sibling mapping key, but the property-bearing form fell through to the generic node path, which parsed the sequence without that terminator and read the sibling key as a mixed sequence-and-mapping entry. The zero-indent sequence is now handled before the generic path, carrying any property to the sequence. Fixes yaml-test-suite 57H4; raises parse-level conformance to 313/333 (94.0%) and value-level to 206/206 (100.0%), with the should-error rate unchanged. --- CHANGELOG.md | 9 +++++++ .../Parsing/TokenEventParser.Helpers.swift | 11 +++++--- Tests/TaggedZeroIndentSequenceTests.swift | 27 +++++++++++++++++++ 3 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 Tests/TaggedZeroIndentSequenceTests.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index 87a69a7..0afed6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -84,6 +84,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- A block sequence value carrying an inline property (`sequence: !!seq` then `- entry` at the key's + column) is now correctly terminated by a sibling mapping key. Such a zero-indent sequence has its + entries at the mapping key's own indentation, so a following key ends it. The untagged form already + stopped at the sibling key, but the property-bearing form was routed through the generic node path, + which read the sibling key as a mixed sequence-and-mapping entry and rejected the document. The + zero-indent sequence is now handled before that path, with or without a property. Fixes + yaml-test-suite 57H4; raises parse-level conformance to 313/333 (94.0%) and value-level to 206/206 + (100.0%), with the should-error rate unchanged. + - Node properties that span their own indented lines above a further-indented node now attach to that node (`key: &anchor` then ` !!map` then ` a: b`). An inline anchor with a tag on the next indented line, above a mapping indented deeper still, left the node introduced by another indent diff --git a/Sources/Parsing/TokenEventParser.Helpers.swift b/Sources/Parsing/TokenEventParser.Helpers.swift index d2555e7..f92ab70 100644 --- a/Sources/Parsing/TokenEventParser.Helpers.swift +++ b/Sources/Parsing/TokenEventParser.Helpers.swift @@ -43,15 +43,20 @@ extension PureYAML.Parsing.TokenEventParser { if properties != .none, cursor.current?.kind.isMappingKey == true { return try emitEmptyNodeOrThrow(properties: properties) } - if properties != .none || hasNodeOnSameLine(after: valueIndicator) { - return try parseNode(properties: properties) - } if cursor.current?.kind.isBlockEntry == true { + // A block sequence value whose entries return to the mapping key's own indentation is a + // zero-indent sequence, which a sibling mapping key terminates. This holds whether or not + // the value carries properties (`seq: !!seq` then `- a`), so it is handled before the + // generic property-bearing node below, which would not stop at the sibling key and would + // misread it as a mixed collection. return try parseBlockSequence( properties: properties, allowMappingKeyTerminator: true, ) } + if properties != .none || hasNodeOnSameLine(after: valueIndicator) { + return try parseNode(properties: properties) + } return try emitNullMappingValue(at: valueIndicator.endMark) } diff --git a/Tests/TaggedZeroIndentSequenceTests.swift b/Tests/TaggedZeroIndentSequenceTests.swift new file mode 100644 index 0000000..839bdc9 --- /dev/null +++ b/Tests/TaggedZeroIndentSequenceTests.swift @@ -0,0 +1,27 @@ +@testable import PureYAML +import Testing + +@Suite("A tagged zero-indent block sequence is terminated by a sibling mapping key") +struct TaggedZeroIndentSequenceTests { + /// yaml-test-suite 57H4: a block sequence value carrying an inline tag (`sequence: !!seq` then + /// `- entry` at the key's column) is a zero-indent sequence whose entries sit at the mapping + /// key's indentation. A following sibling key (`mapping:`) ends the sequence rather than mixing + /// into it, the same as an untagged zero-indent sequence. + @Test("A tagged zero-indent sequence ends at the next sibling key") + func test_taggedZeroIndentSequence() throws { + let root = try requireMapping(PureYAML.parse( + "sequence: !!seq\n- entry\n- !!seq\n - nested\nmapping: !!map\n foo: bar\n", + )) + #expect(root?["sequence"] == .sequence([.string("entry"), .sequence([.string("nested")])])) + let mapping = try requireMapping(#require(root?["mapping"])) + #expect(mapping?["foo"] == .string("bar")) + } + + /// The untagged zero-indent sequence followed by a sibling key keeps working unchanged. + @Test("An untagged zero-indent sequence still ends at the next sibling key") + func test_untaggedZeroIndentSequence() throws { + let root = try requireMapping(PureYAML.parse("seq:\n- a\n- b\nother: x\n")) + #expect(root?["seq"] == .sequence([.string("a"), .string("b")])) + #expect(root?["other"] == .string("x")) + } +}