The gap-only manifest stopped guarding a case once it was fixed, so later work silently regressed previously-fixed cases (caught only by an external full-suite run, not the gated sweep or full corpus). Replace it with a full manifest of all 402 official cases (Tests/Fixtures/yaml-test-suite-manifest.yaml), each carrying its conformant expectation and a passing/gap status, with every case's files vendored. The gated sweep now asserts every passing case still passes (the regression guard) and every gap still reproduces, so any regression fails the build. The honest gate-enforced count is 329/402 passing (the earlier 348 came from a more lenient one-time oracle). Repairs three regressions the new gate revealed: the bucket-6 tab rule over-rejected tabs in block-scalar content and as separation before a scalar value (root cause: the batch scanner and the incremental TokenCursor had divergent line-start loops; unified onto a single scanStep, fixing 96NN/00, 96NN/01, R4YG, Y79Y/001, DK95/00, DK95/03, DK95/07, 6CA3, Q5MG, J3BT); an anchor on a prior line before an alias key wrongly rejected 26DV; and a tag-only flow key mis-parsed WZ62. Genuine tab-indentation errors (4EJS, Y79Y/000) still reject.
204 lines
9.3 KiB
Swift
204 lines
9.3 KiB
Swift
extension PureYAML.Parsing.TokenEventParser {
|
|
/// Parses a flow sequence, mirroring libyaml's `flow_sequence_entry`
|
|
/// productions. Each entry is either a flow node or, when introduced by a
|
|
/// `mappingKey` (`?`) or recognized as a single-pair `key: value`, a one-entry
|
|
/// flow mapping nested directly in the sequence.
|
|
mutating func parseFlowSequence(
|
|
properties: PureYAML.Parsing.NodeProperties,
|
|
) throws -> PureYAML.Parsing.NodeResult {
|
|
let start = try expect("flow sequence start") { kind in
|
|
if case .flowSequenceStart = kind {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
let startMark = properties.mark ?? start.mark
|
|
var endMark = start.endMark
|
|
try emit(.sequenceStart(
|
|
anchor: properties.anchor,
|
|
tag: properties.tag,
|
|
style: .flow,
|
|
mark: startMark,
|
|
))
|
|
while cursor.current != nil {
|
|
if case .flowSequenceEnd = cursor.current?.kind {
|
|
let end = try expect("flow sequence end") { kind in
|
|
if case .flowSequenceEnd = kind {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
endMark = end.endMark
|
|
try emit(.sequenceEnd(mark: endMark))
|
|
return PureYAML.Parsing.NodeResult(kind: .sequence, endMark: endMark)
|
|
}
|
|
if case .flowEntry = cursor.current?.kind {
|
|
_ = try cursor.advance()
|
|
continue
|
|
}
|
|
endMark = try parseFlowSequenceEntry().endMark
|
|
if cursor.current?.kind.isFlowTerminator == false {
|
|
throw unexpectedToken(expected: "flow entry or flow sequence end")
|
|
}
|
|
}
|
|
throw unexpectedToken(expected: "flow sequence end")
|
|
}
|
|
|
|
/// Parses a single flow-sequence entry. A `mappingKey` (`?`, or one spliced in
|
|
/// by the simple-key mechanism before a `key: value` pair) produces a
|
|
/// single-pair flow mapping wrapping the entry; otherwise the entry is a plain
|
|
/// flow node.
|
|
mutating func parseFlowSequenceEntry() throws -> PureYAML.Parsing.NodeResult {
|
|
if cursor.current?.kind.isMappingKey == true {
|
|
return try parseFlowSequenceSinglePair()
|
|
}
|
|
return try parseNode()
|
|
}
|
|
|
|
/// Wraps an explicit-`?` single-pair entry of a flow sequence in a one-entry
|
|
/// flow mapping, mirroring libyaml's `flow_sequence_entry_mapping_*` states.
|
|
mutating func parseFlowSequenceSinglePair() throws -> PureYAML.Parsing.NodeResult {
|
|
let keyIndicator = try expect("mapping key") { $0.isMappingKey }
|
|
try emit(.mappingStart(anchor: nil, tag: nil, style: .flow, mark: keyIndicator.mark))
|
|
try parseFlowSinglePairKeyValue(keyIndicator: keyIndicator)
|
|
let mark = cursor.current?.mark ?? keyIndicator.endMark
|
|
try emit(.mappingEnd(mark: mark))
|
|
return PureYAML.Parsing.NodeResult(kind: .mapping, endMark: mark)
|
|
}
|
|
|
|
mutating func parseFlowMapping(
|
|
properties: PureYAML.Parsing.NodeProperties,
|
|
) throws -> PureYAML.Parsing.NodeResult {
|
|
let start = try expect("flow mapping start") { kind in
|
|
if case .flowMappingStart = kind {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
let startMark = properties.mark ?? start.mark
|
|
var endMark = start.endMark
|
|
try emit(.mappingStart(
|
|
anchor: properties.anchor,
|
|
tag: properties.tag,
|
|
style: .flow,
|
|
mark: startMark,
|
|
))
|
|
while cursor.current != nil {
|
|
if case .flowMappingEnd = cursor.current?.kind {
|
|
let end = try expect("flow mapping end") { kind in
|
|
if case .flowMappingEnd = kind {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
endMark = end.endMark
|
|
try emit(.mappingEnd(mark: endMark))
|
|
return PureYAML.Parsing.NodeResult(kind: .mapping, endMark: endMark)
|
|
}
|
|
if case .flowEntry = cursor.current?.kind {
|
|
_ = try cursor.advance()
|
|
continue
|
|
}
|
|
endMark = try parseFlowMappingPair().endMark
|
|
if cursor.current?.kind.isFlowTerminator == false {
|
|
throw unexpectedToken(expected: "flow entry or flow mapping end")
|
|
}
|
|
}
|
|
throw unexpectedToken(expected: "flow mapping end")
|
|
}
|
|
|
|
/// Parses one flow-mapping entry. With a `mappingKey` (`?`) indicator the key
|
|
/// follows the explicit form; without one the entry is a `flow_node` that is
|
|
/// itself the key (carrying an empty value unless a `:` provides one).
|
|
mutating func parseFlowMappingPair() throws -> PureYAML.Parsing.NodeResult {
|
|
if cursor.current?.kind.isMappingKey == true {
|
|
let keyIndicator = try expect("mapping key") { $0.isMappingKey }
|
|
return try parseFlowSinglePairKeyValue(keyIndicator: keyIndicator)
|
|
}
|
|
try parseFlowMappingImplicitKey()
|
|
return try parseFlowMappingValue()
|
|
}
|
|
|
|
/// Parses the key and value of a flow pair introduced by an explicit `?`,
|
|
/// where each side may be absent (composing an empty scalar). Shared by flow
|
|
/// mappings and single-pair entries nested in a flow sequence.
|
|
@discardableResult
|
|
mutating func parseFlowSinglePairKeyValue(
|
|
keyIndicator: PureYAML.Parsing.Token,
|
|
) throws -> PureYAML.Parsing.NodeResult {
|
|
if flowKeyIsEmpty() {
|
|
try emitEmptyScalar(properties: .none, mark: keyIndicator.endMark)
|
|
} else {
|
|
// The key of a flow pair follows the same rules whether the pair was
|
|
// introduced by an explicit `?` or had a `mappingKey` spliced in by the
|
|
// simple-key mechanism: a bare tag/anchor before the `:` composes an empty
|
|
// tagged scalar key (`{ !!str : bar }`), and an alias before the `:` is the
|
|
// key itself. Routing through `parseNode` would instead treat the tag/`:`
|
|
// as a nested block mapping, so use the flow key parser shared with the
|
|
// implicit form.
|
|
try parseFlowMappingImplicitKey()
|
|
}
|
|
return try parseFlowMappingValue()
|
|
}
|
|
|
|
/// Parses the key of an implicit flow-mapping entry (`key : value` with no
|
|
/// explicit `?`). The key may be absent entirely (`{ : v }`, `{ , : v }`)
|
|
/// or be only a tag/anchor with no content, both composing to an empty
|
|
/// scalar key rather than routing into a block mapping.
|
|
mutating func parseFlowMappingImplicitKey() throws {
|
|
let properties = try consumeProperties()
|
|
if properties == .none, cursor.current?.kind.isMappingValue == true {
|
|
let mark = cursor.current?.mark ?? cursor.previous?.endMark ?? .start
|
|
try emitEmptyScalar(properties: .none, mark: mark)
|
|
return
|
|
}
|
|
if properties != .none, cursor.current?.kind.isMappingValue == true || cursor.current?.kind.isFlowTerminator == true {
|
|
let mark = cursor.current?.mark ?? cursor.previous?.endMark ?? .start
|
|
try emitEmptyScalar(properties: properties, mark: mark)
|
|
return
|
|
}
|
|
// An alias as a flow-mapping key (`{*a: b}`) is emitted directly so it is
|
|
// not re-routed into a block mapping by the alias-as-key check in
|
|
// `parseNode`, which only applies to block context.
|
|
if properties == .none, cursor.current?.kind.isAlias == true, try cursor.peek()?.kind.isMappingValue == true {
|
|
try emitAliasKey()
|
|
return
|
|
}
|
|
_ = try parseNode(properties: properties)
|
|
}
|
|
|
|
/// Parses the value half of a flow-mapping entry. A `:` consumes its node (or an
|
|
/// empty scalar when the value is absent); without a `:` the entry has an
|
|
/// implicit empty value (libyaml's `flow_mapping_value` with `empty == 1`).
|
|
mutating func parseFlowMappingValue() throws -> PureYAML.Parsing.NodeResult {
|
|
guard cursor.current?.kind.isMappingValue == true else {
|
|
let mark = cursor.current?.mark ?? cursor.previous?.endMark ?? .start
|
|
try emitEmptyScalar(properties: .none, mark: mark)
|
|
return PureYAML.Parsing.NodeResult(kind: .scalar, endMark: mark)
|
|
}
|
|
let valueIndicator = try expect("mapping value") { kind in
|
|
if case .mappingValue = kind {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
let properties = try consumeProperties()
|
|
guard properties != .none || canBeginNode(cursor.current), cursor.current?.kind.isFlowTerminator == false else {
|
|
let mark = valueIndicator.endMark
|
|
try emitEmptyScalar(properties: properties, mark: mark)
|
|
return PureYAML.Parsing.NodeResult(kind: .scalar, endMark: mark)
|
|
}
|
|
return try parseNode(properties: properties)
|
|
}
|
|
|
|
/// True when the key position right after a `?` indicator is empty, i.e. the
|
|
/// next token is a `:` value indicator, a flow terminator, or the end of input,
|
|
/// so an empty scalar key must be composed (libyaml's `flow_mapping_key`).
|
|
func flowKeyIsEmpty() -> Bool {
|
|
guard let current = cursor.current else {
|
|
return true
|
|
}
|
|
return current.kind.isMappingValue || current.kind.isFlowTerminator
|
|
}
|
|
}
|