Ports parser.c: the one-event-at-a-time state machine that turns the scanner's token stream into the YAML event stream. Covers stream and document framing, directive processing with %TAG handle resolution and default handles, the node property/tag/anchor production, block and indentless sequences, block mappings, and flow sequences and mappings including the single-pair `?` mapping forms. Output verified against the libyaml reference event dumper: the empty mapping value, %TAG resolution, and shorthand tags all match byte for byte. 331 tests, no regression.
92 lines
3.4 KiB
Swift
92 lines
3.4 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`. Skips stray document-end markers
|
|
/// before an explicit document, opens an implicit document on a bare node,
|
|
/// opens an explicit document after directives and a `---`, or emits STREAM-END.
|
|
mutating func parseDocumentStart(implicit: Bool) throws -> Event {
|
|
var token = peekToken()
|
|
if !implicit {
|
|
while case .documentEnd = token.type {
|
|
skipToken()
|
|
token = peekToken()
|
|
}
|
|
}
|
|
if implicit, !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()
|
|
state = .documentStart
|
|
return .documentEnd(explicit: explicit, mark: token.startMark)
|
|
}
|
|
|
|
private func isDirectiveOrBoundary(_ token: Token) -> Bool {
|
|
switch token.type {
|
|
case .versionDirective, .tagDirective, .documentStart, .streamEnd:
|
|
true
|
|
default:
|
|
false
|
|
}
|
|
}
|
|
|
|
private func isDocumentEnd(_ token: Token) -> Bool {
|
|
if case .documentEnd = token.type {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
}
|