A ':' value indicator with no preceding key on its line denotes a mapping entry whose key is empty (null), which YAML 1.2 permits but libyaml rejects with 'did not find expected key'. The block-mapping-key parser now emits the empty scalar key and leaves the VALUE token for the value state to consume, rather than throwing. Fixes the canonical event streams for plain empty-key mappings, explicit empty keys with anchors and aliases, empty keys nested in sequences, and mixed empty-key/value entries. Port score 275 -> 282; surpasses libyaml on 2JQS, 6M2F, M2N8/00, NHX8, S3PD, SM9W/01, UKK6/00.
110 lines
4.1 KiB
Swift
110 lines
4.1 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)
|
|
}
|
|
// YAML 1.2 permits an empty (null) key: a VALUE indicator with no
|
|
// preceding key denotes a mapping entry whose key is empty. libyaml
|
|
// rejects this here; instead emit the empty scalar key and leave the
|
|
// VALUE token for `parseBlockMappingValue` to consume.
|
|
if case .value = token.type {
|
|
state = .blockMappingValue
|
|
return processEmptyScalar(at: 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)
|
|
}
|
|
}
|