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.
This commit is contained in:
Mihaela M 2026-06-28 01:00:57 +02:00
parent f1db1344da
commit 30dbd9316b
3 changed files with 42 additions and 0 deletions

View File

@ -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

View File

@ -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)
}

View File

@ -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)
}
}