Stage 3 of the faithful libyaml scanner port, continued. Translates fetch_block_scalar, scan_block_scalar, and scan_block_scalar_breaks: literal (|) and folded (>) block scalars with the chomping (+/-) and indentation indicators in either order, indentation auto-detection from the first non-empty line (clamped to parser indent + 1), folded joining of equally-indented non-empty lines into a space, and clip/strip/keep chomping of the trailing breaks. Unit-tested for literal clip, folded join, and strip chomping. Only tags and directives remain unported in the scanner. 314 tests, no regression.
208 lines
6.3 KiB
Swift
208 lines
6.3 KiB
Swift
@testable import PureYAML
|
|
import Testing
|
|
|
|
/// End-to-end tokenization through the ported fetch loop. The headline case is
|
|
/// the original block-mapping bug: `- a:` followed by a same-column `b:` must
|
|
/// tokenize as siblings (one BLOCK-MAPPING-START), matching libyaml, while a
|
|
/// more-indented value nests (two BLOCK-MAPPING-STARTs).
|
|
@Suite("LibYAML Scanner Fetch")
|
|
struct LibYAMLScannerFetchTests {
|
|
private typealias TokenType = PureYAML.Parsing.LibYAML.TokenType
|
|
|
|
private func tokenize(_ input: String) throws -> [TokenType] {
|
|
var scanner = PureYAML.Parsing.LibYAML.Scanner(input)
|
|
return try scanner.scanAll().map(\.type)
|
|
}
|
|
|
|
@Test("a block mapping inside a sequence entry keeps same-column keys siblings")
|
|
func test_sequenceEntryMappingSiblings() throws {
|
|
// The original bug: `- a:` / ` b: c` is one mapping {a: null, b: c}.
|
|
let tokens = try tokenize("- a:\n b: c\n")
|
|
#expect(tokens == [
|
|
.streamStart,
|
|
.blockSequenceStart,
|
|
.blockEntry,
|
|
.blockMappingStart,
|
|
.key,
|
|
.scalar(value: "a", style: .plain),
|
|
.value,
|
|
.key,
|
|
.scalar(value: "b", style: .plain),
|
|
.value,
|
|
.scalar(value: "c", style: .plain),
|
|
.blockEnd,
|
|
.blockEnd,
|
|
.streamEnd,
|
|
])
|
|
}
|
|
|
|
@Test("a more-indented value nests a second mapping")
|
|
func test_moreIndentedValueNests() throws {
|
|
let tokens = try tokenize("a:\n b: c\n")
|
|
#expect(tokens == [
|
|
.streamStart,
|
|
.blockMappingStart,
|
|
.key,
|
|
.scalar(value: "a", style: .plain),
|
|
.value,
|
|
.blockMappingStart,
|
|
.key,
|
|
.scalar(value: "b", style: .plain),
|
|
.value,
|
|
.scalar(value: "c", style: .plain),
|
|
.blockEnd,
|
|
.blockEnd,
|
|
.streamEnd,
|
|
])
|
|
}
|
|
|
|
@Test("a simple block mapping pair")
|
|
func test_simplePair() throws {
|
|
let tokens = try tokenize("key: value\n")
|
|
#expect(tokens == [
|
|
.streamStart,
|
|
.blockMappingStart,
|
|
.key,
|
|
.scalar(value: "key", style: .plain),
|
|
.value,
|
|
.scalar(value: "value", style: .plain),
|
|
.blockEnd,
|
|
.streamEnd,
|
|
])
|
|
}
|
|
|
|
@Test("a multi-line plain scalar folds its line break to a space")
|
|
func test_plainScalarFolding() throws {
|
|
let tokens = try tokenize("a\nb\n")
|
|
#expect(tokens == [
|
|
.streamStart,
|
|
.scalar(value: "a b", style: .plain),
|
|
.streamEnd,
|
|
])
|
|
}
|
|
|
|
@Test("a flow sequence of plain scalars")
|
|
func test_flowSequence() throws {
|
|
let tokens = try tokenize("[a, b]\n")
|
|
#expect(tokens == [
|
|
.streamStart,
|
|
.flowSequenceStart,
|
|
.scalar(value: "a", style: .plain),
|
|
.flowEntry,
|
|
.scalar(value: "b", style: .plain),
|
|
.flowSequenceEnd,
|
|
.streamEnd,
|
|
])
|
|
}
|
|
|
|
@Test("a flow mapping with a key and value")
|
|
func test_flowMapping() throws {
|
|
let tokens = try tokenize("{k: v}\n")
|
|
#expect(tokens == [
|
|
.streamStart,
|
|
.flowMappingStart,
|
|
.key,
|
|
.scalar(value: "k", style: .plain),
|
|
.value,
|
|
.scalar(value: "v", style: .plain),
|
|
.flowMappingEnd,
|
|
.streamEnd,
|
|
])
|
|
}
|
|
|
|
@Test("single-quoted scalar with an escaped quote")
|
|
func test_singleQuoted() throws {
|
|
let tokens = try tokenize("'it''s here'\n")
|
|
#expect(tokens == [
|
|
.streamStart,
|
|
.scalar(value: "it's here", style: .singleQuoted),
|
|
.streamEnd,
|
|
])
|
|
}
|
|
|
|
@Test("double-quoted scalar decodes escapes")
|
|
func test_doubleQuotedEscapes() throws {
|
|
let tokens = try tokenize("\"a\\nb\\tc\\u263A\"\n")
|
|
#expect(tokens == [
|
|
.streamStart,
|
|
.scalar(value: "a\nb\tc\u{263A}", style: .doubleQuoted),
|
|
.streamEnd,
|
|
])
|
|
}
|
|
|
|
@Test("a multi-line quoted scalar folds line breaks")
|
|
func test_quotedFolding() throws {
|
|
let tokens = try tokenize("\"a\nb\"\n")
|
|
#expect(tokens == [
|
|
.streamStart,
|
|
.scalar(value: "a b", style: .doubleQuoted),
|
|
.streamEnd,
|
|
])
|
|
}
|
|
|
|
@Test("an anchored scalar value and an alias")
|
|
func test_anchorAndAlias() throws {
|
|
let tokens = try tokenize("a: &x 1\nb: *x\n")
|
|
#expect(tokens == [
|
|
.streamStart,
|
|
.blockMappingStart,
|
|
.key,
|
|
.scalar(value: "a", style: .plain),
|
|
.value,
|
|
.anchor(name: "x"),
|
|
.scalar(value: "1", style: .plain),
|
|
.key,
|
|
.scalar(value: "b", style: .plain),
|
|
.value,
|
|
.alias(name: "x"),
|
|
.blockEnd,
|
|
.streamEnd,
|
|
])
|
|
}
|
|
|
|
@Test("a literal block scalar keeps line breaks and clips the trailing newline")
|
|
func test_literalBlockScalar() throws {
|
|
let tokens = try tokenize("key: |\n a\n b\n")
|
|
#expect(tokens == [
|
|
.streamStart,
|
|
.blockMappingStart,
|
|
.key,
|
|
.scalar(value: "key", style: .plain),
|
|
.value,
|
|
.scalar(value: "a\nb\n", style: .literal),
|
|
.blockEnd,
|
|
.streamEnd,
|
|
])
|
|
}
|
|
|
|
@Test("a folded block scalar joins lines with a space")
|
|
func test_foldedBlockScalar() throws {
|
|
let tokens = try tokenize("key: >\n a\n b\n")
|
|
#expect(tokens == [
|
|
.streamStart,
|
|
.blockMappingStart,
|
|
.key,
|
|
.scalar(value: "key", style: .plain),
|
|
.value,
|
|
.scalar(value: "a b\n", style: .folded),
|
|
.blockEnd,
|
|
.streamEnd,
|
|
])
|
|
}
|
|
|
|
@Test("a strip chomping indicator drops the trailing newline")
|
|
func test_literalStripChomping() throws {
|
|
let tokens = try tokenize("key: |-\n a\n")
|
|
#expect(tokens == [
|
|
.streamStart,
|
|
.blockMappingStart,
|
|
.key,
|
|
.scalar(value: "key", style: .plain),
|
|
.value,
|
|
.scalar(value: "a", style: .literal),
|
|
.blockEnd,
|
|
.streamEnd,
|
|
])
|
|
}
|
|
}
|