A mapping key is a full node: it may carry &anchor and !tag properties and may itself be an *alias. Fixes: (1) anchor/alias name scanning now stops at the first non-name character per yaml_parser_scan_anchor, so *a: reads as alias 'a' plus a mapping indicator instead of name 'a:'; (2) the block-mapping parser enters on a leading node property or alias, attaches same-line key properties to the key node, and emits alias keys directly; (3) an anchor or tag at value position with no same-line node composes an empty (null) value carrying the property, so 'a: &anchor' registers the anchor; (4) the composer registers an anchor's resolved model value (an empty anchored scalar registers null); (5) implicit keys with trailing spaces before the colon are recognized. Flips 10 official YAML test suite cases to passing (280/402), including G9HC now correctly rejected. 2SXE no longer false-rejects but still value-mismatches on compact-mapping interpretation (a separate gap), so it is reclassified in the manifest rather than removed. Updates the manifest, fixtures, counts, and roadmap.
136 lines
5.4 KiB
Swift
136 lines
5.4 KiB
Swift
extension PureYAML.Parsing.TokenEventParser {
|
|
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 parseNode().endMark
|
|
if cursor.current?.kind.isFlowTerminator == false {
|
|
throw unexpectedToken(expected: "flow entry or flow sequence end")
|
|
}
|
|
}
|
|
throw unexpectedToken(expected: "flow sequence end")
|
|
}
|
|
|
|
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")
|
|
}
|
|
|
|
mutating func parseFlowMappingPair() throws -> PureYAML.Parsing.NodeResult {
|
|
if cursor.current?.kind.isMappingKey == true {
|
|
return try parseMappingPair()
|
|
}
|
|
|
|
try parseFlowMappingPairKey()
|
|
|
|
guard cursor.current?.kind.isMappingValue == true else {
|
|
return PureYAML.Parsing.NodeResult(kind: .scalar, endMark: cursor.previous?.endMark ?? .start)
|
|
}
|
|
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)
|
|
}
|
|
|
|
/// 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 parseFlowMappingPairKey() 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)
|
|
}
|
|
}
|