use std::{
iter::{Peekable, Map, Enumerate},
rc::Rc, sync::Arc,
};
use crate::{
SourceStream, SourceIterItem, Text, TextBase,
text::chunk::{CharPos, PosStrish, RefCntStrish},
parser::{DatumAllocator, AllocError},
};
#[inline]
pub fn to_rc_box_str(s: String) -> Rc<Box<str>> {
Rc::new(s.into_boxed_str())
}
#[inline]
pub fn to_rc_string(s: String) -> Rc<String> {
Rc::new(s)
}
#[inline]
pub fn to_rc_str(s: String) -> Rc<str> {
s.into()
}
#[inline]
pub fn to_arc_box_str(s: String) -> Arc<Box<str>> {
Arc::new(s.into_boxed_str())
}
#[inline]
pub fn to_arc_string(s: String) -> Arc<String> {
Arc::new(s)
}
#[inline]
pub fn to_arc_str(s: String) -> Arc<str> {
s.into()
}
#[derive(Debug)]
pub struct CharIterSourceStream<CI, F, R>
where CI: Iterator<Item = char>,
F: Fn(String) -> R,
R: RefCntStrish,
{
pe_iter: PeekableSourceIterItemIter<CI>,
accum: Option<(String, CharPos)>,
to_refcnt_strish: F,
}
type PeekableSourceIterItemIter<CI> = Peekable<Map<Enumerate<CI>, MapFn>>;
type MapFn = fn((usize, char)) -> SourceIterItem<CharPos>;
impl<CI, F, R> CharIterSourceStream<CI, F, R>
where CI: Iterator<Item = char>,
F: Fn(String) -> R,
R: RefCntStrish,
{
pub fn new<I>(iter: I, to_refcnt_strish: F) -> Self
where I: IntoIterator<IntoIter = CI, Item = char>,
{
let map_fn: MapFn = |(pos, ch)| SourceIterItem{ch, pos: CharPos(pos)};
Self {
pe_iter: iter.into_iter()
.enumerate()
.map(map_fn)
.peekable(),
accum: None,
to_refcnt_strish,
}
}
}
impl<CI, F, R> Iterator for CharIterSourceStream<CI, F, R>
where CI: Iterator<Item = char>,
F: Fn(String) -> R,
R: RefCntStrish,
{
type Item = SourceIterItem<CharPos>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.accum = None;
self.pe_iter.next()
}
}
impl<CI, F, R, TT, DA> SourceStream<DA> for CharIterSourceStream<CI, F, R>
where CI: Iterator<Item = char>,
F: Fn(String) -> R,
R: RefCntStrish,
TT: Text<Pos = CharPos>,
TT::Chunk: From<PosStrish<R>>,
DA: DatumAllocator<TT = TT>, {
#[inline]
fn peek(&mut self) -> Option<&<Self as Iterator>::Item> {
self.pe_iter.peek()
}
fn next_accum(&mut self, _: &mut DA)
-> Result<Option<<Self as Iterator>::Item>, AllocError>
{
let next = self.pe_iter.next();
if let Some(SourceIterItem{ch, pos}) = next {
if let Some((s, _)) = &mut self.accum {
s.push(ch);
} else {
let mut s = String::new();
s.push(ch);
self.accum = Some((s, pos));
}
}
Ok(next)
}
fn accum_done(&mut self, _: &mut DA) -> Result<TT, AllocError> {
let ps = if let Some((s, pos)) = self.accum.take() {
PosStrish::new((self.to_refcnt_strish)(s), pos)
} else {
PosStrish::empty()
};
Ok(TT::from_chunkish(ps))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn iter() {
let mut ciss0 = CharIterSourceStream::new("".chars(), to_rc_string);
assert!(ciss0.next().is_none());
let ciss1 = CharIterSourceStream::new("a".chars(), to_arc_string);
assert_eq!(ciss1.collect::<Vec<_>>(),
vec![SourceIterItem{ch: 'a', pos: CharPos(0)}]);
let ciss2 = CharIterSourceStream::new("λb".chars(), to_rc_box_str);
assert_eq!(ciss2.collect::<Vec<_>>(),
vec![SourceIterItem{ch: 'λ', pos: CharPos(0)},
SourceIterItem{ch: 'b', pos: CharPos(1)}]);
let s = String::from("câ–· â–·");
let ciss3 = CharIterSourceStream::new(s.chars(), to_arc_box_str);
assert_eq!(ciss3.collect::<Vec<_>>(),
vec![SourceIterItem{ch: 'c', pos: CharPos(0)},
SourceIterItem{ch: 'â–·', pos: CharPos(1)},
SourceIterItem{ch: ' ', pos: CharPos(2)},
SourceIterItem{ch: 'â–·', pos: CharPos(3)}]);
let ciss4 = CharIterSourceStream::new(" 1 ".chars().chain("23".chars()),
to_rc_str);
assert_eq!(ciss4.collect::<Vec<_>>(),
vec![SourceIterItem{ch: ' ', pos: CharPos(0)},
SourceIterItem{ch: '1', pos: CharPos(1)},
SourceIterItem{ch: ' ', pos: CharPos(2)},
SourceIterItem{ch: '2', pos: CharPos(3)},
SourceIterItem{ch: '3', pos: CharPos(4)}]);
let ciss5 = CharIterSourceStream::new(
(0..4321).map(|n| if n % 2 == 0 { 'λ' } else { '-' }),
to_arc_str);
assert_eq!(ciss5.collect::<Vec<_>>(),
(0..4321)
.map(|n| SourceIterItem{ch: if n % 2 == 0 { 'λ' } else { '-' },
pos: CharPos(n)})
.collect::<Vec<_>>());
}
#[test]
#[allow(clippy::cyclomatic_complexity)]
fn source_stream() {
use std::marker::PhantomData;
use crate::{text::TextVec, Datum, datum::DatumBox};
struct DummyDA<R>(PhantomData<R>);
impl<R> DatumAllocator for DummyDA<R>
where R: RefCntStrish,
{
type TT = TextVec<PosStrish<R>>;
type ET = ();
type DR = DatumBox<Self::TT, Self::ET>;
fn new_datum(&mut self, _from: Datum<Self::TT, Self::ET, Self::DR>)
-> Result<Self::DR, AllocError> {
unreachable!()
}
}
fn txt_to_chunks<R>(t: &TextVec<PosStrish<R>>) -> Vec<(&str, usize)>
where R: RefCntStrish,
{
use crate::Text;
t.iter_chunks().map(|c| (c.val.as_ref(), c.pos.0)).collect::<Vec<_>>()
}
let dda_rc_string: &mut DummyDA<Rc<String>> = &mut DummyDA(PhantomData);
let dda_arc_box_str: &mut DummyDA<Arc<Box<str>>> = &mut DummyDA(PhantomData);
let dda_rc_str: &mut DummyDA<Rc<str>> = &mut DummyDA(PhantomData);
let dda_arc_string: &mut DummyDA<Arc<String>> = &mut DummyDA(PhantomData);
let mut ciss0 = CharIterSourceStream::new("".chars(), to_rc_string);
assert_eq!(SourceStream::<DummyDA<_>>::peek(&mut ciss0), None);
assert_eq!(ciss0.next_accum(dda_rc_string), Ok(None));
assert_eq!(ciss0.accum_done(dda_rc_string).map(|t| t.is_empty()), Ok(true));
let mut ciss1 = CharIterSourceStream::new("Z".chars(), to_arc_box_str);
assert_eq!(SourceStream::<DummyDA<_>>::peek(&mut ciss1),
Some(&SourceIterItem{ch: 'Z', pos: CharPos(0)}));
assert_eq!(ciss1.next_accum(dda_arc_box_str),
Ok(Some(SourceIterItem{ch: 'Z', pos: CharPos(0)})));
assert_eq!(SourceStream::<DummyDA<_>>::peek(&mut ciss1), None);
assert_eq!(ciss1.next_accum(dda_arc_box_str), Ok(None));
assert_eq!(ciss1.accum_done(dda_arc_box_str).as_ref().map(txt_to_chunks),
Ok(vec![("Z", 0)]));
let mut ciss2 = CharIterSourceStream::new(r"y\\x {\}}".chars(), to_rc_str);
assert_eq!(SourceStream::<DummyDA<_>>::peek(&mut ciss2),
Some(&SourceIterItem{ch: 'y', pos: CharPos(0)}));
assert_eq!(ciss2.next_accum(dda_rc_str),
Ok(Some(SourceIterItem{ch: 'y', pos: CharPos(0)})));
assert_eq!(SourceStream::<DummyDA<_>>::peek(&mut ciss2),
Some(&SourceIterItem{ch: '\\', pos: CharPos(1)}));
assert_eq!(ciss2.accum_done(dda_rc_str).as_ref().map(txt_to_chunks),
Ok(vec![("y", 0)]));
assert_eq!(ciss2.next(), Some(SourceIterItem{ch: '\\', pos: CharPos(1)}));
assert_eq!(ciss2.next_accum(dda_rc_str),
Ok(Some(SourceIterItem{ch: '\\', pos: CharPos(2)})));
assert_eq!(SourceStream::<DummyDA<_>>::peek(&mut ciss2),
Some(&SourceIterItem{ch: 'x', pos: CharPos(3)}));
assert_eq!(ciss2.next_accum(dda_rc_str),
Ok(Some(SourceIterItem{ch: 'x', pos: CharPos(3)})));
assert_eq!(SourceStream::<DummyDA<_>>::peek(&mut ciss2),
Some(&SourceIterItem{ch: ' ', pos: CharPos(4)}));
assert_eq!(ciss2.next_accum(dda_rc_str),
Ok(Some(SourceIterItem{ch: ' ', pos: CharPos(4)})));
assert_eq!(SourceStream::<DummyDA<_>>::peek(&mut ciss2),
Some(&SourceIterItem{ch: '{', pos: CharPos(5)}));
assert_eq!(ciss2.accum_done(dda_rc_str).as_ref().map(txt_to_chunks),
Ok(vec![(r"\x ", 2)]));
assert_eq!(ciss2.next(), Some(SourceIterItem{ch: '{', pos: CharPos(5)}));
assert_eq!(SourceStream::<DummyDA<_>>::peek(&mut ciss2),
Some(&SourceIterItem{ch: '\\', pos: CharPos(6)}));
assert_eq!(ciss2.accum_done(dda_rc_str).as_ref().map(txt_to_chunks),
Ok(vec![("", 0)]));
assert_eq!(ciss2.next(), Some(SourceIterItem{ch: '\\', pos: CharPos(6)}));
assert_eq!(ciss2.next_accum(dda_rc_str),
Ok(Some(SourceIterItem{ch: '}', pos: CharPos(7)})));
assert_eq!(SourceStream::<DummyDA<_>>::peek(&mut ciss2),
Some(&SourceIterItem{ch: '}', pos: CharPos(8)}));
assert_eq!(ciss2.accum_done(dda_rc_str).as_ref().map(txt_to_chunks),
Ok(vec![("}", 7)]));
assert_eq!(ciss2.next(), Some(SourceIterItem{ch: '}', pos: CharPos(8)}));
assert_eq!(SourceStream::<DummyDA<_>>::peek(&mut ciss2), None);
assert_eq!(ciss2.accum_done(dda_rc_str).as_ref().map(txt_to_chunks),
Ok(vec![("", 0)]));
assert_eq!(ciss2.next_accum(dda_rc_str), Ok(None));
assert_eq!(ciss2.accum_done(dda_rc_str).as_ref().map(txt_to_chunks),
Ok(vec![("", 0)]));
assert_eq!(ciss2.next(), None);
assert_eq!(SourceStream::<DummyDA<_>>::peek(&mut ciss2), None);
let mut ciss3 = CharIterSourceStream::new("wVu".chars(), to_arc_string);
assert_eq!(ciss3.next_accum(dda_arc_string),
Ok(Some(SourceIterItem{ch: 'w', pos: CharPos(0)})));
assert_eq!(ciss3.next_accum(dda_arc_string),
Ok(Some(SourceIterItem{ch: 'V', pos: CharPos(1)})));
assert_eq!(ciss3.next(), Some(SourceIterItem{ch: 'u', pos: CharPos(2)}));
assert_eq!(ciss3.accum_done(dda_arc_string).as_ref().map(txt_to_chunks),
Ok(vec![("", 0)]));
assert_eq!(SourceStream::<DummyDA<_>>::peek(&mut ciss3), None);
assert_eq!(ciss3.next_accum(dda_arc_string), Ok(None));
}
}