diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d38cab..dac17bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- A directive after a document-end marker (`...`) now begins the next document instead of being + rejected. A multi-document stream where each document carries its own directives + (`%TAG ...` / `--- doc` / `...` / `%TAG ...` / `--- doc`) is now accepted, since a directive + following `...` opens the next document's directive section. A directive after document content + within the same document is still rejected, and a directive after `...` still requires its own + `---`. Lifts official test-suite parse-level conformance from 278/333 (83.5%) to 283/333 + (85.0%), with no regression. - A block sequence entry can no longer follow a flow collection that closed earlier on the same line (`- { y: z }- invalid`, `- [a] - b`); the entry would begin a sequence inline after a complete node. The scanner now tracks a top-level flow close on the current line and rejects a diff --git a/Sources/Parsing/Scanner.Directives.swift b/Sources/Parsing/Scanner.Directives.swift index a25c684..14db655 100644 --- a/Sources/Parsing/Scanner.Directives.swift +++ b/Sources/Parsing/Scanner.Directives.swift @@ -88,7 +88,12 @@ extension PureYAML.Parsing.Scanner { guard let name = parts.first else { return } - guard !state.hasDocumentContent, !state.hasDocumentStartMarker, !state.isDocumentClosed else { + // A directive after a document-end marker (`...`) begins the next document's directive + // section, so it reopens the stream for a new document rather than being rejected. + if state.isDocumentClosed { + state.isDocumentClosed = false + } + guard !state.hasDocumentContent, !state.hasDocumentStartMarker else { throw PureYAML.Parsing.ParseError.unsupportedDirective(name: name, line: start.line) } // Every directive must be followed by an explicit `---`; record that one is now awaiting it. diff --git a/Tests/DirectiveAfterDocumentEndTests.swift b/Tests/DirectiveAfterDocumentEndTests.swift new file mode 100644 index 0000000..03ed272 --- /dev/null +++ b/Tests/DirectiveAfterDocumentEndTests.swift @@ -0,0 +1,28 @@ +@testable import PureYAML +import Testing + +@Suite("Directives after a document-end marker") +struct DirectiveAfterDocumentEndTests { + @Test("A directive after `...` begins the next document") + func test_directiveAfterDocumentEndStartsNextDocument() throws { + let docs = try PureYAML.parseStream("a\n...\n%YAML 1.2\n---\nb\n") + #expect(docs.count == 2) + #expect(docs[0].value == .string("a")) + #expect(docs[1].value == .string("b")) + } + + @Test("A %TAG directive may be repeated for each document after `...`") + func test_repeatedTagDirectivePerDocument() throws { + let docs = try PureYAML.parseStream( + "%TAG !m! !my-\n--- !m!light fluorescent\n...\n%TAG !m! !my-\n--- !m!light green\n", + ) + #expect(docs.count == 2) + } + + @Test("A directive after `...` still requires its own document start") + func test_directiveAfterDocumentEndStillRequiresDocument() throws { + #expect(throws: PureYAML.Parsing.ParseError.self) { + try PureYAML.parseStream("a\n...\n%YAML 1.2\n") + } + } +}