use super::{Tree, Location, Loc, Token, Stream};
pub struct Context<I: Stream> {
input: I,
stack: Vec<Loc<Box<dyn Tree>>>,
locs: Vec<Location>,
}
impl<I: Stream> Context<I> {
pub fn new(input: I) -> Self {
Self {input, stack: Vec::new(), locs: Vec::new()}
}
pub fn pop(&mut self) -> Location {
self.locs.pop().expect("No tokens have been read")
}
pub fn first(&self) -> Location {
*self.locs.first().expect("No tokens have been read")
}
pub fn last(&self) -> Location {
*self.locs.last().expect("No tokens have been read")
}
pub fn locate<T>(&self, value: T) -> Loc<T> { Loc(value, self.last()) }
pub fn drain(&mut self) -> Location {
let ret = Location {start: self.first().start, end: self.last().end};
self.locs.clear();
ret
}
fn read_inner(&mut self) -> Token {
if let Some(Loc(t, loc)) = self.stack.pop() {
Token::new(t, loc)
} else {
self.input.read()
}
}
pub fn read_any(&mut self) -> Result<Box<dyn Tree>, String> {
let token = self.read_inner();
self.locs.push(token.location());
token.result()
}
pub fn read<T: Tree>(&mut self) -> Result<Option<Box<T>>, String> {
Ok(match self.read_any()?.downcast::<T>() {
Ok(t) => Some(t),
Err(t) => { self.unread(t); None },
})
}
pub fn read_if<T: Tree>(
&mut self,
is_wanted: impl FnOnce(&T) -> bool,
) -> Result<Option<Box<T>>, String> {
Ok(self.read::<T>()?.and_then(
|t| if is_wanted(&*t) { Some(t) } else { self.unread(t); None }
))
}
pub fn unread(&mut self, tree: Box<dyn Tree>) {
let loc = self.pop();
self.stack.push(Loc(tree, loc));
}
}
pub trait Parse: Sized {
fn parse(
&self,
input: &mut Context<impl Stream>,
) -> Result<Box<dyn Tree>, String>;
fn parse_stream<I: Stream>(self, input: I) -> ParseStream<Self, I> {
ParseStream {parse: self, input: Context::new(input)}
}
}
pub struct ParseStream<P: Parse, I: Stream> {
parse: P,
input: Context<I>,
}
impl<P: Parse, I: Stream> Stream for ParseStream<P, I> {
fn read(&mut self) -> Token {
let ret = self.parse.parse(&mut self.input);
let last = self.input.last();
let all = self.input.drain();
let loc = if ret.is_ok() { all } else { last };
Token(Loc(ret, loc))
}
}