The scalar event now carries the scanner token's end mark, so a located scalar's range spans the whole token (end exclusive, one past the last character) instead of being start-anchored. This gives consumers exact highlight widths and is the prerequisite for surgical, position-based text edits. Only the scalar event case gained a field; the value tree, its equality, and the event dump (which compares value and style, not marks) are unchanged, so the canonical-event and whole-suite gates stay green (343 tests).
100 lines
4.7 KiB
Swift
100 lines
4.7 KiB
Swift
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,
|
|
)
|
|
}
|
|
}
|