Routes the value and stream APIs through the ported scanner+parser (the event API already was), so PureYAML now parses entirely on the libyaml port. Adds the features needed to match and exceed the previous engine: JSON UTF-16 surrogate-pair \u escapes, bare documents after an explicit `...`, reserved-directive handling that still requires a following `---`, and a 64-level nesting bound enforced on the event stream so deeply nested input fails with nestingTooDeep instead of overflowing a recursive consumer. Parser errors are translated to the diagnostic-aware ParseError, recovering precise codes where identifiable and a readable malformed fallback otherwise; error-reporting fixtures are rebased onto the port. Canonical event conformance: 268 -> 271 of 308 (7Z25, HWV9, QT73). 393 tests pass.
154 lines
5.9 KiB
Swift
154 lines
5.9 KiB
Swift
@testable import PureYAML
|
|
import Testing
|
|
|
|
@Suite("Complex Mapping Key Parsing")
|
|
struct ComplexMappingKeyParsingTests {
|
|
@Test("Parses block sequence keys as first-class key nodes")
|
|
func test_parsesBlockSequenceKeysAsFirstClassKeyNodes() throws {
|
|
let value = try PureYAML.parse("""
|
|
? [Detroit Tigers, Chicago Cubs]
|
|
:
|
|
- 2001-07-23
|
|
""")
|
|
let expectedKey = PureYAML.Model.Key.sequence([
|
|
.string("Detroit Tigers"),
|
|
.string("Chicago Cubs"),
|
|
])
|
|
|
|
#expect(value == .mapping(.init([
|
|
.init(
|
|
keyNode: expectedKey,
|
|
value: .sequence([.string("2001-07-23")]),
|
|
),
|
|
])))
|
|
#expect(value.rootMapping?.pairs.first?.keyNode == expectedKey)
|
|
#expect(value.rootMapping?.pairs.first?.key == "[\"Detroit Tigers\", \"Chicago Cubs\"]")
|
|
#expect(value.rootMapping?["Detroit Tigers"] == nil)
|
|
#expect(value.rootMapping?[expectedKey] == .sequence([.string("2001-07-23")]))
|
|
}
|
|
|
|
@Test("Parses block mapping keys as first-class key nodes")
|
|
func test_parsesBlockMappingKeysAsFirstClassKeyNodes() throws {
|
|
let value = try PureYAML.parse("""
|
|
? {name: Example, version: 1}
|
|
: active
|
|
""")
|
|
let expectedKey = PureYAML.Model.Key.mapping(.init([
|
|
.init(key: "name", value: .string("Example")),
|
|
.init(key: "version", value: .int(1)),
|
|
]))
|
|
|
|
#expect(value == .mapping(.init([
|
|
.init(keyNode: expectedKey, value: .string("active")),
|
|
])))
|
|
#expect(value.rootMapping?.pairs.first?.keyNode.description == """
|
|
{"name": "Example", "version": 1}
|
|
""")
|
|
#expect(value.rootMapping?["name"] == nil)
|
|
#expect(value.rootMapping?[expectedKey] == .string("active"))
|
|
}
|
|
|
|
@Test("Keeps scalar mapping keys string-keyed even when tags look typed")
|
|
func test_keepsScalarMappingKeysStringKeyedEvenWhenTagsLookTyped() throws {
|
|
let value = try PureYAML.parse("""
|
|
? !!int 1
|
|
: integer-looking
|
|
? true
|
|
: boolean-looking
|
|
""")
|
|
|
|
#expect(value == .mapping(.init([
|
|
.init(key: "1", value: .string("integer-looking")),
|
|
.init(key: "true", value: .string("boolean-looking")),
|
|
])))
|
|
#expect(value.rootMapping?["1"] == .string("integer-looking"))
|
|
#expect(value.rootMapping?["true"] == .string("boolean-looking"))
|
|
#expect(value.rootMapping?[.sequence([.string("1")])] == nil)
|
|
}
|
|
|
|
@Test("Parses alias-backed complex keys without string coercion")
|
|
func test_parsesAliasBackedComplexKeysWithoutStringCoercion() throws {
|
|
let value = try PureYAML.parse("""
|
|
pair: &pair [a, b]
|
|
? *pair
|
|
: value
|
|
""")
|
|
let key = PureYAML.Model.Key.sequence([.string("a"), .string("b")])
|
|
|
|
#expect(value == .mapping(.init([
|
|
.init(key: "pair", value: .sequence([.string("a"), .string("b")])),
|
|
.init(keyNode: key, value: .string("value")),
|
|
])))
|
|
#expect(value.rootMapping?["pair"] == .sequence([.string("a"), .string("b")]))
|
|
#expect(value.rootMapping?[key] == .string("value"))
|
|
#expect(value.rootMapping?["[\"a\", \"b\"]"] == nil)
|
|
}
|
|
|
|
@Test("Parses explicit scalar keys with same-line mapping values")
|
|
func test_parsesExplicitScalarKeysWithSameLineMappingValues() throws {
|
|
let value = try PureYAML.parse("""
|
|
? __stringMin14PatternS3Captions
|
|
: minLength: 14
|
|
pattern: ^((s3://(.*?)\\.(srt|SRT))|(https?://(.*?)\\.(srt|SRT)))$
|
|
type: string
|
|
next: done
|
|
""")
|
|
let key = PureYAML.Model.Key.string("__stringMin14PatternS3Captions")
|
|
|
|
#expect(value == .mapping(.init([
|
|
.init(keyNode: key, value: .mapping(.init([
|
|
.init(key: "minLength", value: .int(14)),
|
|
.init(
|
|
key: "pattern",
|
|
value: .string("^((s3://(.*?)\\.(srt|SRT))|(https?://(.*?)\\.(srt|SRT)))$"),
|
|
),
|
|
.init(key: "type", value: .string("string")),
|
|
]))),
|
|
.init(key: "next", value: .string("done")),
|
|
])))
|
|
#expect(value.rootMapping?[key]?.rootMapping?["minLength"] == .int(14))
|
|
#expect(value.rootMapping?[key]?.rootMapping?["type"] == .string("string"))
|
|
#expect(value.rootMapping?["next"] == .string("done"))
|
|
#expect(value.rootMapping?["minLength"] == nil)
|
|
}
|
|
|
|
@Test("Expands merged mappings with complex-key local overrides")
|
|
func test_expandsMergedMappingsWithComplexKeyLocalOverrides() throws {
|
|
let value = try PureYAML.parse("""
|
|
defaults: &defaults
|
|
? [a, b]
|
|
: inherited
|
|
name: inherited-name
|
|
service:
|
|
<<: *defaults
|
|
? [a, b]
|
|
: local
|
|
""")
|
|
let service = value.rootMapping?["service"]
|
|
let key = PureYAML.Model.Key.sequence([.string("a"), .string("b")])
|
|
|
|
#expect(service == .mapping(.init([
|
|
.init(key: "name", value: .string("inherited-name")),
|
|
.init(keyNode: key, value: .string("local")),
|
|
])))
|
|
#expect(try PureYAML.validate(value, strict: false).isEmpty)
|
|
#expect(service?.rootMapping?[key] == .string("local"))
|
|
#expect(service?.rootMapping?["[\"a\", \"b\"]"] == nil)
|
|
}
|
|
|
|
@Test("Rejects a sequence dangling after a complex key mapping")
|
|
func test_rejectsSequenceDanglingAfterComplexKeyMapping() {
|
|
// `? [a, b]` is a valid explicit key with an implicit empty (null) value,
|
|
// so the mapping closes cleanly. The same-column `- invalid` then dangles
|
|
// outside any node and is rejected at the document boundary.
|
|
expectParseError("""
|
|
? [a, b]
|
|
- invalid
|
|
""", .malformed(
|
|
reason: "while parsing a block mapping, did not find expected key",
|
|
line: 1,
|
|
column: 1,
|
|
))
|
|
}
|
|
}
|