The double-quoted escape decoder recognized only a subset of escapes
(" \ \/ \b \f \n \r \t \u) and silently passed any other character
through. That accepted invalid escapes such as \. and \', and mangled
valid ones the subset omitted: \x and \U hex escapes, \v \e \0, the named
\N \_ \L \P, and a backslash before a space, were emitted as their literal
letters (\x0d became the text x0d).
The decoder now maps every escape in the YAML 1.2 table to its character,
reads \x (8-bit) and \U (32-bit) hex escapes alongside \u, treats a
backslash before a line break as an escaped line break that joins the
lines with no separator, and rejects an unknown or truncated escape with
a diagnostic. Single-character escapes move to a lookup table so the
decoder stays within the complexity limit.
Measured against the official YAML test-suite, parse-level conformance
rises from 247/333 (74.2%) to 249/333 (74.8%) as two invalid documents
are now rejected, and value-level from 166 to 170 of comparable cases
(90.4%) as the previously-mangled escapes decode correctly, with no
regression.
51 lines
1.7 KiB
Swift
51 lines
1.7 KiB
Swift
@testable import PureYAML
|
|
import Testing
|
|
|
|
@Suite("Double-quoted escape sequences")
|
|
struct DoubleQuotedEscapeTests {
|
|
@Test("Every YAML 1.2 escape decodes to its character", arguments: [
|
|
("\"\\0\"\n", "\u{00}"),
|
|
("\"\\a\"\n", "\u{07}"),
|
|
("\"\\b\"\n", "\u{08}"),
|
|
("\"\\t\"\n", "\t"),
|
|
("\"\\n\"\n", "\n"),
|
|
("\"\\v\"\n", "\u{0B}"),
|
|
("\"\\f\"\n", "\u{0C}"),
|
|
("\"\\r\"\n", "\r"),
|
|
("\"\\e\"\n", "\u{1B}"),
|
|
("\"a\\ b\"\n", "a b"),
|
|
("\"\\\"\"\n", "\""),
|
|
("\"\\/\"\n", "/"),
|
|
("\"\\\\\"\n", "\\"),
|
|
("\"\\N\"\n", "\u{85}"),
|
|
("\"\\_\"\n", "\u{A0}"),
|
|
("\"\\L\"\n", "\u{2028}"),
|
|
("\"\\P\"\n", "\u{2029}"),
|
|
("\"\\x41\"\n", "A"),
|
|
("\"\\u0041\"\n", "A"),
|
|
("\"\\U0001F600\"\n", "\u{1F600}"),
|
|
])
|
|
func test_validEscapeDecodes(_ yaml: String, _ expected: String) throws {
|
|
#expect(try PureYAML.parse(yaml) == .string(expected))
|
|
}
|
|
|
|
@Test("An unknown or truncated escape is rejected", arguments: [
|
|
"\"\\.\"\n", // not an escape character
|
|
"\"\\'\"\n", // single quote is not escaped in a double-quoted scalar
|
|
"\"\\q\"\n", // unknown letter
|
|
"\"\\x4\"\n", // truncated hex
|
|
"\"\\U12\"\n", // truncated 32-bit hex
|
|
])
|
|
func test_invalidEscapeIsRejected(_ yaml: String) throws {
|
|
#expect(throws: PureYAML.Parsing.ParseError.self) {
|
|
try PureYAML.parse(yaml)
|
|
}
|
|
}
|
|
|
|
@Test("An escaped line break joins the lines with no separator")
|
|
func test_escapedLineBreakJoinsLines() throws {
|
|
#expect(try PureYAML.parse("\"a\\\n b\"\n") == .string("ab"))
|
|
#expect(try PureYAML.parse("\"one \\\n two\"\n") == .string("one two"))
|
|
}
|
|
}
|