fix(parsing): reject a block sequence inline as a mapping value
A block sequence cannot be an implicit key's value on the same line as its ':' (key: - a); the value must begin on a following line, or use flow style. The scanner accepted the inline block entry and parsed it as the key's sequence value. The block-entry case now rejects a '-' that starts a sequence when the line already holds an implicit-key mapping value, reusing the same same-line-pair flag as the inline nested mapping rule. As there, an explicit- or null-key ':' opens its line and does not set the flag, so a compact sequence value after it stays valid, as do a sequence on the next line, a nested sequence, a flow sequence, and a negative number (key: -1). Measured against the official YAML test-suite, parse-level conformance rises from 260/333 (78.1%) to 261/333 (78.4%) with no regression.
This commit is contained in:
parent
735767ced9
commit
b0dc31922b
@ -23,6 +23,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
### Fixed
|
||||
|
||||
- A block sequence can no longer be an implicit key's value on the same line as its `:`
|
||||
(`key: - a`); the value must begin on a following line, or use flow style (`key: [a]`). Like the
|
||||
inline nested mapping rule, this is specific to an implicit key: an explicit- or null-key `:`
|
||||
opens its line, so a compact sequence after it stays valid, as do a sequence on the next line, a
|
||||
nested sequence (`- - a`), a flow sequence, and a negative number (`key: -1`). Lifts official
|
||||
test-suite parse-level conformance from 260/333 (78.1%) to 261/333 (78.4%), with no regression.
|
||||
- An inline nested block mapping is now rejected. Block style allows only one mapping pair per
|
||||
line, so a scalar followed by `:` after a block-level value on the same line (`a: b: c`,
|
||||
`a: 'b': c`, `x: { y: z }in: valid`, or trailing content after a quoted value) no longer parses
|
||||
|
||||
@ -24,6 +24,7 @@ public extension PureYAML.Parsing {
|
||||
case duplicateDirective(name: String, line: Int)
|
||||
case directiveWithoutDocument(line: Int)
|
||||
case contentAfterDocumentEnd(line: Int)
|
||||
case blockSequenceAsInlineMappingValue(line: Int)
|
||||
|
||||
public var description: String {
|
||||
switch self {
|
||||
@ -73,6 +74,8 @@ public extension PureYAML.Parsing {
|
||||
"a directive must be followed by a document at line \(line)"
|
||||
case let .contentAfterDocumentEnd(line):
|
||||
"no content may follow a document end marker at line \(line)"
|
||||
case let .blockSequenceAsInlineMappingValue(line):
|
||||
"a block sequence cannot start on the same line as a mapping value at line \(line)"
|
||||
}
|
||||
}
|
||||
|
||||
@ -124,6 +127,8 @@ public extension PureYAML.Parsing {
|
||||
"directiveWithoutDocument"
|
||||
case .contentAfterDocumentEnd:
|
||||
"contentAfterDocumentEnd"
|
||||
case .blockSequenceAsInlineMappingValue:
|
||||
"blockSequenceAsInlineMappingValue"
|
||||
}
|
||||
}
|
||||
|
||||
@ -142,7 +147,8 @@ public extension PureYAML.Parsing {
|
||||
let .malformedDirective(_, line),
|
||||
let .duplicateDirective(_, line),
|
||||
let .directiveWithoutDocument(line),
|
||||
let .contentAfterDocumentEnd(line):
|
||||
let .contentAfterDocumentEnd(line),
|
||||
let .blockSequenceAsInlineMappingValue(line):
|
||||
line
|
||||
case let .expectedNode(line, _),
|
||||
let .expectedScalarKey(line, _),
|
||||
@ -172,7 +178,8 @@ public extension PureYAML.Parsing {
|
||||
.malformedDirective,
|
||||
.duplicateDirective,
|
||||
.directiveWithoutDocument,
|
||||
.contentAfterDocumentEnd:
|
||||
.contentAfterDocumentEnd,
|
||||
.blockSequenceAsInlineMappingValue:
|
||||
nil
|
||||
case let .expectedNode(_, column),
|
||||
let .expectedScalarKey(_, column),
|
||||
|
||||
@ -168,6 +168,13 @@ extension PureYAML.Parsing.Scanner {
|
||||
scanComment(&state)
|
||||
return true
|
||||
case "-" where isBlockEntry(state.reader):
|
||||
// A block sequence cannot be an implicit key's value on the same line as its `:`
|
||||
// (`key: - a`); the value must begin on a following line. As with an inline nested
|
||||
// mapping, an explicit- or null-key `:` opens its line and does not set the flag, so a
|
||||
// compact sequence value after it stays valid.
|
||||
if state.sawMappingValueOnLine, state.flowDepth == 0 {
|
||||
throw PureYAML.Parsing.ParseError.blockSequenceAsInlineMappingValue(line: state.reader.mark.line)
|
||||
}
|
||||
scanFixedToken(.blockEntry, state: &state)
|
||||
return true
|
||||
case "?" where isIndicatorBoundary(state.reader.peek(offset: 1)):
|
||||
|
||||
34
Tests/InlineBlockSequenceValueTests.swift
Normal file
34
Tests/InlineBlockSequenceValueTests.swift
Normal file
@ -0,0 +1,34 @@
|
||||
@testable import PureYAML
|
||||
import Testing
|
||||
|
||||
@Suite("Block sequence as an inline mapping value")
|
||||
struct InlineBlockSequenceValueTests {
|
||||
@Test("A block sequence on the same line as an implicit key's `:` is rejected")
|
||||
func test_inlineBlockSequenceValueRejected() throws {
|
||||
#expect(throws: PureYAML.Parsing.ParseError.self) {
|
||||
try PureYAML.parse("key: - a\n - b\n")
|
||||
}
|
||||
}
|
||||
|
||||
@Test("Block sequences positioned correctly, and look-alikes, stay valid", arguments: [
|
||||
"key:\n - a\n - b\n", // sequence indented on the next line
|
||||
"key:\n- a\n- b\n", // sequence at the key's indentation
|
||||
"- - a\n", // a nested block sequence
|
||||
"- key: value\n", // a sequence item that is a mapping
|
||||
"key: [a, b]\n", // a flow sequence value
|
||||
"key: -1\n", // a negative number, not a block entry
|
||||
"a: b - c\n", // a dash inside a plain scalar value
|
||||
])
|
||||
func test_validSequencesStillParse(_ yaml: String) throws {
|
||||
_ = try PureYAML.parse(yaml)
|
||||
}
|
||||
|
||||
@Test("An explicit key's value on its own `:` line is not rejected as inline")
|
||||
func test_explicitKeyCompactSequenceValueNotRejected() throws {
|
||||
// The `:` opens the line, so it is an explicit-key value indicator and the compact sequence
|
||||
// after it is allowed: the reject above is specific to an implicit key. The element folding
|
||||
// of an explicit-key compact sequence is a separate, pre-existing concern, so this asserts
|
||||
// only that the construct still parses rather than its exact value.
|
||||
_ = try PureYAML.parse("? k\n: - a\n - b\n")
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user