236 lines
5.2 KiB
Rust
236 lines
5.2 KiB
Rust
use std::ops::Deref;
|
|
use std::ops::Range;
|
|
use std::sync::Arc;
|
|
|
|
use winnow::stream::Compare;
|
|
use winnow::stream::FindSlice;
|
|
use winnow::stream::Offset;
|
|
use winnow::stream::Stream;
|
|
use winnow::stream::StreamIsPartial;
|
|
|
|
#[derive(Clone, PartialEq, Eq)]
|
|
pub struct NomoInput {
|
|
backing: Arc<str>,
|
|
range: Range<usize>,
|
|
}
|
|
|
|
impl NomoInput {
|
|
pub fn from_parts(backing: Arc<str>, range: Range<usize>) -> NomoInput {
|
|
NomoInput { backing, range }
|
|
}
|
|
|
|
pub fn into_parts(self) -> (Arc<str>, Range<usize>) {
|
|
(self.backing, self.range)
|
|
}
|
|
|
|
pub fn get_range(&self) -> Range<usize> {
|
|
self.range.clone()
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct NomoInputCheckpoint {
|
|
range: Range<usize>,
|
|
}
|
|
|
|
impl std::fmt::Debug for NomoInput {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
write!(f, "{:?} ({:?})", self.as_str(), self.range)
|
|
}
|
|
}
|
|
|
|
impl From<String> for NomoInput {
|
|
fn from(value: String) -> Self {
|
|
let range = 0..value.len();
|
|
let backing = Arc::from(value);
|
|
|
|
NomoInput { backing, range }
|
|
}
|
|
}
|
|
|
|
impl From<&str> for NomoInput {
|
|
fn from(value: &str) -> Self {
|
|
let backing = Arc::from(value.to_string());
|
|
let range = 0..value.len();
|
|
|
|
NomoInput { backing, range }
|
|
}
|
|
}
|
|
|
|
impl FindSlice<&str> for NomoInput {
|
|
fn find_slice(&self, substr: &str) -> Option<core::ops::Range<usize>> {
|
|
self.as_str().find_slice(substr)
|
|
}
|
|
}
|
|
|
|
impl Compare<&str> for NomoInput {
|
|
fn compare(&self, t: &str) -> winnow::stream::CompareResult {
|
|
self.as_str().compare(t)
|
|
}
|
|
}
|
|
|
|
impl Deref for NomoInput {
|
|
type Target = str;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.backing[self.range.clone()]
|
|
}
|
|
}
|
|
|
|
impl NomoInput {
|
|
pub fn as_str(&self) -> &str {
|
|
self.deref()
|
|
}
|
|
}
|
|
|
|
impl Offset for NomoInputCheckpoint {
|
|
fn offset_from(&self, start: &Self) -> usize {
|
|
self.range.start - start.range.start
|
|
}
|
|
}
|
|
|
|
impl Offset<NomoInputCheckpoint> for NomoInput {
|
|
fn offset_from(&self, start: &NomoInputCheckpoint) -> usize {
|
|
self.range.start - start.range.start
|
|
}
|
|
}
|
|
|
|
impl Offset for NomoInput {
|
|
fn offset_from(&self, start: &Self) -> usize {
|
|
self.as_str().offset_from(&start.as_str())
|
|
}
|
|
}
|
|
|
|
pub struct NomoInputIter {
|
|
idx: usize,
|
|
input: NomoInput,
|
|
}
|
|
|
|
impl Iterator for NomoInputIter {
|
|
type Item = (usize, char);
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
let val = self.input.as_str().char_indices().nth(self.idx);
|
|
self.idx += 1;
|
|
val
|
|
}
|
|
}
|
|
|
|
impl StreamIsPartial for NomoInput {
|
|
type PartialState = ();
|
|
|
|
fn complete(&mut self) -> Self::PartialState {
|
|
// Always complete
|
|
}
|
|
|
|
fn restore_partial(&mut self, _state: Self::PartialState) {}
|
|
|
|
fn is_partial_supported() -> bool {
|
|
false
|
|
}
|
|
}
|
|
|
|
impl Stream for NomoInput {
|
|
type Token = char;
|
|
|
|
type Slice = NomoInput;
|
|
|
|
type IterOffsets = NomoInputIter;
|
|
|
|
type Checkpoint = NomoInputCheckpoint;
|
|
|
|
fn iter_offsets(&self) -> Self::IterOffsets {
|
|
NomoInputIter {
|
|
idx: 0,
|
|
input: self.clone(),
|
|
}
|
|
}
|
|
|
|
fn eof_offset(&self) -> usize {
|
|
self.len()
|
|
}
|
|
|
|
fn next_token(&mut self) -> Option<Self::Token> {
|
|
let c = self.chars().next()?;
|
|
|
|
self.range.start += c.len_utf8();
|
|
Some(c)
|
|
}
|
|
|
|
fn peek_token(&self) -> Option<Self::Token> {
|
|
self.chars().next()
|
|
}
|
|
|
|
fn offset_for<P>(&self, predicate: P) -> Option<usize>
|
|
where
|
|
P: Fn(Self::Token) -> bool,
|
|
{
|
|
self.as_str().offset_for(predicate)
|
|
}
|
|
|
|
fn offset_at(&self, tokens: usize) -> Result<usize, winnow::error::Needed> {
|
|
self.as_str().offset_at(tokens)
|
|
}
|
|
|
|
fn next_slice(&mut self, offset: usize) -> Self::Slice {
|
|
let mut next = self.clone();
|
|
|
|
self.range.start += offset;
|
|
next.range.end = self.range.start;
|
|
|
|
next
|
|
}
|
|
|
|
fn peek_slice(&self, offset: usize) -> Self::Slice {
|
|
let mut next = self.clone();
|
|
next.range.end = self.range.start + offset;
|
|
next
|
|
}
|
|
|
|
fn checkpoint(&self) -> Self::Checkpoint {
|
|
NomoInputCheckpoint {
|
|
range: self.get_range(),
|
|
}
|
|
}
|
|
|
|
fn reset(&mut self, checkpoint: &Self::Checkpoint) {
|
|
self.range = checkpoint.range.clone();
|
|
}
|
|
|
|
fn raw(&self) -> &dyn core::fmt::Debug {
|
|
self
|
|
}
|
|
|
|
fn trace(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
f.write_str(self.as_str())
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use winnow::stream::Stream;
|
|
|
|
use crate::input::NomoInput;
|
|
|
|
#[test]
|
|
fn check_stream_impl() {
|
|
let mut stream = NomoInput::from("checking");
|
|
|
|
let checkpoint = stream.checkpoint();
|
|
|
|
assert_eq!(stream.peek_token(), Some('c'));
|
|
assert_eq!(stream.next_token(), Some('c'));
|
|
let next_slice = stream.next_slice(4);
|
|
assert_eq!(next_slice.as_str(), "heck");
|
|
assert_eq!(stream.peek_token(), Some('i'));
|
|
|
|
stream.reset(&checkpoint);
|
|
assert_eq!(stream.peek_token(), Some('c'));
|
|
|
|
let peek = stream.peek_slice(4);
|
|
assert_eq!(peek.as_str(), "chec");
|
|
|
|
let eof_offset = stream.eof_offset();
|
|
assert_eq!(eof_offset, 8);
|
|
}
|
|
}
|