A block scalar that is the document's own root node (`>`, `|`, or after `---` on the same line) may begin its content at column 0: the document node's indentation is -1, so column-0 content is still more indented than its parent. The scanner read the base level as 0 and required content strictly deeper than it, so the lines fell through indentation handling and were mis-scanned as a stray plain scalar after the document, raising "expected document boundary". A root block scalar and an under-indented nested one (`- |` then unindented content) present identical indentation state at the first content line, so the two cannot be told apart there. Capture the distinction at header-scan time instead: a root block scalar has the base indentation and no mapping value, explicit key, block-entry implicit indent, or flow context introducing it on its line. Only then is its parent the document; a sequence item or mapping value keeps its parent's indent as the floor, so its under-indented content is still rejected, unchanged. Raises yaml-test-suite parse-level to 291/333 (87.4%) and value-level to 173/191 (90.6%). A regression suite covers the root cases (including a leading '#' as literal content) and the nested under-indented cases that must error. 410 tests pass.
47 lines
2.3 KiB
Swift
47 lines
2.3 KiB
Swift
@testable import PureYAML
|
|
import Testing
|
|
|
|
/// A block scalar that is the document's own root node has the document as its parent, whose
|
|
/// indentation is -1, so its content may begin at column 0. A block scalar nested in a collection
|
|
/// keeps its parent's indentation as the floor, so column-0 content there is under-indented and stays
|
|
/// an error. These two sit on the same scanner state at the first content line, distinguished only by
|
|
/// whether a mapping value, explicit key, block entry, or flow context introduced the scalar.
|
|
@Suite("Root block scalar indentation")
|
|
struct RootBlockScalarIndentationTests {
|
|
@Test("A root block scalar folds content that begins at column 0")
|
|
func test_rootFoldedAtColumnZero() throws {
|
|
#expect(try PureYAML.parse(">\nline1\nline2\n") == .string("line1 line2\n"))
|
|
#expect(try PureYAML.parse("--- >\nline1\nline2\n") == .string("line1 line2\n"))
|
|
}
|
|
|
|
@Test("A root literal block keeps line breaks for content at column 0")
|
|
func test_rootLiteralAtColumnZero() throws {
|
|
#expect(try PureYAML.parse("|\nline1\nline2\n") == .string("line1\nline2\n"))
|
|
#expect(try PureYAML.parse("--- |\nline1\nline2\n") == .string("line1\nline2\n"))
|
|
}
|
|
|
|
@Test("In a block scalar at column 0, a leading '#' is literal content, not a comment")
|
|
func test_rootBlockScalarHashIsLiteral() throws {
|
|
#expect(try PureYAML.parse(">\nline1\n# no comment\nline3\n") == .string("line1 # no comment line3\n"))
|
|
}
|
|
|
|
@Test("A non-root block scalar with content at column 0 is still under-indented and rejected")
|
|
func test_nestedUnderIndentedStillRejected() {
|
|
// A sequence item value: its parent is the sequence at indent 0, so content must be indented.
|
|
#expect(throws: PureYAML.Parsing.ParseError.self) {
|
|
try PureYAML.parse("- |\ncontent\n")
|
|
}
|
|
// A mapping value: same floor, so unindented content is not the block scalar's body.
|
|
#expect(throws: PureYAML.Parsing.ParseError.self) {
|
|
try PureYAML.parse("key: |\ncontent\n")
|
|
}
|
|
}
|
|
|
|
@Test("A nested block scalar with properly indented content is unaffected")
|
|
func test_nestedIndentedStillWorks() throws {
|
|
#expect(try PureYAML.parse("key: |\n content\n") == .mapping(.init([
|
|
.init(key: "key", value: .string("content\n")),
|
|
])))
|
|
}
|
|
}
|