The 22-case TokenType mapping was split across three initializers to stay under the complexity lint, leaving a preconditionFailure default the types could not prove unreachable, which also tripped the forbidden-patterns gate on every push. One exhaustive switch makes a new TokenType case a compile error instead of a runtime trap; the lint now ignores case statements when counting complexity, since exhaustive closed-enum switches are the point, not a smell.
103 lines
4.4 KiB
Swift
103 lines
4.4 KiB
Swift
public extension PureYAML.Streaming {
|
|
/// A resumable, chunked YAML token scanner: the public counterpart to the
|
|
/// internal libyaml-ported scanner's `scanStep()`, which this wraps
|
|
/// directly with no independent logic of its own. Proven to match batch
|
|
/// scanning byte-for-byte across the whole yaml-test-suite corpus and at
|
|
/// several chunk sizes (`StreamingScannerDifferentialTests` in the test
|
|
/// suite exercises the identical internal `scanStep()` this type calls).
|
|
struct Scanner: Sendable {
|
|
private var scanner: PureYAML.Parsing.LibYAML.Scanner
|
|
|
|
/// Creates a scanner with no input yet. Feed input with ``feed(_:)``
|
|
/// and mark the end of input with ``finish()``.
|
|
public init() {
|
|
scanner = PureYAML.Parsing.LibYAML.Scanner("", isFinal: false)
|
|
}
|
|
|
|
/// Appends a chunk of YAML source. Chunks are concatenated in the
|
|
/// order fed; a chunk need not align with any YAML grammar boundary.
|
|
public mutating func feed(_ chunk: String) {
|
|
scanner.reader.append(chunk)
|
|
}
|
|
|
|
/// Marks that no further input will be fed. After this, ``next()``
|
|
/// never returns ``Outcome/needMoreInput``: a fetch that would have
|
|
/// needed more input instead surfaces as a real scanning error, since
|
|
/// there is no more input coming that could resolve it.
|
|
public mutating func finish() {
|
|
scanner.reader.markFinal()
|
|
}
|
|
|
|
/// Advances the scan by one token. Call in a loop, feeding more input
|
|
/// on ``Outcome/needMoreInput`` and stopping on ``Outcome/done``.
|
|
public mutating func next() throws -> Outcome {
|
|
switch try scanner.scanStep() {
|
|
case let .token(token):
|
|
.token(Token(token))
|
|
case .needMoreInput:
|
|
.needMoreInput
|
|
case .done:
|
|
.done
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
extension PureYAML.Streaming.Token {
|
|
init(_ token: PureYAML.Parsing.LibYAML.Token) {
|
|
self.init(
|
|
kind: Kind(token.type),
|
|
range: PureYAML.Model.SourceRange(
|
|
start: PureYAML.Model.SourcePosition(line: token.startMark.line, column: token.startMark.column),
|
|
end: PureYAML.Model.SourcePosition(line: token.endMark.line, column: token.endMark.column),
|
|
),
|
|
)
|
|
}
|
|
}
|
|
|
|
extension PureYAML.Streaming.Token.Kind {
|
|
/// A mechanical 1:1 mapping over all 22 `TokenType` cases in one
|
|
/// exhaustive switch: no `default`, so adding a `TokenType` case is a
|
|
/// compile error here instead of a runtime trap. The lint's complexity
|
|
/// metric ignores case statements precisely for closed-enum mappings
|
|
/// like this one.
|
|
init(_ type: PureYAML.Parsing.LibYAML.TokenType) {
|
|
switch type {
|
|
case .streamStart: self = .streamStart
|
|
case .streamEnd: self = .streamEnd
|
|
case .documentStart: self = .documentStart
|
|
case .documentEnd: self = .documentEnd
|
|
case .blockSequenceStart: self = .blockSequenceStart
|
|
case .blockMappingStart: self = .blockMappingStart
|
|
case .blockEnd: self = .blockEnd
|
|
case .blockEntry: self = .blockEntry
|
|
case .flowSequenceStart: self = .flowSequenceStart
|
|
case .flowMappingStart: self = .flowMappingStart
|
|
case .flowSequenceEnd: self = .flowSequenceEnd
|
|
case .flowMappingEnd: self = .flowMappingEnd
|
|
case .flowEntry: self = .flowEntry
|
|
case .key: self = .key
|
|
case .value: self = .value
|
|
case .reservedDirective: self = .reservedDirective
|
|
case let .versionDirective(major, minor): self = .versionDirective(major: major, minor: minor)
|
|
case let .tagDirective(handle, prefix): self = .tagDirective(handle: handle, prefix: prefix)
|
|
case let .anchor(name): self = .anchor(name: name)
|
|
case let .alias(name): self = .alias(name: name)
|
|
case let .tag(handle, suffix): self = .tag(handle: handle, suffix: suffix)
|
|
case let .scalar(value, style): self = .scalar(value: value, style: PureYAML.Streaming.Token.ScalarStyle(style))
|
|
}
|
|
}
|
|
}
|
|
|
|
extension PureYAML.Streaming.Token.ScalarStyle {
|
|
init(_ style: PureYAML.Parsing.ScalarStyle) {
|
|
switch style {
|
|
case .plain: self = .plain
|
|
case .singleQuoted: self = .singleQuoted
|
|
case .doubleQuoted: self = .doubleQuoted
|
|
case .literal: self = .literal
|
|
case .folded: self = .folded
|
|
}
|
|
}
|
|
}
|