feat(parsing): surpass libyaml on reserved directives and YAML versions
YAML 1.2 §6.8.1 requires a reserved directive (anything other than %YAML or %TAG) to be ignored with a warning, and a document whose minor version is newer than 1.2 to be parsed with a warning. libyaml rejects both. The scanner now drops a reserved directive's whole line and emits no token, and the parser accepts any major-version-1 document. Canonical event conformance: 253 -> 258 of 308 (2LFX, 6LVF, MUS6/05, MUS6/06, BEC7). 333 tests pass.
This commit is contained in:
parent
561921df42
commit
6ce1f906f9
@ -31,7 +31,10 @@ extension PureYAML.Parsing.LibYAML.Parser {
|
||||
guard version == nil else {
|
||||
throw ParserError(context: nil, problem: "found duplicate %YAML directive", mark: mark)
|
||||
}
|
||||
guard major == 1, minor == 1 || minor == 2 else {
|
||||
// YAML 1.2 §6.8.1: a document whose major version is 1 is parsed even when
|
||||
// the minor version is newer than 1.2 (with a warning), rather than
|
||||
// libyaml's hard rejection of any minor other than 1 or 2.
|
||||
guard major == 1 else {
|
||||
throw ParserError(context: nil, problem: "found incompatible YAML document", mark: mark)
|
||||
}
|
||||
version = (major, minor)
|
||||
|
||||
@ -14,29 +14,44 @@ extension PureYAML.Parsing.LibYAML.Scanner {
|
||||
indentEngine.unrollIndent(column: -1, mark: reader.mark)
|
||||
try removeSimpleKey()
|
||||
simpleKeyAllowed = false
|
||||
let token = try scanDirective()
|
||||
indentEngine.enqueue(token)
|
||||
if let token = try scanDirective() {
|
||||
indentEngine.enqueue(token)
|
||||
}
|
||||
}
|
||||
|
||||
/// Port of `yaml_parser_scan_directive`. Eats the `%`, reads the directive name,
|
||||
/// dispatches to the `%YAML` version value or the `%TAG` handle/prefix value,
|
||||
/// then consumes the rest of the line including any comment.
|
||||
private mutating func scanDirective() throws -> Token {
|
||||
/// Port of `yaml_parser_scan_directive`, extended to YAML 1.2 §6.8.1: a `%YAML`
|
||||
/// or `%TAG` directive produces its token; any other (reserved) directive is
|
||||
/// ignored with a warning, producing no token, rather than libyaml's hard
|
||||
/// rejection. Returns nil for the ignored case.
|
||||
private mutating func scanDirective() throws -> Token? {
|
||||
let startMark = reader.mark
|
||||
reader.skip()
|
||||
let name = try scanDirectiveName(startMark: startMark)
|
||||
let token: Token
|
||||
if name == "YAML" {
|
||||
let version = try scanVersionDirectiveValue(startMark: startMark)
|
||||
token = Token(type: .versionDirective(major: version.major, minor: version.minor), startMark: startMark, endMark: reader.mark)
|
||||
} else if name == "TAG" {
|
||||
let value = try scanTagDirectiveValue(startMark: startMark)
|
||||
token = Token(type: .tagDirective(handle: value.handle, prefix: value.prefix), startMark: startMark, endMark: reader.mark)
|
||||
} else {
|
||||
throw directiveError("while scanning a directive", "found unknown directive name", startMark)
|
||||
let token = Token(type: .versionDirective(major: version.major, minor: version.minor), startMark: startMark, endMark: reader.mark)
|
||||
try consumeDirectiveTail(startMark: startMark)
|
||||
return token
|
||||
}
|
||||
if name == "TAG" {
|
||||
let value = try scanTagDirectiveValue(startMark: startMark)
|
||||
let token = Token(type: .tagDirective(handle: value.handle, prefix: value.prefix), startMark: startMark, endMark: reader.mark)
|
||||
try consumeDirectiveTail(startMark: startMark)
|
||||
return token
|
||||
}
|
||||
skipReservedDirectiveLine()
|
||||
return nil
|
||||
}
|
||||
|
||||
/// Consume a reserved directive's parameters through the end of the line; the
|
||||
/// whole line is ignored per YAML 1.2 §6.8.1.
|
||||
private mutating func skipReservedDirectiveLine() {
|
||||
while !Classify.isBreakZ(reader.peek()) {
|
||||
reader.skip()
|
||||
}
|
||||
if Classify.isBreak(reader.peek()) {
|
||||
reader.skipLine()
|
||||
}
|
||||
try consumeDirectiveTail(startMark: startMark)
|
||||
return token
|
||||
}
|
||||
|
||||
/// Eat trailing blanks and an optional comment, require a line break, eat it.
|
||||
|
||||
@ -21,11 +21,11 @@ struct CanonicalEventConformanceTests {
|
||||
// itself handles.
|
||||
//
|
||||
// false-rejects (parseEvents throws on valid input, as libyaml does)
|
||||
"2JQS", "2LFX", "2SXE", "4ABK", "4MUZ/00", "4MUZ/01", "4MUZ/02", "58MP",
|
||||
"5MUD", "5T43", "6BCT", "6CA3", "6LVF", "6M2F", "7Z25", "8XYN", "96NN/00",
|
||||
"96NN/01", "9SA2", "A2M4", "BEC7", "CFD4", "DBG4", "DK3J", "DK95/00",
|
||||
"2JQS", "2SXE", "4ABK", "4MUZ/00", "4MUZ/01", "4MUZ/02", "58MP",
|
||||
"5MUD", "5T43", "6BCT", "6CA3", "6M2F", "7Z25", "8XYN", "96NN/00",
|
||||
"96NN/01", "9SA2", "A2M4", "CFD4", "DBG4", "DK3J", "DK95/00",
|
||||
"DK95/03", "DK95/04", "FP8R", "FRK4", "HM87/00", "HWV9", "K3WX", "M2N8/00",
|
||||
"M7A3", "MUS6/05", "MUS6/06", "NHX8", "NJ66", "NKF9", "Q5MG", "QT73",
|
||||
"M7A3", "NHX8", "NJ66", "NKF9", "Q5MG", "QT73",
|
||||
"R4YG", "S3PD", "SM9W/01", "UKK6/00", "UT92", "VJP3/01", "W4TN", "W5VH",
|
||||
"Y79Y/001", "Y79Y/010",
|
||||
// wrong event stream: the port matches libyaml but both deviate from the
|
||||
|
||||
Loading…
Reference in New Issue
Block a user