extension PureYAML.Parsing.LibYAML.Parser { typealias NodeProperties = PureYAML.Parsing.LibYAML.NodeProperties /// Port of `yaml_parser_parse_node`. An ALIAS is emitted directly; otherwise /// the node's anchor/tag properties are consumed, the tag is resolved, and the /// content token selects a scalar, a flow/block collection start, or an empty /// scalar standing in for a property-only node. mutating func parseNode(block: Bool, indentlessSequence: Bool) throws -> Event { let token = peekToken() if case let .alias(name) = token.type { state = states.removeLast() skipToken() return .alias(anchor: name, mark: token.startMark) } let props = consumeNodeProperties() let tag = try props.tagHandle.map { try resolveTag(handle: $0, suffix: props.tagSuffix ?? "", startMark: props.startMark, tagMark: props.tagMark) } return try dispatchNodeContent(block: block, indentlessSequence: indentlessSequence, props: props, tag: tag) } /// Consume `properties ::= TAG ANCHOR? | ANCHOR TAG?` ahead of the content. private mutating func consumeNodeProperties() -> NodeProperties { var token = peekToken() var props = NodeProperties(anchor: nil, tagHandle: nil, tagSuffix: nil, startMark: token.startMark, tagMark: token.startMark) if case let .anchor(name) = token.type { props.anchor = name skipToken() token = peekToken() if case let .tag(handle, suffix) = token.type { props.tagHandle = handle props.tagSuffix = suffix props.tagMark = token.startMark skipToken() } } else if case let .tag(handle, suffix) = token.type { props.tagHandle = handle props.tagSuffix = suffix props.tagMark = token.startMark skipToken() token = peekToken() if case let .anchor(name) = token.type { props.anchor = name skipToken() } } return props } /// Select the event for the content token following a node's properties. private mutating func dispatchNodeContent(block: Bool, indentlessSequence: Bool, props: NodeProperties, tag: String?) throws -> Event { let token = peekToken() if indentlessSequence, case .blockEntry = token.type { state = .indentlessSequenceEntry return .sequenceStart(anchor: props.anchor, tag: tag, style: .block, mark: props.startMark) } if let event = contentEvent(token: token, block: block, props: props, tag: tag) { return event } return try emptyNodeOrError(token: token, block: block, props: props, tag: tag) } /// A scalar or a flow/block collection start, or nil if the token is neither. private mutating func contentEvent(token: Token, block: Bool, props: NodeProperties, tag: String?) -> Event? { switch token.type { case let .scalar(value, style): state = states.removeLast() skipToken() return .scalar(value: value, anchor: props.anchor, tag: tag, style: style, mark: props.startMark, endMark: token.endMark) case .flowSequenceStart: state = .flowSequenceFirstEntry return .sequenceStart(anchor: props.anchor, tag: tag, style: .flow, mark: props.startMark) case .flowMappingStart: state = .flowMappingFirstKey return .mappingStart(anchor: props.anchor, tag: tag, style: .flow, mark: props.startMark) case .blockSequenceStart where block: state = .blockSequenceFirstEntry return .sequenceStart(anchor: props.anchor, tag: tag, style: .block, mark: props.startMark) case .blockMappingStart where block: state = .blockMappingFirstKey return .mappingStart(anchor: props.anchor, tag: tag, style: .block, mark: props.startMark) default: return nil } } /// An empty scalar when the node carried only properties, else a parse error. private mutating func emptyNodeOrError(token: Token, block: Bool, props: NodeProperties, tag: String?) throws -> Event { if props.anchor != nil || tag != nil { state = states.removeLast() return .scalar(value: "", anchor: props.anchor, tag: tag, style: .plain, mark: props.startMark, endMark: props.startMark) } throw ParserError( context: block ? "while parsing a block node" : "while parsing a flow node", problem: "did not find expected node content", mark: token.startMark, ) } }