PureYAML/Sources/Parsing/LibYAML.Parser.Block.swift
Mihaela Mihaljevic 521062d974 feat(parsing): port libyaml parser state machine to Swift
Ports parser.c: the one-event-at-a-time state machine that turns the
scanner's token stream into the YAML event stream. Covers stream and
document framing, directive processing with %TAG handle resolution and
default handles, the node property/tag/anchor production, block and
indentless sequences, block mappings, and flow sequences and mappings
including the single-pair `?` mapping forms.

Output verified against the libyaml reference event dumper: the empty
mapping value, %TAG resolution, and shorthand tags all match byte for
byte. 331 tests, no regression.
2026-06-24 17:05:59 +02:00

102 lines
3.7 KiB
Swift

extension PureYAML.Parsing.LibYAML.Parser {
/// Port of `yaml_parser_parse_block_sequence_entry`.
mutating func parseBlockSequenceEntry(first: Bool) throws -> Event {
if first {
marks.append(peekToken().startMark)
skipToken()
}
let token = peekToken()
if case .blockEntry = token.type {
let mark = token.endMark
skipToken()
let next = peekToken()
if !isBlockEntry(next), !isBlockEnd(next) {
states.append(.blockSequenceEntry)
return try parseNode(block: true, indentlessSequence: false)
}
state = .blockSequenceEntry
return processEmptyScalar(at: mark)
}
if case .blockEnd = token.type {
state = states.removeLast()
_ = marks.popLast()
skipToken()
return .sequenceEnd(mark: token.startMark)
}
throw ParserError(
context: "while parsing a block collection",
problem: "did not find expected '-' indicator",
mark: marks.popLast() ?? token.startMark,
)
}
/// Port of `yaml_parser_parse_indentless_sequence_entry`. The closing token
/// belongs to the enclosing collection, so the SEQUENCE-END is emitted without
/// consuming it.
mutating func parseIndentlessSequenceEntry() throws -> Event {
let token = peekToken()
guard case .blockEntry = token.type else {
state = states.removeLast()
return .sequenceEnd(mark: token.startMark)
}
let mark = token.endMark
skipToken()
let next = peekToken()
if !isBlockEntry(next), !isKey(next), !isValue(next), !isBlockEnd(next) {
states.append(.indentlessSequenceEntry)
return try parseNode(block: true, indentlessSequence: false)
}
state = .indentlessSequenceEntry
return processEmptyScalar(at: mark)
}
/// Port of `yaml_parser_parse_block_mapping_key`.
mutating func parseBlockMappingKey(first: Bool) throws -> Event {
if first {
marks.append(peekToken().startMark)
skipToken()
}
let token = peekToken()
if case .key = token.type {
let mark = token.endMark
skipToken()
let next = peekToken()
if !isKey(next), !isValue(next), !isBlockEnd(next) {
states.append(.blockMappingValue)
return try parseNode(block: true, indentlessSequence: true)
}
state = .blockMappingValue
return processEmptyScalar(at: mark)
}
if case .blockEnd = token.type {
state = states.removeLast()
_ = marks.popLast()
skipToken()
return .mappingEnd(mark: token.startMark)
}
throw ParserError(
context: "while parsing a block mapping",
problem: "did not find expected key",
mark: marks.popLast() ?? token.startMark,
)
}
/// Port of `yaml_parser_parse_block_mapping_value`.
mutating func parseBlockMappingValue() throws -> Event {
let token = peekToken()
guard case .value = token.type else {
state = .blockMappingKey
return processEmptyScalar(at: token.startMark)
}
let mark = token.endMark
skipToken()
let next = peekToken()
if !isKey(next), !isValue(next), !isBlockEnd(next) {
states.append(.blockMappingKey)
return try parseNode(block: true, indentlessSequence: true)
}
state = .blockMappingKey
return processEmptyScalar(at: mark)
}
}