From 30dbd9316be1f847206ab1b4db6d435c0d15aba7 Mon Sep 17 00:00:00 2001 From: Mihaela M Date: Sun, 28 Jun 2026 01:00:57 +0200 Subject: [PATCH] fix(parsing): reject a lone dash before a flow indicator In a flow collection a '-' that begins a plain scalar must be followed by a non-indicator character. Immediately before a flow indicator ('[-]', '[-, -]', '{a: -}') the dash is not a valid scalar, but the scanner accepted it as the scalar '-'. scanToken now rejects a '-' in flow context when the next character is a flow indicator, before falling through to plain-scalar scanning. A dash followed by ordinary content stays valid: negative numbers, a dash inside a scalar, a dash starting a word, and a quoted dash are unaffected. Measured against the official YAML test-suite, parse-level conformance rises from 273/333 (82.0%) to 275/333 (82.6%) with no regression. --- CHANGELOG.md | 6 ++++++ Sources/Parsing/Scanner.swift | 10 ++++++++++ Tests/FlowDashScalarTests.swift | 26 ++++++++++++++++++++++++++ 3 files changed, 42 insertions(+) create mode 100644 Tests/FlowDashScalarTests.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index 297398b..53e04c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- A `-` immediately before a flow indicator is no longer treated as a scalar. In a flow collection + a `-` that begins a plain scalar must be followed by a non-indicator character, so `[-]`, + `[-, -]`, and `{a: -}` are now rejected. A dash followed by ordinary content is unaffected: + negative numbers (`[-1, -2]`), a dash inside a scalar (`a-b`), a dash starting a word (`-x`), and + a quoted dash all stay valid. Lifts official test-suite parse-level conformance from 273/333 + (82.0%) to 275/333 (82.6%), with no regression. - A block scalar leading blank line that is more indented than the content is now rejected. The spec forbids a leading empty line from containing more spaces than the first non-empty line, so a block scalar whose blank lines are wider than its content (`|` then a 5-space blank then 2-space diff --git a/Sources/Parsing/Scanner.swift b/Sources/Parsing/Scanner.swift index d7a582a..bb73818 100644 --- a/Sources/Parsing/Scanner.swift +++ b/Sources/Parsing/Scanner.swift @@ -164,6 +164,16 @@ extension PureYAML.Parsing.Scanner { if try scanNodePropertyToken(character, state: &state) { return } + // In a flow collection, a `-` that begins a plain scalar must be followed by a non-indicator + // character. Immediately before a flow indicator (`[-]`, `[-, -]`) it is not a valid scalar. + if state.flowDepth > 0, character == "-", let next = state.reader.peek(offset: 1), isFlowDelimiter(next) { + throw PureYAML.Parsing.ParseError.unexpectedToken( + expected: "a flow value", + actual: "-", + line: state.reader.mark.line, + column: state.reader.mark.column, + ) + } scanPlainScalar(&state) } diff --git a/Tests/FlowDashScalarTests.swift b/Tests/FlowDashScalarTests.swift new file mode 100644 index 0000000..cd44914 --- /dev/null +++ b/Tests/FlowDashScalarTests.swift @@ -0,0 +1,26 @@ +@testable import PureYAML +import Testing + +@Suite("A dash before a flow indicator") +struct FlowDashScalarTests { + @Test("A `-` immediately before a flow indicator is not a valid scalar", arguments: [ + "[-]\n", // a lone dash before `]` + "---\n- [-, -]\n", // dashes before `,` and `]` + "{a: -}\n", // a lone dash before `}` + ]) + func test_dashBeforeFlowIndicatorRejected(_ yaml: String) throws { + #expect(throws: PureYAML.Parsing.ParseError.self) { + try PureYAML.parse(yaml) + } + } + + @Test("A dash followed by a non-indicator character stays a valid scalar", arguments: [ + "[-1, -2]\n", // negative numbers + "[a-b, c-d]\n", // a dash inside a scalar + "[-x, -y]\n", // a dash starting a word + "[\"-\", '-']\n", // a quoted dash + ]) + func test_validDashScalars(_ yaml: String) throws { + _ = try PureYAML.parse(yaml) + } +}