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 } // A flow sequence element that begins with `:` is a single-pair mapping with an empty // key, e.g. `[ : value ]`; an element that begins with a node and is then followed by `:` // already becomes a mapping through the scalar key's implicit-key marker (`[ a: b ]`). if case .mappingValue = cursor.current?.kind { endMark = try parseEmptyKeyFlowMappingElement().endMark } else { 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") } /// Emits a single-pair flow mapping with an empty (null) key for a flow sequence element that /// begins with `:`. The value follows the `:` and may itself be empty before the next separator. mutating func parseEmptyKeyFlowMappingElement() throws -> PureYAML.Parsing.NodeResult { let mark = cursor.current?.mark ?? .start try emit(.mappingStart(anchor: nil, tag: nil, style: .flow, mark: mark)) try emit(.scalar(value: "", anchor: nil, tag: nil, style: .plain, mark: mark)) let valueIndicator = try expect("mapping value") { kind in if case .mappingValue = kind { return true } return false } let properties = try consumeProperties() let endMark: PureYAML.Parsing.Mark if properties == .none, cursor.current?.kind.isFlowTerminator != false { endMark = valueIndicator.endMark try emit(.scalar(value: "", anchor: nil, tag: nil, style: .plain, mark: endMark)) } else { endMark = try parseNode(properties: properties).endMark } try emit(.mappingEnd(mark: endMark)) return PureYAML.Parsing.NodeResult(kind: .mapping, endMark: endMark) } 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() } // A flow mapping key may be empty (`{ : v }`) or carry only properties (`{ !!str : v }`): // both resolve to a null key, so emit one rather than requiring a node before the `:`. This is // valid only in flow mapping key position, so the empty-node allowance stays local here. let keyProperties = try consumeProperties() if case .mappingValue = cursor.current?.kind { let mark = keyProperties.mark ?? cursor.current?.mark ?? .start try emit(.scalar(value: "", anchor: keyProperties.anchor, tag: keyProperties.tag, style: .plain, mark: mark)) } else { _ = try parseNode(properties: keyProperties) } 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) } }