use std::collections::VecDeque;
#[derive(Deref, Debug)]
#[repr(transparent)]
struct Buf(VecDeque<char>);
impl Buf {
fn new(max_len: usize) -> Self {
Self(VecDeque::with_capacity(max_len))
}
fn is_full(&self) -> bool {
self.len() == self.capacity()
}
fn fill_up(&mut self, iter: &mut impl Iterator<Item = char>) {
while !self.is_full() {
match iter.next() {
None => break,
Some(x) => self.0.push_back(x),
}
}
}
fn matches(&self, pat: &str) -> bool {
self.iter().take(pat.len()).copied().eq(pat.chars())
}
fn pop_front(&mut self) -> char {
self.0.pop_front().unwrap()
}
fn pop_front_n(&mut self, n: usize) {
let _ = self.0.drain(..n);
}
}
#[derive(Debug)]
enum Tription<T> {
Some(T),
None,
Wait,
}
impl<T> From<Option<T>> for Tription<T> {
fn from(o: Option<T>) -> Self {
match o {
Some(t) => Tription::Some(t),
None => Tription::None,
}
}
}
#[derive(Copy, Clone, Debug)]
pub struct Comment {
pub(crate) open_pat: &'static str,
pub(crate) close_pat: &'static str,
pub(crate) nests: bool,
pub(crate) keep_close_pat: bool, pub(crate) allow_close_pat: bool, }
pub struct WithoutComments<I: Iterator<Item = char>> {
iter: I,
buf: Buf,
comments: Box<[Comment]>,
state: Option<(usize, Option<usize>)>,
}
impl<I: Iterator<Item = char>> WithoutComments<I> {
fn new(iter: I, comments: Box<[Comment]>, buf_len: usize) -> Self {
Self {
iter,
buf: Buf::new(buf_len),
comments,
state: None,
}
}
fn next_(&mut self) -> Tription<char> {
self.buf.fill_up(&mut self.iter);
if self.buf.is_empty() {
return Tription::None;
}
if let Some((idx, ref mut nesting)) = self.state {
let comment = &self.comments[idx];
let &Comment {
open_pat,
close_pat,
keep_close_pat,
..
} = comment;
if self.buf.matches(close_pat) {
if !keep_close_pat {
self.buf.pop_front_n(close_pat.len());
}
match nesting {
None | Some(0) => self.state = None,
Some(d) => *d -= 1,
}
} else if let Some(depth) = nesting {
if self.buf.matches(open_pat) {
self.buf.pop_front_n(open_pat.len());
*depth += 1;
} else {
self.buf.pop_front();
}
} else {
self.buf.pop_front();
}
Tription::Wait
} else {
for (idx, comment) in self.comments.iter().enumerate() {
let Comment {
open_pat,
close_pat,
nests,
allow_close_pat,
..
} = comment;
if self.buf.matches(open_pat) {
self.buf.pop_front_n(open_pat.len());
let nesting = match nests {
true => Some(0),
false => None,
};
self.state = Some((idx, nesting));
return Tription::Wait;
} else if !allow_close_pat && self.buf.matches(close_pat) {
panic!("Got \"{}\" without matching \"{}\"", close_pat, open_pat)
}
}
Tription::Some(self.buf.pop_front())
}
}
}
impl<I: Iterator<Item = char>> Iterator for WithoutComments<I> {
type Item = char;
fn next(&mut self) -> Option<Self::Item> {
loop {
match self.next_() {
Tription::None => return None,
Tription::Some(c) => return Some(c),
Tription::Wait => (),
}
}
}
}
pub trait IntoWithoutComments
where
Self: Sized + Iterator<Item = char>,
{
fn without_comments(self, language: Box<[Comment]>) -> WithoutComments<Self> {
let mut buf_len = 0;
for &Comment {
open_pat,
close_pat,
..
} in language.iter()
{
if open_pat.len() > buf_len {
buf_len = open_pat.len()
}
if close_pat.len() > buf_len {
buf_len = close_pat.len()
}
}
assert_ne!(buf_len, 0);
WithoutComments::new(self, language, buf_len)
}
}
impl<I: Iterator<Item = char>> IntoWithoutComments for I {}