nomo/src/input.rs
Marcel Müller 8afc2d1bde Add parsing for conditionals (cont.)
Signed-off-by: Marcel Müller <neikos@neikos.email>
2026-03-08 15:06:29 +01:00

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);
}
}