257 lines
9.9 KiB
Swift
257 lines
9.9 KiB
Swift
extension PureYAML.Parsing.TokenEventParser {
|
|
mutating func parseBlockSequence(
|
|
properties: PureYAML.Parsing.NodeProperties,
|
|
allowMappingKeyTerminator: Bool = false,
|
|
) throws -> PureYAML.Parsing.NodeResult {
|
|
let start = try expect("block sequence entry") { $0.isBlockEntry }
|
|
let startMark = properties.mark ?? start.mark
|
|
var endMark = start.endMark
|
|
events.append(.sequenceStart(
|
|
anchor: properties.anchor,
|
|
tag: properties.tag,
|
|
style: .block,
|
|
mark: startMark,
|
|
))
|
|
|
|
try parseBlockSequenceItem(
|
|
after: start,
|
|
endMark: &endMark,
|
|
allowMappingKeyTerminator: allowMappingKeyTerminator,
|
|
)
|
|
try consumeSequenceItemBoundaryDedent(sequenceIndentWidth: start.mark.column - 1)
|
|
try consumeSequenceItemBoundaryIndent(sequenceIndentWidth: start.mark.column - 1)
|
|
while cursor.current?.kind.isBlockEntry == true {
|
|
let entry = try expect("block sequence entry") { $0.isBlockEntry }
|
|
try parseBlockSequenceItem(
|
|
after: entry,
|
|
endMark: &endMark,
|
|
allowMappingKeyTerminator: allowMappingKeyTerminator,
|
|
)
|
|
try consumeSequenceItemBoundaryDedent(sequenceIndentWidth: entry.mark.column - 1)
|
|
try consumeSequenceItemBoundaryIndent(sequenceIndentWidth: entry.mark.column - 1)
|
|
}
|
|
if let current = cursor.current, current.kind.isMappingKey, !allowMappingKeyTerminator {
|
|
throw PureYAML.Parsing.ParseError.mixedCollectionStyles(line: current.mark.line)
|
|
}
|
|
|
|
events.append(.sequenceEnd(mark: endMark))
|
|
return PureYAML.Parsing.NodeResult(kind: .sequence, endMark: endMark)
|
|
}
|
|
|
|
mutating func consumeSequenceItemBoundaryDedent(sequenceIndentWidth: Int) throws {
|
|
guard dedentWidth(cursor.current) == sequenceIndentWidth,
|
|
cursor.peek()?.kind.isBlockEntry == true
|
|
else {
|
|
return
|
|
}
|
|
_ = try expect("dedent") { $0.isDedent }
|
|
}
|
|
|
|
mutating func consumeSequenceItemBoundaryIndent(sequenceIndentWidth: Int) throws {
|
|
guard indentWidth(cursor.current) == sequenceIndentWidth,
|
|
cursor.peek()?.kind.isBlockEntry == true
|
|
else {
|
|
return
|
|
}
|
|
_ = try expect("indent") { $0.isIndent }
|
|
}
|
|
|
|
mutating func parseBlockSequenceItem(
|
|
after entry: PureYAML.Parsing.Token,
|
|
endMark: inout PureYAML.Parsing.Mark,
|
|
allowMappingKeyTerminator: Bool,
|
|
) throws {
|
|
if hasNodeOnSameLine(after: entry) {
|
|
let properties = consumeProperties()
|
|
if cursor.current?.kind.isIndent == true {
|
|
_ = try expect("indent") { $0.isIndent }
|
|
endMark = try parseNode(properties: properties).endMark
|
|
_ = try expect("dedent") { $0.isDedent }
|
|
return
|
|
}
|
|
if cursor.current?.kind.isMappingKey == true {
|
|
endMark = try parseBlockMapping(
|
|
properties: properties,
|
|
includeIndentedContinuation: true,
|
|
minimumColumn: entry.mark.column + 1,
|
|
).endMark
|
|
} else if cursor.current?.kind.isBlockEntry == true {
|
|
endMark = try parseBlockSequence(
|
|
properties: properties,
|
|
allowMappingKeyTerminator: allowMappingKeyTerminator,
|
|
).endMark
|
|
} else {
|
|
endMark = try parseNode(properties: properties).endMark
|
|
}
|
|
} else if cursor.current?.kind.isIndent == true {
|
|
_ = try expect("indent") { $0.isIndent }
|
|
endMark = try parseNode().endMark
|
|
_ = try expect("dedent") { $0.isDedent }
|
|
} else {
|
|
let mark = entry.endMark
|
|
events.append(.scalar(value: "", anchor: nil, tag: nil, style: .plain, mark: mark))
|
|
endMark = mark
|
|
}
|
|
}
|
|
|
|
mutating func parseBlockMapping(
|
|
properties: PureYAML.Parsing.NodeProperties,
|
|
includeIndentedContinuation: Bool,
|
|
minimumColumn: Int? = nil,
|
|
) throws -> PureYAML.Parsing.NodeResult {
|
|
let startMark = properties.mark ?? cursor.current?.mark ?? .start
|
|
let tokenColumn = cursor.current?.mark.column ?? startMark.column
|
|
let contentColumn = min(startMark.column, tokenColumn)
|
|
let boundaryColumn = minimumColumn ?? contentColumn
|
|
var endMark = startMark
|
|
events.append(.mappingStart(
|
|
anchor: properties.anchor,
|
|
tag: properties.tag,
|
|
style: .block,
|
|
mark: startMark,
|
|
))
|
|
|
|
try parseBlockMappingPairs(
|
|
boundaryColumn: boundaryColumn,
|
|
includeIndentedContinuation: includeIndentedContinuation,
|
|
endMark: &endMark,
|
|
)
|
|
if let current = cursor.current, !canEndBlockMapping(at: current, minimumColumn: boundaryColumn) {
|
|
throw PureYAML.Parsing.ParseError.unexpectedToken(
|
|
expected: "mapping key",
|
|
actual: current.kind.description,
|
|
line: current.mark.line,
|
|
column: current.mark.column,
|
|
)
|
|
}
|
|
events.append(.mappingEnd(mark: endMark))
|
|
return PureYAML.Parsing.NodeResult(kind: .mapping, endMark: endMark)
|
|
}
|
|
|
|
mutating func parseBlockMappingPairs(
|
|
boundaryColumn: Int,
|
|
includeIndentedContinuation: Bool,
|
|
endMark: inout PureYAML.Parsing.Mark,
|
|
) throws {
|
|
while canContinueBlockMapping(minimumColumn: boundaryColumn) {
|
|
endMark = try parseMappingPairAndBoundaries(
|
|
boundaryColumn: boundaryColumn,
|
|
includeIndentedContinuation: includeIndentedContinuation,
|
|
).endMark
|
|
}
|
|
while includeIndentedContinuation, cursor.current?.kind.isIndent == true {
|
|
endMark = try parseIndentedBlockMappingPairs(boundaryColumn: boundaryColumn).endMark
|
|
}
|
|
}
|
|
|
|
mutating func parseIndentedBlockMappingPairs(
|
|
boundaryColumn: Int,
|
|
) throws -> PureYAML.Parsing.NodeResult {
|
|
_ = try expect("indent") { $0.isIndent }
|
|
guard cursor.current?.kind.isMappingKey == true else {
|
|
throw unexpectedToken(expected: "mapping key")
|
|
}
|
|
var endMark = cursor.current?.mark ?? .start
|
|
while canContinueBlockMapping(minimumColumn: boundaryColumn) {
|
|
endMark = try parseMappingPairAndBoundaries(
|
|
boundaryColumn: boundaryColumn,
|
|
includeIndentedContinuation: true,
|
|
).endMark
|
|
}
|
|
if !canEndBlockMapping(at: cursor.current, minimumColumn: boundaryColumn) {
|
|
_ = try expect("dedent") { $0.isDedent }
|
|
}
|
|
return PureYAML.Parsing.NodeResult(kind: .mapping, endMark: endMark)
|
|
}
|
|
|
|
mutating func parseMappingPairAndBoundaries(
|
|
boundaryColumn: Int,
|
|
includeIndentedContinuation: Bool,
|
|
) throws -> PureYAML.Parsing.NodeResult {
|
|
let result = try parseMappingPair()
|
|
try consumeMappingSiblingBoundaryDedents(minimumColumn: boundaryColumn)
|
|
try consumeMappingSiblingBoundaryReindent(minimumColumn: boundaryColumn)
|
|
if includeIndentedContinuation {
|
|
try consumeDedents(atOrAbove: boundaryColumn)
|
|
try consumeMappingSiblingBoundaryDedents(minimumColumn: boundaryColumn)
|
|
try consumeMappingSiblingBoundaryReindent(minimumColumn: boundaryColumn)
|
|
}
|
|
return result
|
|
}
|
|
|
|
mutating func parseBlockScalar(
|
|
properties: PureYAML.Parsing.NodeProperties,
|
|
) throws -> PureYAML.Parsing.NodeResult {
|
|
let header = try expect("block scalar header") { kind in
|
|
if case .blockScalarHeader = kind {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
var lines: [String] = []
|
|
var endMark = header.endMark
|
|
if cursor.current?.kind.isIndent == true {
|
|
_ = try expect("indent") { $0.isIndent }
|
|
while cursor.current != nil, cursor.current?.kind.isDedent != true {
|
|
guard case let .scalar(value, style) = cursor.current?.kind else {
|
|
throw unexpectedToken(expected: "block scalar content")
|
|
}
|
|
let token = cursor.advance() ?? header
|
|
try lines.append(decodeScalar(value, style: style, line: token.mark.line))
|
|
endMark = token.endMark
|
|
}
|
|
_ = try expect("dedent") { $0.isDedent }
|
|
}
|
|
|
|
let value = blockScalarValue(lines: lines, header: header.kind)
|
|
let mark = properties.mark ?? header.mark
|
|
events.append(.scalar(
|
|
value: value,
|
|
anchor: properties.anchor,
|
|
tag: properties.tag,
|
|
style: scalarStyle(for: header.kind),
|
|
mark: mark,
|
|
))
|
|
return PureYAML.Parsing.NodeResult(kind: .scalar, endMark: endMark)
|
|
}
|
|
|
|
func scalarStyle(for kind: PureYAML.Parsing.TokenKind) -> PureYAML.Parsing.ScalarStyle {
|
|
switch kind {
|
|
case .blockScalarHeader(.folded, _):
|
|
.folded
|
|
case .blockScalarHeader(.literal, _):
|
|
.literal
|
|
default:
|
|
.plain
|
|
}
|
|
}
|
|
|
|
func blockScalarValue(
|
|
lines: [String],
|
|
header: PureYAML.Parsing.TokenKind,
|
|
) -> String {
|
|
let (style, chomping): (PureYAML.Parsing.BlockScalarStyle, PureYAML.Parsing.BlockScalarChomping)
|
|
switch header {
|
|
case let .blockScalarHeader(headerStyle, headerChomping):
|
|
style = headerStyle
|
|
chomping = headerChomping
|
|
default:
|
|
style = .literal
|
|
chomping = .clip
|
|
}
|
|
|
|
let separator = style == .folded ? " " : "\n"
|
|
let content = lines.joined(separator: separator)
|
|
guard !content.isEmpty else {
|
|
return ""
|
|
}
|
|
|
|
switch chomping {
|
|
case .strip:
|
|
return content
|
|
case .clip:
|
|
return content + "\n"
|
|
}
|
|
}
|
|
}
|