A flow sequence entry that begins with ':' (e.g. '[ : v ]') is a single-pair mapping with an empty key, which YAML 1.2 permits but libyaml rejects by handing the leading VALUE token to its node parser. Open the implicit single-pair mapping on a leading ':' in the flow-sequence-entry parser, and leave the VALUE token for the mapping value state to consume so the value is parsed rather than dropped. Surpasses libyaml on CFD4. Port score 298 -> 299.
158 lines
6.2 KiB
Swift
158 lines
6.2 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 isValue(token) {
|
|
// A leading `:` opens a single-pair mapping with an empty key
|
|
// (`[ : v ]`). Keep the VALUE token for the mapping-key state.
|
|
state = .flowSequenceEntryMappingKey
|
|
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)
|
|
}
|
|
// An empty key: leave a VALUE token for the value state to consume (so the
|
|
// value is parsed); a closing `]` ends the pair with an empty value too.
|
|
if isValue(token) || 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)
|
|
}
|
|
}
|