240 lines
7.5 KiB
Swift
240 lines
7.5 KiB
Swift
extension PureYAML.Parsing {
|
|
struct EventComposer {
|
|
var events: [Event]
|
|
var index: Int
|
|
var anchors: [String: PureYAML.Model.Value]
|
|
let scalarParser: Parser
|
|
|
|
init(
|
|
events: [Event],
|
|
scalarParser: Parser,
|
|
) {
|
|
self.events = events
|
|
index = 0
|
|
anchors = [:]
|
|
self.scalarParser = scalarParser
|
|
}
|
|
|
|
mutating func compose() throws -> PureYAML.Model.Value {
|
|
_ = try expect("stream start") { event in
|
|
if case .streamStart = event {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
_ = try expect("document start") { event in
|
|
if case .documentStart = event {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
let value = try composeNode()
|
|
_ = try expect("document end") { event in
|
|
if case .documentEnd = event {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
if case let .documentStart(mark)? = current {
|
|
throw PureYAML.Parsing.ParseError.unsupportedMultiDocumentStream(line: mark.line)
|
|
}
|
|
_ = try expectStreamEnd()
|
|
return value
|
|
}
|
|
|
|
mutating func composeStream() throws -> [PureYAML.Stream.Document] {
|
|
_ = try expect("stream start") { event in
|
|
if case .streamStart = event {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
var documents: [PureYAML.Stream.Document] = []
|
|
while current?.isStreamEnd != true {
|
|
_ = try expect("document start") { event in
|
|
if case .documentStart = event {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
anchors = [:]
|
|
let value = try composeNode()
|
|
_ = try expect("document end") { event in
|
|
if case .documentEnd = event {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
documents.append(.init(index: documents.count, value: value))
|
|
}
|
|
|
|
_ = try expectStreamEnd()
|
|
return documents
|
|
}
|
|
}
|
|
}
|
|
|
|
extension PureYAML.Parsing.EventComposer {
|
|
mutating func composeNode() throws -> PureYAML.Model.Value {
|
|
guard let event = current else {
|
|
throw unexpectedEvent(expected: "YAML node")
|
|
}
|
|
|
|
switch event {
|
|
case let .alias(anchor, mark):
|
|
index += 1
|
|
guard let value = anchors[anchor] else {
|
|
throw PureYAML.Parsing.ParseError.undefinedAlias(
|
|
anchor: anchor,
|
|
line: mark.line,
|
|
column: mark.column,
|
|
)
|
|
}
|
|
return value
|
|
case let .mappingStart(anchor, _, _, _):
|
|
let value = try composeMapping()
|
|
store(value, anchor: anchor)
|
|
return value
|
|
case let .scalar(value, anchor, tag, style, mark):
|
|
index += 1
|
|
let model = try composeScalar(
|
|
value,
|
|
tag: tag,
|
|
style: style,
|
|
mark: mark,
|
|
)
|
|
store(model, anchor: anchor)
|
|
return model
|
|
case let .sequenceStart(anchor, _, _, _):
|
|
let value = try composeSequence()
|
|
store(value, anchor: anchor)
|
|
return value
|
|
default:
|
|
throw unexpectedEvent(expected: "YAML node")
|
|
}
|
|
}
|
|
|
|
mutating func composeSequence() throws -> PureYAML.Model.Value {
|
|
_ = try expect("sequence start") { event in
|
|
if case .sequenceStart = event {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
var values: [PureYAML.Model.Value] = []
|
|
while current?.isSequenceEnd != true {
|
|
try values.append(composeNode())
|
|
}
|
|
_ = try expect("sequence end") { $0.isSequenceEnd }
|
|
return .sequence(values)
|
|
}
|
|
|
|
mutating func composeMapping() throws -> PureYAML.Model.Value {
|
|
_ = try expect("mapping start") { event in
|
|
if case .mappingStart = event {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
var mergeSources: [[PureYAML.Model.Pair]] = []
|
|
var localPairs: [PureYAML.Model.Pair] = []
|
|
while current?.isMappingEnd != true {
|
|
let key = try composeMappingKey()
|
|
if key.isMergeKey {
|
|
try mergeSources.append(contentsOf: composeMergeSources())
|
|
} else {
|
|
let value = try composeNode()
|
|
localPairs.append(PureYAML.Model.Pair(keyNode: key.keyNode, value: value))
|
|
}
|
|
}
|
|
_ = try expect("mapping end") { $0.isMappingEnd }
|
|
let pairs = mergedPairs(from: mergeSources, localPairs: localPairs)
|
|
return .mapping(PureYAML.Model.Mapping(pairs))
|
|
}
|
|
|
|
mutating func composeMappingKey() throws -> ComposedMappingKey {
|
|
guard let event = current else {
|
|
throw unexpectedEvent(expected: "mapping key")
|
|
}
|
|
guard case let .scalar(value, anchor, tag, style, _) = event else {
|
|
let value = try composeNode()
|
|
return ComposedMappingKey(
|
|
keyNode: PureYAML.Model.Key(value: value),
|
|
scalarValue: nil,
|
|
tag: nil,
|
|
style: nil,
|
|
)
|
|
}
|
|
index += 1
|
|
store(.string(value), anchor: anchor)
|
|
return ComposedMappingKey(
|
|
keyNode: .string(value),
|
|
scalarValue: value,
|
|
tag: scalarParser.normalizedTag(tag),
|
|
style: style,
|
|
)
|
|
}
|
|
|
|
func composeScalar(
|
|
_ value: String,
|
|
tag: String?,
|
|
style: PureYAML.Parsing.ScalarStyle,
|
|
mark: PureYAML.Parsing.Mark,
|
|
) throws -> PureYAML.Model.Value {
|
|
try scalarParser.composeScalarValue(value, tag: tag, style: style, mark: mark)
|
|
}
|
|
}
|
|
|
|
extension PureYAML.Parsing.EventComposer {
|
|
var current: PureYAML.Parsing.Event? {
|
|
guard events.indices.contains(index) else {
|
|
return nil
|
|
}
|
|
return events[index]
|
|
}
|
|
|
|
mutating func store(
|
|
_ value: PureYAML.Model.Value,
|
|
anchor: String?,
|
|
) {
|
|
guard let anchor else {
|
|
return
|
|
}
|
|
anchors[anchor] = value
|
|
}
|
|
|
|
mutating func expect(
|
|
_ expected: String,
|
|
matching: (PureYAML.Parsing.Event) -> Bool,
|
|
) throws -> PureYAML.Parsing.Event {
|
|
guard let event = current else {
|
|
throw unexpectedEvent(expected: expected)
|
|
}
|
|
guard matching(event) else {
|
|
throw unexpectedEvent(expected: expected)
|
|
}
|
|
index += 1
|
|
return event
|
|
}
|
|
|
|
func unexpectedEvent(expected: String) -> PureYAML.Parsing.ParseError {
|
|
let mark = current?.mark ?? events.last?.mark ?? .start
|
|
return PureYAML.Parsing.ParseError.unexpectedEvent(
|
|
expected: expected,
|
|
actual: current?.description ?? "end of events",
|
|
line: mark.line,
|
|
column: mark.column,
|
|
)
|
|
}
|
|
|
|
mutating func expectStreamEnd() throws -> PureYAML.Parsing.Event {
|
|
try expect("stream end") { event in
|
|
if case .streamEnd = event {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|