use crate::csets::CharacterSet; pub struct Scanner { source: Box<[char]>, position: usize, } impl Scanner { pub fn new(source: impl AsRef) -> Self { Self { source: source.as_ref().chars().collect(), position: 0, } } pub fn position(&self) -> usize { self.position } pub fn goto(&mut self, position: usize) -> Option { // allow reverse ranges let production = if self.position < position { self.source.get(self.position..position)?.iter().collect() } else { self.source .get(position..self.position)? .iter() .rev() .collect() }; self.position = position; Some(production) } pub fn advance(&mut self, amount: usize) -> Option { self.goto(self.position + amount) } pub fn find_substring(&self, substring: impl AsRef) -> Option { self.source .get(self.position..)? .iter() .collect::() .find(substring.as_ref()) } pub fn starts_with(&self, substring: impl AsRef) -> Option { let mut i = self.position; for substring_char in substring.as_ref().chars() { if *self.source.get(i)? != substring_char { return None; } i += 1; } Some(i) } pub fn advance_if_starts_with(&mut self, substring: impl AsRef) -> Option { let position = self.starts_with(substring)?; self.goto(position) } pub fn any(&self, cset: impl CharacterSet) -> Option { cset.contains(*self.source.get(self.position)?) .then_some(self.position + 1) } pub fn many(&self, cset: impl CharacterSet) -> Option { if !cset.contains(*self.source.get(self.position)?) { return None; } let mut i = self.position; while i < self.source.len() && cset.contains(self.source[i]) { i += 1; } Some(i) } pub fn upto(&self, cset: impl CharacterSet) -> Option { let mut i = self.position; while !cset.contains(*self.source.get(i)?) { i += 1; } Some(i) } }