Add parseLocated / parseStreamLocated, returning a Model.LocatedValue tree parallel in shape to Value, where every node carries its source span (1-based line/column) and located children. Collections span from their opening to closing event; scalars carry a start-anchored span; aliases and merge-inherited pairs honestly point at their reference site. The plain Value, its equality, and the conformance oracle are untouched: locations are built only when the streaming composer is asked for them (tracksLocations), behind new entry points, so the default value path is byte-identical. A whole-suite test proves the located projection equals a plain parse for every passing case (402/402), including through merge keys and aliases, and the canonical-event and gap gates stay green.
92 lines
3.4 KiB
Swift
92 lines
3.4 KiB
Swift
extension PureYAML.Parsing.Parser {
|
|
func parseEvents(_ yaml: String) throws -> [PureYAML.Parsing.Event] {
|
|
let events: [PureYAML.Parsing.Event]
|
|
do {
|
|
var parser = try PureYAML.Parsing.LibYAML.Parser(yaml)
|
|
events = try parser.parse().map { $0.bumpingMark() }
|
|
} catch let error as PureYAML.Parsing.LibYAML.ScannerError {
|
|
throw PureYAML.Parsing.ParseError(translating: error)
|
|
} catch let error as PureYAML.Parsing.LibYAML.ParserError {
|
|
throw PureYAML.Parsing.ParseError(translating: error)
|
|
}
|
|
try enforceNestingLimit(events)
|
|
return events
|
|
}
|
|
|
|
/// The deepest collection nesting the value/event composers will build. The
|
|
/// ported parser produces events iteratively without recursion, but the
|
|
/// composers, equality, hashing, and `Model/Value` teardown all recurse with
|
|
/// the nesting depth, so the event stream is bounded here before any recursive
|
|
/// consumer runs. 64 accepts every realistic document yet rejects pathological
|
|
/// input well before it can overflow the stack.
|
|
static var maxNestingDepth: Int {
|
|
64
|
|
}
|
|
|
|
/// Reject an event stream that nests collections past ``maxNestingDepth`` with
|
|
/// a ``ParseError/nestingTooDeep(line:column:)`` so deeply nested input fails
|
|
/// cleanly instead of overflowing a recursive consumer's stack.
|
|
private func enforceNestingLimit(_ events: [PureYAML.Parsing.Event]) throws {
|
|
var depth = 0
|
|
for event in events {
|
|
switch event {
|
|
case .sequenceStart, .mappingStart:
|
|
depth += 1
|
|
if depth > Self.maxNestingDepth {
|
|
throw PureYAML.Parsing.ParseError.nestingTooDeep(line: event.mark.line, column: event.mark.column)
|
|
}
|
|
case .sequenceEnd, .mappingEnd:
|
|
depth -= 1
|
|
default:
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
func parseStreaming(_ yaml: String) throws -> PureYAML.Model.Value {
|
|
var composer = PureYAML.Parsing.EventStreamComposer(
|
|
scalarParser: self,
|
|
mode: .singleDocument,
|
|
)
|
|
for event in try parseEvents(yaml) {
|
|
try composer.consume(event)
|
|
}
|
|
return try composer.composedValue()
|
|
}
|
|
|
|
func parseStreamStreaming(_ yaml: String) throws -> [PureYAML.Stream.Document] {
|
|
var composer = PureYAML.Parsing.EventStreamComposer(
|
|
scalarParser: self,
|
|
mode: .stream,
|
|
)
|
|
for event in try parseEvents(yaml) {
|
|
try composer.consume(event)
|
|
}
|
|
return try composer.composedStream()
|
|
}
|
|
|
|
func parseLocatedStreaming(_ yaml: String) throws -> PureYAML.Model.LocatedValue {
|
|
var composer = PureYAML.Parsing.EventStreamComposer(
|
|
scalarParser: self,
|
|
mode: .singleDocument,
|
|
tracksLocations: true,
|
|
)
|
|
for event in try parseEvents(yaml) {
|
|
try composer.consume(event)
|
|
}
|
|
return try composer.composedLocated()
|
|
}
|
|
|
|
func parseStreamLocatedStreaming(_ yaml: String) throws -> [PureYAML.Model.LocatedValue] {
|
|
var composer = PureYAML.Parsing.EventStreamComposer(
|
|
scalarParser: self,
|
|
mode: .stream,
|
|
tracksLocations: true,
|
|
)
|
|
for event in try parseEvents(yaml) {
|
|
try composer.consume(event)
|
|
}
|
|
return try composer.composedLocatedStream()
|
|
}
|
|
}
|