The scalar event now carries the scanner token's end mark, so a located scalar's range spans the whole token (end exclusive, one past the last character) instead of being start-anchored. This gives consumers exact highlight widths and is the prerequisite for surgical, position-based text edits. Only the scalar event case gained a field; the value tree, its equality, and the event dump (which compares value and style, not marks) are unchanged, so the canonical-event and whole-suite gates stay green (343 tests).
159 lines
5.1 KiB
Swift
159 lines
5.1 KiB
Swift
@testable import PureYAML
|
|
import Testing
|
|
|
|
/// End-to-end event production through the ported parser state machine. Events are
|
|
/// rendered to the YAML test-suite "yeast" format (mark-independent) so the
|
|
/// expectations read like the canonical `test.event` files.
|
|
@Suite("LibYAML Parser")
|
|
struct LibYAMLParserTests {
|
|
private func events(_ input: String) throws -> [String] {
|
|
var parser = try PureYAML.Parsing.LibYAML.Parser(input)
|
|
return try parser.parse().map(render)
|
|
}
|
|
|
|
private func render(_ event: PureYAML.Parsing.Event) -> String {
|
|
switch event {
|
|
case .streamStart:
|
|
"+STR"
|
|
case .streamEnd:
|
|
"-STR"
|
|
case let .documentStart(explicit, _):
|
|
explicit ? "+DOC ---" : "+DOC"
|
|
case let .documentEnd(explicit, _):
|
|
explicit ? "-DOC ..." : "-DOC"
|
|
case let .sequenceStart(anchor, tag, style, _):
|
|
"+SEQ" + (style == .flow ? " []" : "") + properties(anchor, tag)
|
|
case .sequenceEnd:
|
|
"-SEQ"
|
|
case let .mappingStart(anchor, tag, style, _):
|
|
"+MAP" + (style == .flow ? " {}" : "") + properties(anchor, tag)
|
|
case .mappingEnd:
|
|
"-MAP"
|
|
case let .scalar(value, anchor, tag, style, _, _):
|
|
"=VAL" + properties(anchor, tag) + " " + styleChar(style) + escape(value)
|
|
case let .alias(anchor, _):
|
|
"=ALI *" + anchor
|
|
}
|
|
}
|
|
|
|
private func properties(_ anchor: String?, _ tag: String?) -> String {
|
|
(anchor.map { " &" + $0 } ?? "") + (tag.map { " <" + $0 + ">" } ?? "")
|
|
}
|
|
|
|
private func styleChar(_ style: PureYAML.Parsing.ScalarStyle) -> String {
|
|
switch style {
|
|
case .plain: ":"
|
|
case .singleQuoted: "'"
|
|
case .doubleQuoted: "\""
|
|
case .literal: "|"
|
|
case .folded: ">"
|
|
}
|
|
}
|
|
|
|
private func escape(_ value: String) -> String {
|
|
var output = ""
|
|
for scalar in value.unicodeScalars {
|
|
switch scalar {
|
|
case "\\": output += "\\\\"
|
|
case "\n": output += "\\n"
|
|
case "\t": output += "\\t"
|
|
case "\r": output += "\\r"
|
|
default: output.unicodeScalars.append(scalar)
|
|
}
|
|
}
|
|
return output
|
|
}
|
|
|
|
@Test("a bare root scalar")
|
|
func test_rootScalar() throws {
|
|
#expect(try events("x\n") == ["+STR", "+DOC", "=VAL :x", "-DOC", "-STR"])
|
|
}
|
|
|
|
@Test("a block sequence")
|
|
func test_blockSequence() throws {
|
|
#expect(try events("- a\n- b\n") == [
|
|
"+STR", "+DOC", "+SEQ", "=VAL :a", "=VAL :b", "-SEQ", "-DOC", "-STR",
|
|
])
|
|
}
|
|
|
|
@Test("a block mapping")
|
|
func test_blockMapping() throws {
|
|
#expect(try events("a: b\n") == [
|
|
"+STR", "+DOC", "+MAP", "=VAL :a", "=VAL :b", "-MAP", "-DOC", "-STR",
|
|
])
|
|
}
|
|
|
|
@Test("a mapping nested in a sequence entry with an empty value")
|
|
func test_nestedMappingEmptyValue() throws {
|
|
// The original block-mapping bug: `- a:` / ` b: c` is {a: null, b: c}.
|
|
#expect(try events("- a:\n b: c\n") == [
|
|
"+STR", "+DOC", "+SEQ", "+MAP",
|
|
"=VAL :a", "=VAL :",
|
|
"=VAL :b", "=VAL :c",
|
|
"-MAP", "-SEQ", "-DOC", "-STR",
|
|
])
|
|
}
|
|
|
|
@Test("a flow sequence")
|
|
func test_flowSequence() throws {
|
|
#expect(try events("[a, b]\n") == [
|
|
"+STR", "+DOC", "+SEQ []", "=VAL :a", "=VAL :b", "-SEQ", "-DOC", "-STR",
|
|
])
|
|
}
|
|
|
|
@Test("a flow mapping")
|
|
func test_flowMapping() throws {
|
|
#expect(try events("{a: b}\n") == [
|
|
"+STR", "+DOC", "+MAP {}", "=VAL :a", "=VAL :b", "-MAP", "-DOC", "-STR",
|
|
])
|
|
}
|
|
|
|
@Test("an anchor and an alias")
|
|
func test_anchorAlias() throws {
|
|
#expect(try events("a: &x 1\nb: *x\n") == [
|
|
"+STR", "+DOC", "+MAP",
|
|
"=VAL :a", "=VAL &x :1",
|
|
"=VAL :b", "=ALI *x",
|
|
"-MAP", "-DOC", "-STR",
|
|
])
|
|
}
|
|
|
|
@Test("a shorthand tag resolves against the default !! handle")
|
|
func test_shorthandTag() throws {
|
|
#expect(try events("!!str x\n") == [
|
|
"+STR", "+DOC", "=VAL <tag:yaml.org,2002:str> :x", "-DOC", "-STR",
|
|
])
|
|
}
|
|
|
|
@Test("a custom %TAG directive resolves a shorthand")
|
|
func test_tagDirective() throws {
|
|
#expect(try events("%TAG !e! tag:example.com,2000:\n---\n!e!foo x\n") == [
|
|
"+STR", "+DOC ---", "=VAL <tag:example.com,2000:foo> :x", "-DOC", "-STR",
|
|
])
|
|
}
|
|
|
|
@Test("a version directive opens an explicit document")
|
|
func test_versionDirective() throws {
|
|
#expect(try events("%YAML 1.2\n---\nx\n") == [
|
|
"+STR", "+DOC ---", "=VAL :x", "-DOC", "-STR",
|
|
])
|
|
}
|
|
|
|
@Test("multiple explicit documents")
|
|
func test_multipleDocuments() throws {
|
|
#expect(try events("---\na\n---\nb\n") == [
|
|
"+STR",
|
|
"+DOC ---", "=VAL :a", "-DOC",
|
|
"+DOC ---", "=VAL :b", "-DOC",
|
|
"-STR",
|
|
])
|
|
}
|
|
|
|
@Test("a quoted scalar is not plain-implicit")
|
|
func test_quotedScalar() throws {
|
|
#expect(try events("\"x\"\n") == [
|
|
"+STR", "+DOC", "=VAL \"x", "-DOC", "-STR",
|
|
])
|
|
}
|
|
}
|