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.
94 lines
3.6 KiB
Swift
94 lines
3.6 KiB
Swift
extension PureYAML.Parsing.LibYAML.Parser {
|
|
/// Port of `yaml_parser_parse_stream_start`.
|
|
mutating func parseStreamStart() throws -> Event {
|
|
let token = peekToken()
|
|
guard case .streamStart = token.type else {
|
|
throw ParserError(context: nil, problem: "did not find expected <stream-start>", mark: token.startMark)
|
|
}
|
|
state = .implicitDocumentStart
|
|
skipToken()
|
|
return .streamStart(mark: token.startMark)
|
|
}
|
|
|
|
/// Port of `yaml_parser_parse_document_start`, extended for YAML 1.2: stray
|
|
/// `...` markers are skipped, then a bare document is opened when one is allowed
|
|
/// (the first document or after an explicit `...`), an explicit document is
|
|
/// opened after directives and a `---`, or STREAM-END is emitted. libyaml only
|
|
/// allows a bare document as the very first one.
|
|
mutating func parseDocumentStart() throws -> Event {
|
|
var token = peekToken()
|
|
while case .documentEnd = token.type {
|
|
skipToken()
|
|
token = peekToken()
|
|
}
|
|
if allowBareDocument, !isDirectiveOrBoundary(token) {
|
|
try processDirectives()
|
|
states.append(.documentEnd)
|
|
state = .blockNode
|
|
return .documentStart(explicit: false, mark: token.startMark)
|
|
}
|
|
if case .streamEnd = token.type {
|
|
state = .end
|
|
skipToken()
|
|
return .streamEnd(mark: token.startMark)
|
|
}
|
|
return try openExplicitDocument(startMark: token.startMark)
|
|
}
|
|
|
|
/// The explicit-document branch: directives then a required `---`.
|
|
private mutating func openExplicitDocument(startMark: Mark) throws -> Event {
|
|
try processDirectives()
|
|
let token = peekToken()
|
|
guard case .documentStart = token.type else {
|
|
throw ParserError(context: nil, problem: "did not find expected <document start>", mark: token.startMark)
|
|
}
|
|
states.append(.documentEnd)
|
|
state = .documentContent
|
|
skipToken()
|
|
return .documentStart(explicit: true, mark: startMark)
|
|
}
|
|
|
|
/// Port of `yaml_parser_parse_document_content`: an empty scalar stands in for
|
|
/// a document with no node, otherwise parse the root node.
|
|
mutating func parseDocumentContent() throws -> Event {
|
|
let token = peekToken()
|
|
if isDirectiveOrBoundary(token) || isDocumentEnd(token) {
|
|
state = states.removeLast()
|
|
return processEmptyScalar(at: token.startMark)
|
|
}
|
|
return try parseNode(block: true, indentlessSequence: false)
|
|
}
|
|
|
|
/// Port of `yaml_parser_parse_document_end`: reset the per-document tag
|
|
/// directives and emit DOCUMENT-END, explicit only when `...` was present.
|
|
mutating func parseDocumentEnd() throws -> Event {
|
|
let token = peekToken()
|
|
var explicit = false
|
|
if case .documentEnd = token.type {
|
|
skipToken()
|
|
explicit = true
|
|
}
|
|
tagDirectives.removeAll()
|
|
// A bare document may follow only an explicit `...` end marker.
|
|
allowBareDocument = explicit
|
|
state = .documentStart
|
|
return .documentEnd(explicit: explicit, mark: token.startMark)
|
|
}
|
|
|
|
private func isDirectiveOrBoundary(_ token: Token) -> Bool {
|
|
switch token.type {
|
|
case .versionDirective, .tagDirective, .reservedDirective, .documentStart, .streamEnd:
|
|
true
|
|
default:
|
|
false
|
|
}
|
|
}
|
|
|
|
private func isDocumentEnd(_ token: Token) -> Bool {
|
|
if case .documentEnd = token.type {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
}
|