use alloc::{collections::VecDeque, rc::Rc, vec::Vec};
use core::{
cell::RefCell,
fmt::Debug,
iter::{Fuse, Iterator},
};
use yap::{IntoTokens, TokenLocation, Tokens};
pub(crate) mod str_stream_tokens;
pub trait StreamTokensBuffer<Item>: Default {
fn drain_front(&mut self, n: usize);
fn push(&mut self, item: Item);
fn get(&self, idx: usize) -> Option<Item>;
}
impl<Item: core::clone::Clone> StreamTokensBuffer<Item> for VecDeque<Item> {
fn drain_front(&mut self, n: usize) {
if n > self.len() {
self.clear()
} else {
for _ in 0..n {
self.pop_front();
}
}
}
fn push(&mut self, item: Item) {
self.push_back(item)
}
fn get(&self, idx: usize) -> Option<Item> {
self.get(idx).cloned()
}
}
#[derive(Clone, Default, Debug, PartialEq, Eq)]
struct Buffer<Buf> {
oldest_elem_cursor: usize,
elements: Buf,
}
#[derive(Debug)]
pub struct StreamTokens<I, Buf>
where
I: Iterator,
{
iter: Fuse<I>,
cursor: usize,
buffer: Buffer<Buf>,
checkout: Rc<RefCell<Vec<usize>>>,
}
#[derive(Debug)]
pub struct StreamTokensLocation {
cursor: usize,
checkout: Rc<RefCell<Vec<usize>>>,
}
impl Clone for StreamTokensLocation {
fn clone(&self) -> Self {
let mut checkout = self.checkout.borrow_mut();
let idx = match checkout.binary_search(&self.cursor) {
Ok(x) | Err(x) => x,
};
checkout.insert(idx, self.cursor);
Self {
cursor: self.cursor,
checkout: Rc::clone(&self.checkout),
}
}
}
impl PartialEq for StreamTokensLocation {
fn eq(&self, other: &Self) -> bool {
self.cursor == other.cursor
}
}
impl Eq for StreamTokensLocation {}
impl Drop for StreamTokensLocation {
fn drop(&mut self) {
let mut checkout = self.checkout.borrow_mut();
let idx = checkout
.binary_search(&self.cursor)
.expect("missing entry for location in checkout");
checkout.remove(idx);
}
}
impl TokenLocation for StreamTokensLocation {
fn offset(&self) -> usize {
self.cursor
}
}
impl<I: Iterator, Buf: Default> StreamTokens<I, Buf> {
pub(crate) fn _new(iter: I) -> Self {
StreamTokens {
iter: iter.fuse(),
cursor: Default::default(),
buffer: Default::default(),
checkout: Default::default(),
}
}
}
impl<I: Iterator> StreamTokens<I, VecDeque<I::Item>>
where
I::Item: Clone,
{
pub fn new(iter: I) -> Self {
Self::_new(iter)
}
}
impl<I, Buffer> Tokens for StreamTokens<I, Buffer>
where
I: Iterator,
I::Item: Clone,
Buffer: StreamTokensBuffer<I::Item>,
{
type Item = I::Item;
type Location = StreamTokensLocation;
fn next(&mut self) -> Option<Self::Item> {
self.cursor += 1;
{
if let Some(val) = self
.buffer
.elements
.get(self.cursor - 1 - self.buffer.oldest_elem_cursor)
{
return Some(val);
}
}
let checkout = self.checkout.borrow();
{
let min = match checkout.first() {
Some(&x) => x.min(self.cursor),
None => self.cursor,
};
let delta = min - self.buffer.oldest_elem_cursor;
self.buffer.elements.drain_front(delta);
self.buffer.oldest_elem_cursor = min;
}
{
let next = self.iter.next()?;
if checkout.is_empty() {
Some(next)
} else {
self.buffer.elements.push(next.clone());
Some(next)
}
}
}
fn location(&self) -> Self::Location {
let mut checkout = self.checkout.borrow_mut();
match checkout.binary_search(&self.cursor) {
Ok(x) | Err(x) => checkout.insert(x, self.cursor),
};
StreamTokensLocation {
cursor: self.cursor,
checkout: Rc::clone(&self.checkout),
}
}
fn set_location(&mut self, location: Self::Location) {
self.cursor = location.cursor;
}
fn is_at_location(&self, location: &Self::Location) -> bool {
self.cursor == location.cursor
}
}
impl<I, Buf> IntoTokens<I::Item> for StreamTokens<I, Buf>
where
I: Iterator,
I::Item: Clone + core::fmt::Debug,
Buf: StreamTokensBuffer<I::Item>,
{
type Tokens = Self;
fn into_tokens(self) -> Self {
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn stream_tokens_sanity_check() {
let chars: &mut dyn Iterator<Item = char> = &mut "hello \n\t world".chars();
let mut tokens = StreamTokens::new(chars);
let loc = tokens.location();
assert!(tokens.tokens("hello".chars()));
tokens.set_location(loc.clone());
assert!(tokens.tokens("hello".chars()));
tokens.skip_while(|c| c.is_whitespace());
assert!(tokens.tokens("world".chars()));
tokens.set_location(loc);
assert!(tokens.tokens("hello \n\t world".chars()));
assert_eq!(None, tokens.next())
}
#[test]
fn str_stream_tokens_sanity_check() {
let chars: &mut dyn Iterator<Item = char> = &mut "hello \n\t world".chars();
let mut tokens = crate::StrStreamTokens::new(chars);
let loc = tokens.location();
assert!(tokens.tokens("hello".chars()));
tokens.set_location(loc.clone());
assert!(tokens.tokens("hello".chars()));
tokens.skip_while(|c| c.is_whitespace());
assert!(tokens.tokens("world".chars()));
tokens.set_location(loc);
assert!(tokens.tokens("hello \n\t world".chars()));
assert_eq!(None, tokens.next())
}
}