In a flow sequence or mapping a comma separates entries, so one must
precede it. The parser advanced past a comma without checking that an
entry came before, accepting a leading comma ([ , a ]) or a repeated comma
([ a, , b ]) as empty entries. Each flow loop now tracks whether an entry
precedes the comma and rejects it otherwise. A single trailing comma
([ a, b, ]) and empty collections ([], {}) remain valid, since the closing
bracket is handled before that check.
Measured against the official YAML test-suite, parse-level conformance
rises from 249/333 (74.8%) to 251/333 (75.4%) with no regression.
123 lines
4.8 KiB
Swift
123 lines
4.8 KiB
Swift
extension PureYAML.Parsing.TokenEventParser {
|
|
mutating func parseFlowSequence(
|
|
properties: PureYAML.Parsing.NodeProperties,
|
|
) throws -> PureYAML.Parsing.NodeResult {
|
|
let start = try expect("flow sequence start") { kind in
|
|
if case .flowSequenceStart = kind {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
let startMark = properties.mark ?? start.mark
|
|
var endMark = start.endMark
|
|
try emit(.sequenceStart(
|
|
anchor: properties.anchor,
|
|
tag: properties.tag,
|
|
style: .flow,
|
|
mark: startMark,
|
|
))
|
|
var hasEntry = false
|
|
while cursor.current != nil {
|
|
if case .flowSequenceEnd = cursor.current?.kind {
|
|
let end = try expect("flow sequence end") { kind in
|
|
if case .flowSequenceEnd = kind {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
endMark = end.endMark
|
|
try emit(.sequenceEnd(mark: endMark))
|
|
return PureYAML.Parsing.NodeResult(kind: .sequence, endMark: endMark)
|
|
}
|
|
if case .flowEntry = cursor.current?.kind {
|
|
// A comma separates entries, so one must precede it: a leading or repeated comma
|
|
// is an empty entry, which is not allowed. A single trailing comma before the
|
|
// closing bracket is fine, since the close is handled above regardless of `hasEntry`.
|
|
guard hasEntry else {
|
|
throw unexpectedToken(expected: "flow sequence entry or flow sequence end")
|
|
}
|
|
hasEntry = false
|
|
_ = try cursor.advance()
|
|
continue
|
|
}
|
|
endMark = try parseNode().endMark
|
|
hasEntry = true
|
|
if cursor.current?.kind.isFlowTerminator == false {
|
|
throw unexpectedToken(expected: "flow entry or flow sequence end")
|
|
}
|
|
}
|
|
throw unexpectedToken(expected: "flow sequence end")
|
|
}
|
|
|
|
mutating func parseFlowMapping(
|
|
properties: PureYAML.Parsing.NodeProperties,
|
|
) throws -> PureYAML.Parsing.NodeResult {
|
|
let start = try expect("flow mapping start") { kind in
|
|
if case .flowMappingStart = kind {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
let startMark = properties.mark ?? start.mark
|
|
var endMark = start.endMark
|
|
try emit(.mappingStart(
|
|
anchor: properties.anchor,
|
|
tag: properties.tag,
|
|
style: .flow,
|
|
mark: startMark,
|
|
))
|
|
var hasEntry = false
|
|
while cursor.current != nil {
|
|
if case .flowMappingEnd = cursor.current?.kind {
|
|
let end = try expect("flow mapping end") { kind in
|
|
if case .flowMappingEnd = kind {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
endMark = end.endMark
|
|
try emit(.mappingEnd(mark: endMark))
|
|
return PureYAML.Parsing.NodeResult(kind: .mapping, endMark: endMark)
|
|
}
|
|
if case .flowEntry = cursor.current?.kind {
|
|
// As in a flow sequence, a comma separates entries and so requires one before it;
|
|
// a leading or repeated comma is an empty entry. A trailing comma is fine.
|
|
guard hasEntry else {
|
|
throw unexpectedToken(expected: "flow mapping entry or flow mapping end")
|
|
}
|
|
hasEntry = false
|
|
_ = try cursor.advance()
|
|
continue
|
|
}
|
|
endMark = try parseFlowMappingPair().endMark
|
|
hasEntry = true
|
|
if cursor.current?.kind.isFlowTerminator == false {
|
|
throw unexpectedToken(expected: "flow entry or flow mapping end")
|
|
}
|
|
}
|
|
throw unexpectedToken(expected: "flow mapping end")
|
|
}
|
|
|
|
mutating func parseFlowMappingPair() throws -> PureYAML.Parsing.NodeResult {
|
|
if cursor.current?.kind.isMappingKey == true {
|
|
return try parseMappingPair()
|
|
}
|
|
|
|
_ = try parseNode()
|
|
let valueIndicator = try expect("mapping value") { kind in
|
|
if case .mappingValue = kind {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
let properties = try consumeProperties()
|
|
guard properties != .none || cursor.current?.kind.isFlowTerminator == false else {
|
|
let mark = valueIndicator.endMark
|
|
try emit(.scalar(value: "", anchor: nil, tag: nil, style: .plain, mark: mark))
|
|
return PureYAML.Parsing.NodeResult(kind: .scalar, endMark: mark)
|
|
}
|
|
return try parseNode(properties: properties)
|
|
}
|
|
}
|