A ':' value indicator with no preceding key is also valid inside a flow
mapping, denoting an entry whose key is empty (null). libyaml hands the
bare VALUE token to its node parser and fails with 'did not find expected
node content'. The flow-mapping-key parser now emits the empty scalar key
and lets the value state consume the indicator.
Fixes '{ : }' and '{ key: value, : empty key }', completing the
multi-document NKF9 case. Port score 282 -> 283.
150 lines
5.7 KiB
Swift
150 lines
5.7 KiB
Swift
extension PureYAML.Parsing.LibYAML.Parser {
|
|
/// Port of `yaml_parser_parse_flow_sequence_entry`. A `?` key inside a flow
|
|
/// sequence opens a single-pair flow mapping.
|
|
mutating func parseFlowSequenceEntry(first: Bool) throws -> Event {
|
|
if first {
|
|
marks.append(peekToken().startMark)
|
|
skipToken()
|
|
}
|
|
var token = peekToken()
|
|
if !isFlowSequenceEnd(token) {
|
|
if !first {
|
|
guard isFlowEntry(token) else {
|
|
throw ParserError(
|
|
context: "while parsing a flow sequence",
|
|
problem: "did not find expected ',' or ']'",
|
|
mark: marks.popLast() ?? token.startMark,
|
|
)
|
|
}
|
|
skipToken()
|
|
token = peekToken()
|
|
}
|
|
if isKey(token) {
|
|
state = .flowSequenceEntryMappingKey
|
|
skipToken()
|
|
return .mappingStart(anchor: nil, tag: nil, style: .flow, mark: token.startMark)
|
|
}
|
|
if !isFlowSequenceEnd(token) {
|
|
states.append(.flowSequenceEntry)
|
|
return try parseNode(block: false, indentlessSequence: false)
|
|
}
|
|
}
|
|
state = states.removeLast()
|
|
_ = marks.popLast()
|
|
skipToken()
|
|
return .sequenceEnd(mark: token.startMark)
|
|
}
|
|
|
|
/// Port of `yaml_parser_parse_flow_sequence_entry_mapping_key`.
|
|
mutating func parseFlowSequenceEntryMappingKey() throws -> Event {
|
|
let token = peekToken()
|
|
if !isValue(token), !isFlowEntry(token), !isFlowSequenceEnd(token) {
|
|
states.append(.flowSequenceEntryMappingValue)
|
|
return try parseNode(block: false, indentlessSequence: false)
|
|
}
|
|
if isFlowSequenceEnd(token) {
|
|
state = .flowSequenceEntryMappingValue
|
|
return processEmptyScalar(at: token.startMark)
|
|
}
|
|
let mark = token.endMark
|
|
skipToken()
|
|
state = .flowSequenceEntryMappingValue
|
|
return processEmptyScalar(at: mark)
|
|
}
|
|
|
|
/// Port of `yaml_parser_parse_flow_sequence_entry_mapping_value`.
|
|
mutating func parseFlowSequenceEntryMappingValue() throws -> Event {
|
|
var token = peekToken()
|
|
if isValue(token) {
|
|
skipToken()
|
|
token = peekToken()
|
|
if !isFlowEntry(token), !isFlowSequenceEnd(token) {
|
|
states.append(.flowSequenceEntryMappingEnd)
|
|
return try parseNode(block: false, indentlessSequence: false)
|
|
}
|
|
}
|
|
state = .flowSequenceEntryMappingEnd
|
|
return processEmptyScalar(at: token.startMark)
|
|
}
|
|
|
|
/// Port of `yaml_parser_parse_flow_sequence_entry_mapping_end`.
|
|
mutating func parseFlowSequenceEntryMappingEnd() throws -> Event {
|
|
let token = peekToken()
|
|
state = .flowSequenceEntry
|
|
return .mappingEnd(mark: token.startMark)
|
|
}
|
|
|
|
/// Port of `yaml_parser_parse_flow_mapping_key`.
|
|
mutating func parseFlowMappingKey(first: Bool) throws -> Event {
|
|
if first {
|
|
marks.append(peekToken().startMark)
|
|
skipToken()
|
|
}
|
|
var token = peekToken()
|
|
if !isFlowMappingEnd(token) {
|
|
if !first {
|
|
guard isFlowEntry(token) else {
|
|
throw ParserError(
|
|
context: "while parsing a flow mapping",
|
|
problem: "did not find expected ',' or '}'",
|
|
mark: marks.popLast() ?? token.startMark,
|
|
)
|
|
}
|
|
skipToken()
|
|
token = peekToken()
|
|
}
|
|
if isKey(token) {
|
|
return try flowMappingKeyEvent()
|
|
}
|
|
// YAML 1.2 permits an empty (null) key in a flow mapping: a `:` with
|
|
// no preceding key denotes an entry whose key is empty. libyaml hands
|
|
// the VALUE token to `parse_node` and fails; instead emit the empty
|
|
// scalar key and let `parseFlowMappingValue` consume the indicator.
|
|
if isValue(token) {
|
|
state = .flowMappingValue
|
|
return processEmptyScalar(at: token.startMark)
|
|
}
|
|
if !isFlowMappingEnd(token) {
|
|
states.append(.flowMappingEmptyValue)
|
|
return try parseNode(block: false, indentlessSequence: false)
|
|
}
|
|
}
|
|
state = states.removeLast()
|
|
_ = marks.popLast()
|
|
skipToken()
|
|
return .mappingEnd(mark: token.startMark)
|
|
}
|
|
|
|
/// The KEY branch of `parse_flow_mapping_key`: parse the key node, or an empty
|
|
/// scalar when a value/entry/end follows the `?` immediately.
|
|
private mutating func flowMappingKeyEvent() throws -> Event {
|
|
skipToken()
|
|
let token = peekToken()
|
|
if !isValue(token), !isFlowEntry(token), !isFlowMappingEnd(token) {
|
|
states.append(.flowMappingValue)
|
|
return try parseNode(block: false, indentlessSequence: false)
|
|
}
|
|
state = .flowMappingValue
|
|
return processEmptyScalar(at: token.startMark)
|
|
}
|
|
|
|
/// Port of `yaml_parser_parse_flow_mapping_value`.
|
|
mutating func parseFlowMappingValue(empty: Bool) throws -> Event {
|
|
var token = peekToken()
|
|
if empty {
|
|
state = .flowMappingKey
|
|
return processEmptyScalar(at: token.startMark)
|
|
}
|
|
if isValue(token) {
|
|
skipToken()
|
|
token = peekToken()
|
|
if !isFlowEntry(token), !isFlowMappingEnd(token) {
|
|
states.append(.flowMappingKey)
|
|
return try parseNode(block: false, indentlessSequence: false)
|
|
}
|
|
}
|
|
state = .flowMappingKey
|
|
return processEmptyScalar(at: token.startMark)
|
|
}
|
|
}
|