///|
/// A source position reported by parser diagnostics.
pub(all) struct Position {
offset : Int
line : Int
column : Int
} derive(Eq, Debug)
///|
/// Operations shared by parser input cursors. `cursor` must increase whenever
/// an input element is consumed.
pub(open) trait Cursor {
fn cursor(Self) -> Int
fn position(Self) -> Position
fn is_at_eof(Self) -> Bool
fn same_cursor(Self, other : Self) -> Bool = _
}
///|
/// Immutable character input cursor.
/// Offsets are UTF-16 code unit offsets, matching MoonBit string indexing.
pub(all) struct Input {
source : String
offset : Int
line : Int
column : Int
} derive(Eq, Debug)
///|
pub fn Input::new(source : String) -> Input {
{ source, offset: 0, line: 1, column: 1 }
}
///|
pub fn Input::is_eof(self : Input) -> Bool {
self.offset >= self.source.length()
}
///|
pub fn Input::peek(self : Input) -> Char? {
self.source.get_char(self.offset)
}
///|
pub fn Input::next(self : Input) -> (Char, Input)? {
match self.peek() {
Some(ch) => Some((ch, self.advance(ch)))
None => None
}
}
///|
pub fn Input::advance(self : Input, ch : Char) -> Input {
if ch == '\n' {
Input::{
source: self.source,
offset: self.offset + ch.utf16_len(),
line: self.line + 1,
column: 1,
}
} else {
Input::{
source: self.source,
offset: self.offset + ch.utf16_len(),
line: self.line,
column: self.column + 1,
}
}
}
///|
pub fn Input::advance_string(self : Input, text : String) -> Input {
let mut input = self
for ch in text.iter() {
input = input.advance(ch)
}
input
}
///|
pub fn Input::remaining(self : Input) -> StringView {
self.source.view(start_offset=self.offset)
}
///|
pub impl Cursor for Input with fn cursor(self) {
self.offset
}
///|
pub impl Cursor for Input with fn position(self) {
{ offset: self.offset, line: self.line, column: self.column }
}
///|
pub impl Cursor for Input with fn is_at_eof(self) {
self.is_eof()
}
///|
/// Checks whether the cursor has the same position as `other`.
///
/// Parameters:
///
/// * `self` : The current cursor.
/// * `other` : Another cursor to compare against.
///
/// Used internally by `many` and `many_until` to detect parsers
/// that accepted empty input.
///
/// Returns `true` if both cursors are at the same position.
impl Cursor with fn same_cursor(self, other) {
self.cursor() == other.cursor()
}