extension PureYAML.Parsing.TokenEventParser { /// Parses a flow sequence, mirroring libyaml's `flow_sequence_entry` /// productions. Each entry is either a flow node or, when introduced by a /// `mappingKey` (`?`) or recognized as a single-pair `key: value`, a one-entry /// flow mapping nested directly in the sequence. 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, )) 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 { _ = try cursor.advance() continue } endMark = try parseFlowSequenceEntry().endMark if cursor.current?.kind.isFlowTerminator == false { throw unexpectedToken(expected: "flow entry or flow sequence end") } } throw unexpectedToken(expected: "flow sequence end") } /// Parses a single flow-sequence entry. A `mappingKey` (`?`, or one spliced in /// by the simple-key mechanism before a `key: value` pair) produces a /// single-pair flow mapping wrapping the entry; otherwise the entry is a plain /// flow node. mutating func parseFlowSequenceEntry() throws -> PureYAML.Parsing.NodeResult { if cursor.current?.kind.isMappingKey == true { return try parseFlowSequenceSinglePair() } return try parseNode() } /// Wraps an explicit-`?` single-pair entry of a flow sequence in a one-entry /// flow mapping, mirroring libyaml's `flow_sequence_entry_mapping_*` states. mutating func parseFlowSequenceSinglePair() throws -> PureYAML.Parsing.NodeResult { let keyIndicator = try expect("mapping key") { $0.isMappingKey } try emit(.mappingStart(anchor: nil, tag: nil, style: .flow, mark: keyIndicator.mark)) try parseFlowSinglePairKeyValue(keyIndicator: keyIndicator) let mark = cursor.current?.mark ?? keyIndicator.endMark try emit(.mappingEnd(mark: mark)) return PureYAML.Parsing.NodeResult(kind: .mapping, endMark: mark) } 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, )) 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 { _ = try cursor.advance() continue } endMark = try parseFlowMappingPair().endMark if cursor.current?.kind.isFlowTerminator == false { throw unexpectedToken(expected: "flow entry or flow mapping end") } } throw unexpectedToken(expected: "flow mapping end") } /// Parses one flow-mapping entry. With a `mappingKey` (`?`) indicator the key /// follows the explicit form; without one the entry is a `flow_node` that is /// itself the key (carrying an empty value unless a `:` provides one). mutating func parseFlowMappingPair() throws -> PureYAML.Parsing.NodeResult { if cursor.current?.kind.isMappingKey == true { let keyIndicator = try expect("mapping key") { $0.isMappingKey } return try parseFlowSinglePairKeyValue(keyIndicator: keyIndicator) } try parseFlowMappingImplicitKey() return try parseFlowMappingValue() } /// Parses the key and value of a flow pair introduced by an explicit `?`, /// where each side may be absent (composing an empty scalar). Shared by flow /// mappings and single-pair entries nested in a flow sequence. @discardableResult mutating func parseFlowSinglePairKeyValue( keyIndicator: PureYAML.Parsing.Token, ) throws -> PureYAML.Parsing.NodeResult { if flowKeyIsEmpty() { try emitEmptyScalar(properties: .none, mark: keyIndicator.endMark) } else { // The key of a flow pair follows the same rules whether the pair was // introduced by an explicit `?` or had a `mappingKey` spliced in by the // simple-key mechanism: a bare tag/anchor before the `:` composes an empty // tagged scalar key (`{ !!str : bar }`), and an alias before the `:` is the // key itself. Routing through `parseNode` would instead treat the tag/`:` // as a nested block mapping, so use the flow key parser shared with the // implicit form. try parseFlowMappingImplicitKey() } return try parseFlowMappingValue() } /// Parses the key of an implicit flow-mapping entry (`key : value` with no /// explicit `?`). The key may be absent entirely (`{ : v }`, `{ , : v }`) /// or be only a tag/anchor with no content, both composing to an empty /// scalar key rather than routing into a block mapping. mutating func parseFlowMappingImplicitKey() throws { let properties = try consumeProperties() if properties == .none, cursor.current?.kind.isMappingValue == true { let mark = cursor.current?.mark ?? cursor.previous?.endMark ?? .start try emitEmptyScalar(properties: .none, mark: mark) return } if properties != .none, cursor.current?.kind.isMappingValue == true || cursor.current?.kind.isFlowTerminator == true { let mark = cursor.current?.mark ?? cursor.previous?.endMark ?? .start try emitEmptyScalar(properties: properties, mark: mark) return } // An alias as a flow-mapping key (`{*a: b}`) is emitted directly so it is // not re-routed into a block mapping by the alias-as-key check in // `parseNode`, which only applies to block context. if properties == .none, cursor.current?.kind.isAlias == true, try cursor.peek()?.kind.isMappingValue == true { try emitAliasKey() return } _ = try parseNode(properties: properties) } /// Parses the value half of a flow-mapping entry. A `:` consumes its node (or an /// empty scalar when the value is absent); without a `:` the entry has an /// implicit empty value (libyaml's `flow_mapping_value` with `empty == 1`). mutating func parseFlowMappingValue() throws -> PureYAML.Parsing.NodeResult { guard cursor.current?.kind.isMappingValue == true else { let mark = cursor.current?.mark ?? cursor.previous?.endMark ?? .start try emitEmptyScalar(properties: .none, mark: mark) return PureYAML.Parsing.NodeResult(kind: .scalar, endMark: mark) } let valueIndicator = try expect("mapping value") { kind in if case .mappingValue = kind { return true } return false } let properties = try consumeProperties() guard properties != .none || canBeginNode(cursor.current), cursor.current?.kind.isFlowTerminator == false else { let mark = valueIndicator.endMark try emitEmptyScalar(properties: properties, mark: mark) return PureYAML.Parsing.NodeResult(kind: .scalar, endMark: mark) } return try parseNode(properties: properties) } /// True when the key position right after a `?` indicator is empty, i.e. the /// next token is a `:` value indicator, a flow terminator, or the end of input, /// so an empty scalar key must be composed (libyaml's `flow_mapping_key`). func flowKeyIsEmpty() -> Bool { guard let current = cursor.current else { return true } return current.kind.isMappingValue || current.kind.isFlowTerminator } }