PureYAML/Sources/Parsing/TokenEventParser.swift

148 lines
5.4 KiB
Swift

extension PureYAML.Parsing {
struct TokenEventParser {
var cursor: TokenCursor
let scalarParser: Parser
var events: [Event]
init(
tokens: [Token],
scalarParser: Parser,
) {
cursor = TokenCursor(tokens: tokens)
self.scalarParser = scalarParser
events = []
}
mutating func parse() throws -> [Event] {
let streamStart = try expect("stream start") { kind in
if case .streamStart = kind {
return true
}
return false
}
events.append(.streamStart(mark: streamStart.mark))
var didParseDocument = false
while cursor.current?.kind.isStreamEnd != true {
try parseDocument()
didParseDocument = true
}
guard didParseDocument else {
throw ParseError.emptyDocument
}
let streamEnd = try expect("stream end") { kind in
if case .streamEnd = kind {
return true
}
return false
}
events.append(.streamEnd(mark: streamEnd.mark))
return events
}
}
}
extension PureYAML.Parsing.TokenEventParser {
mutating func parseDocument() throws {
let startMark = parseDocumentStartMark()
events.append(.documentStart(mark: startMark))
if cursor.current?.kind.isDocumentEnd == true {
let end = try expect("document end") { $0.isDocumentEnd }
appendEmptyDocument(at: end.mark)
events.append(.documentEnd(mark: end.endMark))
return
}
if cursor.current?.kind.isDocumentStart == true || cursor.current?.kind.isStreamEnd == true {
appendEmptyDocument(at: cursor.current?.mark ?? startMark)
events.append(.documentEnd(mark: cursor.current?.mark ?? startMark))
return
}
let result = try parseNode()
var endMark = result.endMark
if cursor.current?.kind.isDocumentEnd == true {
let end = try expect("document end") { $0.isDocumentEnd }
endMark = end.endMark
}
if let current = cursor.current, !canEndDocument(at: current) {
throw PureYAML.Parsing.ParseError.unexpectedToken(
expected: "document boundary or stream end",
actual: current.kind.description,
line: current.mark.line,
column: current.mark.column,
)
}
events.append(.documentEnd(mark: endMark))
}
mutating func parseDocumentStartMark() -> PureYAML.Parsing.Mark {
guard cursor.current?.kind.isDocumentStart == true else {
return cursor.current?.mark ?? cursor.previous?.endMark ?? .start
}
let start = cursor.advance()
return start?.mark ?? .start
}
mutating func appendEmptyDocument(at mark: PureYAML.Parsing.Mark) {
events.append(.scalar(value: "", anchor: nil, tag: nil, style: .plain, mark: mark))
}
func canEndDocument(at token: PureYAML.Parsing.Token) -> Bool {
token.kind.isDocumentStart || token.kind.isStreamEnd
}
mutating func parseNode() throws -> PureYAML.Parsing.NodeResult {
let properties = consumeProperties()
return try parseNode(properties: properties)
}
mutating func parseNode(
properties initialProperties: PureYAML.Parsing.NodeProperties,
) throws -> PureYAML.Parsing.NodeResult {
var properties = initialProperties
properties.mergeUnset(from: consumeProperties())
guard let token = cursor.current, !token.kind.isTerminator else {
let mark = properties.mark ?? cursor.current?.mark ?? cursor.previous?.endMark ?? .start
throw PureYAML.Parsing.ParseError.expectedNode(line: mark.line, column: mark.column)
}
switch token.kind {
case let .alias(anchor):
_ = cursor.advance()
let mark = properties.mark ?? token.mark
events.append(.alias(anchor: anchor, mark: mark))
return PureYAML.Parsing.NodeResult(kind: .alias, endMark: token.endMark)
case .blockEntry:
return try parseBlockSequence(properties: properties)
case .blockScalarHeader:
return try parseBlockScalar(properties: properties)
case .flowMappingStart:
return try parseFlowMapping(properties: properties)
case .flowSequenceStart:
return try parseFlowSequence(properties: properties)
case .mappingKey:
return try parseBlockMapping(
properties: properties,
includeIndentedContinuation: false,
)
case let .scalar(value, style):
_ = cursor.advance()
let decoded = try decodeScalar(value, style: style, line: token.mark.line)
let mark = properties.mark ?? token.mark
events.append(.scalar(
value: decoded,
anchor: properties.anchor,
tag: properties.tag,
style: style,
mark: mark,
))
return PureYAML.Parsing.NodeResult(kind: .scalar, endMark: token.endMark)
default:
throw PureYAML.Parsing.ParseError.expectedNode(line: token.mark.line, column: token.mark.column)
}
}
}