fix(parsing): reject a multi-line implicit key in a flow sequence
A flow-sequence single-pair implicit key is single-line
(c-s-implicit-json-key uses s-separate-in-line), so its ':' may not cross
a line break: '[ "key"\n :value ]' is malformed. A flow MAPPING adjacent
value may still span lines ('{ "foo"\n :bar }' stays valid), which is why
the check is scoped to flow levels that are sequences.
Track per-flow-level whether it is a sequence ('[' vs '{') in a
flowIsSequence stack pushed/popped with the flow level, and in fetchValue
reject a pending simple key whose ':' crossed a line break when the
current flow level is a sequence.
Surpasses libyaml on ZXT5 (5MUD, the flow-mapping analogue, stays valid);
whole-suite conformance 397/402, canonical events 308/308.
This commit is contained in:
parent
57adb14f15
commit
c236945323
@ -22,6 +22,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
### Fixed
|
||||
|
||||
- Reject a multi-line implicit key in a flow sequence. A flow-sequence single-pair
|
||||
implicit key is single-line (`c-s-implicit-json-key`), so its `:` may not cross a
|
||||
line break: `[ "key"\n :value ]` is malformed. A flow MAPPING adjacent value may
|
||||
still span lines (`{ "foo"\n :bar }` stays valid), so the rejection is scoped to
|
||||
flow levels that are sequences, tracked alongside the flow-level stack. Surpasses
|
||||
libyaml on ZXT5.
|
||||
- Reject a block sequence that opens on the same line as an earlier node. A `-`
|
||||
entry mid-line after an inline mapping value (`key: - a`) or a node property
|
||||
(`&anchor - x`) is invalid, while a `-` after another `-` stays a valid compact
|
||||
|
||||
@ -188,7 +188,7 @@ extension PureYAML.Parsing.LibYAML.Scanner {
|
||||
if saved {
|
||||
simpleKeys[simpleKeys.count - 1].isJSON = true
|
||||
}
|
||||
increaseFlowLevel()
|
||||
increaseFlowLevel(isSequence: type == .flowSequenceStart)
|
||||
simpleKeyAllowed = true
|
||||
let startMark = reader.mark
|
||||
reader.skip()
|
||||
@ -313,6 +313,15 @@ extension PureYAML.Parsing.LibYAML.Scanner {
|
||||
// and `:` on one line). An explicit `?` key's `:` has no pending simple key
|
||||
// (it went stale across the line break), so its value may open a sequence.
|
||||
lastValueWasInline = simpleKeys[top].possible
|
||||
// A flow-sequence single-pair implicit key must be single-line, so its `:`
|
||||
// may not cross a line break; a flow-mapping adjacent value may.
|
||||
if simpleKeys[top].possible, crossedLineBreak, flowIsSequence.last == true {
|
||||
throw ScannerError(
|
||||
context: "while scanning a simple key",
|
||||
problem: "could not find expected ':'",
|
||||
mark: simpleKeys[top].mark,
|
||||
)
|
||||
}
|
||||
if simpleKeys[top].possible {
|
||||
if simpleKeys[top].tabbed {
|
||||
throw tabIndentationError()
|
||||
|
||||
@ -27,6 +27,11 @@ extension PureYAML.Parsing.LibYAML {
|
||||
/// sequence on that line (`key: - a`), whereas an explicit key whose `:` is
|
||||
/// on a later line may (`? k`\n`: - a`).
|
||||
var lastValueWasInline = false
|
||||
/// For each open flow level, whether it is a sequence (`[`) rather than a
|
||||
/// mapping (`{`). A flow-sequence single-pair implicit key is single-line
|
||||
/// (`c-s-implicit-json-key`), so its `:` may not cross a line break, unlike
|
||||
/// a flow-mapping adjacent value (`{ "a"`\n`: b }` is allowed).
|
||||
var flowIsSequence: [Bool] = []
|
||||
/// `parser->mark`, kept in sync with the reader at each fetch step.
|
||||
var mark: PureYAML.Parsing.Mark = .start
|
||||
/// `parser->stream_start_produced`.
|
||||
@ -108,9 +113,10 @@ extension PureYAML.Parsing.LibYAML {
|
||||
}
|
||||
|
||||
/// Port of `yaml_parser_increase_flow_level`. Push an empty simple key for
|
||||
/// the new flow level.
|
||||
mutating func increaseFlowLevel() {
|
||||
/// the new flow level, recording whether the level is a sequence.
|
||||
mutating func increaseFlowLevel(isSequence: Bool) {
|
||||
simpleKeys.append(.empty)
|
||||
flowIsSequence.append(isSequence)
|
||||
indentEngine.flowLevel += 1
|
||||
}
|
||||
|
||||
@ -120,6 +126,9 @@ extension PureYAML.Parsing.LibYAML {
|
||||
if indentEngine.flowLevel > 0 {
|
||||
indentEngine.flowLevel -= 1
|
||||
simpleKeys.removeLast()
|
||||
if !flowIsSequence.isEmpty {
|
||||
flowIsSequence.removeLast()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,8 +10,8 @@ source:
|
||||
# oracle: json if in.json present, else none
|
||||
# status: passing (PureYAML conformant today) | gap (not yet conformant)
|
||||
total: 402
|
||||
passing: 396
|
||||
gap: 6
|
||||
passing: 397
|
||||
gap: 5
|
||||
cases:
|
||||
- id: "229Q"
|
||||
name: "Spec Example 2.4. Sequence of Mappings"
|
||||
@ -3226,6 +3226,6 @@ cases:
|
||||
localPath: yaml-test-suite/ZXT5/in.yaml
|
||||
expected: reject
|
||||
oracle: none
|
||||
status: gap
|
||||
status: passing
|
||||
byteCount: 19
|
||||
lineCount: 2
|
||||
|
||||
@ -56,9 +56,10 @@ struct LibYAMLSimpleKeyTests {
|
||||
func test_flowLevelStack() {
|
||||
var scanner = PureYAML.Parsing.LibYAML.Scanner()
|
||||
#expect(scanner.simpleKeys.count == 1)
|
||||
scanner.increaseFlowLevel()
|
||||
scanner.increaseFlowLevel(isSequence: true)
|
||||
#expect(scanner.flowLevel == 1)
|
||||
#expect(scanner.simpleKeys.count == 2)
|
||||
#expect(scanner.flowIsSequence == [true])
|
||||
scanner.decreaseFlowLevel()
|
||||
#expect(scanner.flowLevel == 0)
|
||||
#expect(scanner.simpleKeys.count == 1)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user