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 ", 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() } // Directives apply to the document that follows, so the previous document // must be closed with an explicit `...` end marker before them. After an // implicit end (`allowBareDocument` is false) a directive is an error. if isDirective(token), !allowBareDocument { throw ParserError( context: nil, problem: "directives require a preceding document end marker", mark: token.startMark, ) } 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 ", 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 isDirective(_ token: Token) -> Bool { switch token.type { case .versionDirective, .tagDirective, .reservedDirective: true default: false } } private func isDocumentEnd(_ token: Token) -> Bool { if case .documentEnd = token.type { return true } return false } }