use peekmore::{PeekMore, PeekMoreIterator};
use std::{ops::RangeBounds, str::Chars};
#[derive(Debug, Clone)]
pub struct CharStream {
source: &'static str,
iter: PeekMoreIterator<Chars<'static>>,
peek_cursor: usize,
true_cursor: usize,
}
impl CharStream {
pub fn new(source: &'static str) -> Self {
Self {
source,
iter: source.chars().peekmore(),
peek_cursor: 0,
true_cursor: 0,
}
}
pub fn chomp(&mut self) -> Option<char> {
let next = self.iter.next()?;
self.true_cursor += 1;
self.peek_cursor = self.true_cursor;
Some(next)
}
pub fn match_chomp(&mut self, expected: char) -> bool {
if self.match_peek_with(|c| c == expected) {
self.chomp().is_some()
} else {
false
}
}
pub fn match_chomp_with<F: FnMut(char) -> bool>(&mut self, f: F) -> bool {
if self.match_peek_with(f) {
self.chomp().is_some()
} else {
false
}
}
pub fn peek(&mut self) -> Option<char> {
self.iter.peek().copied()
}
pub fn match_peek(&mut self, expected: char) -> bool {
self.match_peek_with(|c| c == expected)
}
pub fn match_peek_with<F: FnMut(char) -> bool>(&mut self, mut f: F) -> bool {
let Some(c) = self.peek() else { return false };
if f(c) {
self.advance();
true
} else {
false
}
}
pub fn peek_while<F: FnMut(char) -> bool>(&mut self, mut f: F) -> bool {
let mut any = false;
while self.match_peek_with(&mut f) {
any = true
}
any
}
pub fn peek_move(&mut self) -> Option<char> {
let peek = self.peek();
self.advance();
peek
}
pub fn chomp_peeks(&mut self) -> &'static str {
let slice = self.inspect_peeks();
self.iter.truncate_iterator_to_cursor();
self.true_cursor = self.peek_cursor;
slice
}
pub fn empty_peeks(&mut self) -> &'static str {
let slice = self.inspect_peeks();
self.reset_peeks();
slice
}
pub fn inspect_peeks(&mut self) -> &'static str {
let forward_position = std::cmp::min(self.source.len(), self.peek_cursor);
self.slice(self.true_cursor..forward_position)
}
pub fn reset_peeks(&mut self) {
self.iter.reset_cursor();
self.peek_cursor = self.true_cursor;
}
pub fn advance(&mut self) {
self.peek_cursor += 1;
self.iter.advance_cursor();
}
pub fn position(&self) -> usize {
self.true_cursor
}
pub fn peek_position(&self) -> usize {
self.peek_cursor
}
pub fn at_end(&self) -> bool {
self.position() == self.source.len()
}
pub fn slice<R>(&self, range: R) -> &'static str
where
R: RangeBounds<usize> + std::slice::SliceIndex<str, Output = str>,
{
&self.source[range]
}
pub fn source(&self) -> &str {
self.source
}
}
#[derive(Debug, Default)]
pub struct Cursor(usize);
impl Cursor {
pub fn position(&self) -> usize {
self.0
}
pub fn next(&self) -> usize {
self.0 + 1
}
}