// sf_cursor.mbt — Cursor for structured field parsing.
//
// The cursor scans a UTF-16 string by code-unit offset. Structured field
// values are ASCII by grammar, so for valid input a code-unit offset equals a
// UTF-8 byte offset; all reported `HsError.offset` values are therefore byte
// offsets. Non-ASCII input is rejected by the grammar checks.
//
// Every cursor operation that can fail raises `HsError`; boundary functions
// convert to `Result`.
///|
/// A forward-only cursor over a string being parsed as a structured field.
priv struct SfCursor {
input : String
mut offset : Int
}
///|
/// Creates a cursor positioned at the start of `input`.
fn SfCursor::new(input : String) -> SfCursor {
{ input, offset: 0 }
}
///|
/// Returns the current offset in code units.
fn SfCursor::pos(self : SfCursor) -> Int {
self.offset
}
///|
/// Returns `true` when the input is exhausted.
fn SfCursor::at_end(self : SfCursor) -> Bool {
self.offset >= self.input.length()
}
///|
/// Returns the code unit at the current offset, or raises `UnexpectedEnd`.
fn SfCursor::peek(self : SfCursor) -> Char raise HsError {
if self.at_end() {
raise hs_error_at(
StructuredFieldParsing,
UnexpectedEnd,
self.offset,
"unexpected end of structured field",
)
}
self.input
.get_char(self.offset)
.unwrap_or_else(() => {
raise hs_error_at(
StructuredFieldParsing,
UnexpectedEnd,
self.offset,
"unexpected end of structured field",
)
})
}
///|
/// Advances the cursor by one code unit. The caller must have verified that
/// the cursor is not at the end.
fn SfCursor::advance(self : SfCursor) -> Unit {
self.offset = self.offset + 1
}
///|
/// Skips zero or more ASCII SP characters.
fn SfCursor::skip_sp(self : SfCursor) -> Unit raise HsError {
while !self.at_end() {
match self.peek() {
' ' => self.advance()
_ => return
}
}
}
///|
/// Returns the substring consumed since `start` as an owned string.
fn SfCursor::slice_from(self : SfCursor, start : Int) -> String {
self.input.sub(start~, end=self.offset).to_owned()
}
///|
/// Consumes exactly one character and returns it.
fn SfCursor::take(self : SfCursor) -> Char raise HsError {
let c = self.peek()
self.advance()
c
}