extension PureYAML.Parsing.Scanner { func scanFlowToken( _ character: Character, state: inout State, ) -> Bool { switch character { case "[": openFlowCollection(kind: .sequence, token: .flowSequenceStart, state: &state) return true case "]": closeFlowCollection(token: .flowSequenceEnd, state: &state) return true case "{": openFlowCollection(kind: .mapping, token: .flowMappingStart, state: &state) return true case "}": closeFlowCollection(token: .flowMappingEnd, state: &state) return true case ",": // A flow entry separator abandons any pending simple key and reopens the // door for a fresh one on the next entry (libyaml fetch_flow_entry). removeFlowSimpleKey(&state) scanFixedToken(.flowEntry, state: &state) state.simpleKeyAllowed = true if state.flowContexts.last?.kind == .mapping { state.flowContexts[state.flowContexts.count - 1].isExpectingKey = true } return true default: return false } } /// Opens a flow collection (`[` or `{`). The opener itself may be a simple key /// at the enclosing level, so a key is saved before the flow level rises; a /// fresh simple-key slot is then pushed and a key is allowed inside. func openFlowCollection( kind: PureYAML.Parsing.ScannerFlowContextKind, token: PureYAML.Parsing.TokenKind, state: inout State, ) { saveFlowSimpleKey(&state) state.flowDepth += 1 state.flowContexts.append(.init(kind: kind, isExpectingKey: kind == .mapping)) state.flowSimpleKeys.append(nil) scanFixedToken(token, state: &state) state.simpleKeyAllowed = true } /// Closes a flow collection (`]` or `}`): the pending simple key on this level is /// dropped, the level is popped, and no simple key may immediately follow the /// closing indicator (libyaml fetch_flow_collection_end). func closeFlowCollection( token: PureYAML.Parsing.TokenKind, state: inout State, ) { removeFlowSimpleKey(&state) state.flowDepth = max(0, state.flowDepth - 1) scanFixedToken(token, state: &state) _ = state.flowContexts.popLast() _ = state.flowSimpleKeys.popLast() state.simpleKeyAllowed = false } /// Records a potential simple key at the current reader position for the /// innermost flow level, if a simple key is allowed there. Mirrors libyaml's /// `yaml_parser_save_simple_key`: the saved key remembers the index of the /// next token to be appended, so a later `:` can splice a ``mappingKey`` in /// front of the node that starts here. No-op outside flow context. func saveFlowSimpleKey(_ state: inout State) { guard state.flowDepth > 0, state.simpleKeyAllowed, !state.flowSimpleKeys.isEmpty else { return } state.flowSimpleKeys[state.flowSimpleKeys.count - 1] = PureYAML.Parsing.ScannerFlowSimpleKey( tokenIndex: state.tokens.count, mark: state.reader.mark, ) } /// Discards any potential simple key on the innermost flow level. Mirrors /// libyaml's `yaml_parser_remove_simple_key` for the flow context, where a /// simple key is never *required*, so abandoning it is not an error. func removeFlowSimpleKey(_ state: inout State) { guard !state.flowSimpleKeys.isEmpty else { return } state.flowSimpleKeys[state.flowSimpleKeys.count - 1] = nil } /// Promotes a pending flow simple key to an actual mapping key by inserting a /// ``TokenKind/mappingKey`` token at the saved index, mirroring libyaml's /// `fetch_value` splicing a KEY token before a simple key. Returns true when a /// key was inserted so the caller can avoid emitting a redundant one. @discardableResult func promoteFlowSimpleKey(_ state: inout State) -> Bool { guard !state.flowSimpleKeys.isEmpty, let key = state.flowSimpleKeys[state.flowSimpleKeys.count - 1] else { return false } let token = PureYAML.Parsing.Token(kind: .mappingKey, mark: key.mark, endMark: key.mark) let index = min(key.tokenIndex, state.tokens.count) state.tokens.insert(token, at: index) state.flowSimpleKeys[state.flowSimpleKeys.count - 1] = nil return true } }