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.
71 lines
3.5 KiB
Swift
71 lines
3.5 KiB
Swift
extension PureYAML.Parsing.LibYAML.Parser {
|
|
/// The implied tag handles, mirroring libyaml's `default_tag_directives`.
|
|
private var defaultTagDirectives: [(handle: String, prefix: String)] {
|
|
[("!", "!"), ("!!", "tag:yaml.org,2002:")]
|
|
}
|
|
|
|
/// Port of `yaml_parser_process_directives`: consume the `%YAML` and `%TAG`
|
|
/// directive tokens at the head of a document, validating the version and
|
|
/// recording the tag handles, then install the default tag handles. The
|
|
/// version is checked against a local so a duplicate `%YAML` is rejected.
|
|
mutating func processDirectives() throws {
|
|
var version: (major: Int, minor: Int)?
|
|
var token = peekToken()
|
|
while true {
|
|
if case let .versionDirective(major, minor) = token.type {
|
|
try recordVersion(major: major, minor: minor, into: &version, mark: token.startMark)
|
|
} else if case let .tagDirective(handle, prefix) = token.type {
|
|
try appendTagDirective(handle: handle, prefix: prefix, allowDuplicates: false, mark: token.startMark)
|
|
} else if case .reservedDirective = token.type {
|
|
// A reserved directive is ignored (YAML 1.2 §6.8.1) but still
|
|
// consumed here so the explicit-document path requires a `---`.
|
|
} else {
|
|
break
|
|
}
|
|
skipToken()
|
|
token = peekToken()
|
|
}
|
|
for directive in defaultTagDirectives {
|
|
try appendTagDirective(handle: directive.handle, prefix: directive.prefix, allowDuplicates: true, mark: token.startMark)
|
|
}
|
|
}
|
|
|
|
private mutating func recordVersion(major: Int, minor: Int, into version: inout (major: Int, minor: Int)?, mark: Mark) throws {
|
|
guard version == nil else {
|
|
throw ParserError(context: nil, problem: "found duplicate %YAML directive", mark: mark)
|
|
}
|
|
// 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)
|
|
}
|
|
|
|
/// Port of `yaml_parser_append_tag_directive`: append a handle/prefix mapping,
|
|
/// rejecting a redefinition unless duplicates are allowed (the defaults are).
|
|
mutating func appendTagDirective(handle: String, prefix: String, allowDuplicates: Bool, mark: Mark) throws {
|
|
if tagDirectives.contains(where: { $0.handle == handle }) {
|
|
if allowDuplicates {
|
|
return
|
|
}
|
|
throw ParserError(context: nil, problem: "found duplicate %TAG directive", mark: mark)
|
|
}
|
|
tagDirectives.append((handle, prefix))
|
|
}
|
|
|
|
/// Port of the tag-resolution block in `yaml_parser_parse_node`: an empty
|
|
/// handle means the suffix is a verbatim tag; otherwise substitute the handle's
|
|
/// registered prefix, failing on an undefined handle.
|
|
func resolveTag(handle: String, suffix: String, startMark _: Mark, tagMark: Mark) throws -> String {
|
|
guard !handle.isEmpty else {
|
|
return suffix
|
|
}
|
|
guard let directive = tagDirectives.first(where: { $0.handle == handle }) else {
|
|
throw ParserError(context: "while parsing a node", problem: "found undefined tag handle", mark: tagMark)
|
|
}
|
|
return directive.prefix + suffix
|
|
}
|
|
}
|