Stage 3 foundation of the faithful libyaml scanner port. Translates libyaml's input buffer and position tracking (the buffer/mark fields and the PEEK/SKIP/SKIP_LINE macros) into PureYAML.Parsing.LibYAML.Reader. It iterates Unicode scalars rather than grapheme clusters so a CR and an LF are distinct, matching libyaml's byte-oriented scanning (a CR+LF pair is still one line break), and tracks the mark in libyaml's 0-based line and column convention. Unit-tested for peek/skip column tracking and LF and CRLF line breaks. This is the input layer the token fetchers consume.
58 lines
2.0 KiB
Swift
58 lines
2.0 KiB
Swift
extension PureYAML.Parsing.LibYAML {
|
|
/// Port of libyaml's input buffer and position tracking (the `buffer`/`mark`
|
|
/// fields with the `PEEK`, `SKIP`, and `SKIP_LINE` macros). It iterates
|
|
/// `Unicode.Scalar`s rather than grapheme clusters so a CR and an LF are
|
|
/// distinct, matching libyaml's byte-oriented scanning, and tracks the mark in
|
|
/// libyaml's 0-based line and column convention; conversion to the package's
|
|
/// 1-based `Mark` happens at token emission during integration.
|
|
struct Reader {
|
|
private let scalars: [Unicode.Scalar]
|
|
private(set) var index = 0
|
|
private(set) var line = 0
|
|
private(set) var column = 0
|
|
|
|
init(_ string: String) {
|
|
scalars = Array(string.unicodeScalars)
|
|
}
|
|
|
|
/// `PEEK_TOKEN` / `CHECK_AT`: the scalar `offset` ahead, or nil past the
|
|
/// end (libyaml's `\0`).
|
|
func peek(_ offset: Int = 0) -> Unicode.Scalar? {
|
|
let position = index + offset
|
|
return position < scalars.count ? scalars[position] : nil
|
|
}
|
|
|
|
var isAtEnd: Bool {
|
|
index >= scalars.count
|
|
}
|
|
|
|
/// The current position. Column is 0-based, as in libyaml.
|
|
var mark: PureYAML.Parsing.Mark {
|
|
PureYAML.Parsing.Mark(line: line, column: column, index: index)
|
|
}
|
|
|
|
/// `SKIP`: consume one non-break scalar, advancing the column.
|
|
mutating func skip() {
|
|
guard index < scalars.count else {
|
|
return
|
|
}
|
|
index += 1
|
|
column += 1
|
|
}
|
|
|
|
/// `SKIP_LINE`: consume a line break and start a new line. A CR+LF pair is
|
|
/// one break, matching libyaml.
|
|
mutating func skipLine() {
|
|
if peek() == "\r", peek(1) == "\n" {
|
|
index += 2
|
|
} else if CharacterClass.isBreak(peek()) {
|
|
index += 1
|
|
} else {
|
|
return
|
|
}
|
|
line += 1
|
|
column = 0
|
|
}
|
|
}
|
|
}
|