diff --git a/CHANGELOG.md b/CHANGELOG.md index 3652865..9403d12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -84,6 +84,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- Compact nested block sequences (`- - x`) now separate their outer items correctly. Such a sequence + carries no indent/dedent tokens, only block entries at their columns, but the sibling-entry loop + collected every following block entry regardless of column, so `- - x` then `- - y` became one + outer item holding both rather than two. A sequence now collects siblings only at its own column, so + an entry at a smaller column ends it and belongs to the enclosing sequence. Raises yaml-test-suite + value-level conformance to 190/194 (97.9%), with the parse and should-error rates unchanged. + - A block scalar whose only lines are blank now carries those blank lines through chomping at stream end. Such a scalar never establishes a content indentation, so its blank lines stayed buffered and were dropped, making `keep: |+` over a blank line resolve to the empty string instead of a line diff --git a/Sources/Parsing/TokenEventParser.Block.swift b/Sources/Parsing/TokenEventParser.Block.swift index 5992038..8b512f3 100644 --- a/Sources/Parsing/TokenEventParser.Block.swift +++ b/Sources/Parsing/TokenEventParser.Block.swift @@ -5,6 +5,10 @@ extension PureYAML.Parsing.TokenEventParser { ) throws -> PureYAML.Parsing.NodeResult { let start = try expect("block sequence entry") { $0.isBlockEntry } try enforceBlockSequenceNotInlineAfterProperty(properties, firstEntry: start) + // A compact nested sequence (`- - x`) carries no indent/dedent tokens, only block entries at + // their columns, so the sibling-entry loop must stay at this sequence's own column: a later + // entry at a smaller column belongs to an enclosing sequence and ends this one. + let sequenceColumn = start.mark.column let startMark = properties.mark ?? start.mark var endMark = start.endMark try emit(.sequenceStart( @@ -21,7 +25,7 @@ extension PureYAML.Parsing.TokenEventParser { ) try consumeSequenceItemBoundaryDedent(sequenceIndentWidth: start.mark.column - 1) try consumeSequenceItemBoundaryIndent(sequenceIndentWidth: start.mark.column - 1) - while cursor.current?.kind.isBlockEntry == true { + while cursor.current?.kind.isBlockEntry == true, cursor.current?.mark.column == sequenceColumn { let entry = try expect("block sequence entry") { $0.isBlockEntry } try parseBlockSequenceItem( after: entry, diff --git a/Tests/CompactNestedSequenceTests.swift b/Tests/CompactNestedSequenceTests.swift new file mode 100644 index 0000000..7e53837 --- /dev/null +++ b/Tests/CompactNestedSequenceTests.swift @@ -0,0 +1,37 @@ +@testable import PureYAML +import Testing + +/// A compact nested block sequence (`- - x`) carries no indent/dedent tokens, only block entries at +/// their columns. A sequence collects sibling items only at its own column, so a later entry at a +/// smaller column ends it and belongs to the enclosing sequence: `- - x` then `- - y` is `[[x], [y]]`, +/// two outer items, not one outer item holding both. +@Suite("Compact nested sequence") +struct CompactNestedSequenceTests { + @Test("Two compact nested entries are two outer items") + func test_twoNestedItems() throws { + #expect(try PureYAML.parse("- - x\n- - y\n") == .sequence([ + .sequence([.string("x")]), + .sequence([.string("y")]), + ])) + } + + @Test("Three levels of compact nesting separate at the outer column") + func test_threeLevels() throws { + #expect(try PureYAML.parse("- - - []\n- - - {}\n") == .sequence([ + .sequence([.sequence([.sequence([])])]), + .sequence([.sequence([.mapping(.init([]))])]), + ])) + } + + @Test("Entries at the same column stay one sequence") + func test_sameColumnSiblings() throws { + #expect(try PureYAML.parse("- - a\n - b\n") == .sequence([ + .sequence([.string("a"), .string("b")]), + ])) + } + + @Test("A flat sequence is unchanged") + func test_flatSequence() throws { + #expect(try PureYAML.parse("- a\n- b\n") == .sequence([.string("a"), .string("b")])) + } +}