use super::{Tree, EndOfFile, Location, Token, Stream};
pub const MISSING_OPEN: &'static str = "Unmatched close bracket";
pub const MISSING_CLOSE: &'static str = "Unmatched open bracket";
#[derive(Debug)]
pub struct Round(pub Vec<Token>);
impl Round {
pub fn new(contents: Vec<Token>) -> Box<Self> { Box::new(Self(contents)) }
}
impl Tree for Round {}
#[derive(Debug)]
pub struct Square(pub Vec<Token>);
impl Square {
pub fn new(contents: Vec<Token>) -> Box<Self> { Box::new(Self(contents)) }
}
impl Tree for Square {}
#[derive(Debug)]
pub struct Brace(pub Vec<Token>);
impl Brace {
pub fn new(contents: Vec<Token>) -> Box<Self> { Box::new(Self(contents)) }
}
impl Tree for Brace {}
pub struct Brackets<F, I> {
open: char,
close: char,
new_bracket: F,
input: I,
depth: usize
}
impl<
F: Fn(Vec<Token>) -> Box<dyn Tree>,
I: Stream,
> Brackets<F, I> {
pub fn new(open: char, close: char, new_bracket: F, input: I) -> Self {
Self {open, close, new_bracket, input, depth: 0}
}
fn parse_bracket(&mut self, open_loc: Location) -> Token {
let mut contents: Vec<Token> = Vec::new();
loop {
let token = self.read();
if token.is_incomplete() { return token; }
if token == EndOfFile { return Token::new_err(MISSING_CLOSE, open_loc); }
if token == self.close {
let close_loc = token.location();
let bracket = (&self.new_bracket)(contents);
return Token::new(bracket, Location {start: open_loc.start, end: close_loc.end});
}
contents.push(token);
}
}
}
impl<
F: Fn(Vec<Token>) -> Box<dyn Tree>,
I: Stream,
> Stream for Brackets<F, I> {
fn read(&mut self) -> Token {
let token = self.input.read();
if token == self.open {
self.depth += 1;
return self.parse_bracket(token.location());
}
if token == self.close {
if self.depth == 0 {
return Token::new_err(MISSING_OPEN, token.location());
}
self.depth -= 1;
}
token
}
}
#[cfg(test)]
mod tests {
use crate::{Characters};
use super::*;
#[test]
fn some_brackets() {
let mut parser = Brackets::new(
'(',
')',
|contents| Box::new(Round(contents)),
Brackets::new(
'{',
'}',
|contents| Box::new(Brace(contents)),
Characters::new("(a{b}(cd)){}", true)
)
);
let mut contents1 = parser.read().unwrap::<Round>().0.into_iter();
assert_eq!(contents1.read(), 'a');
let mut contents2 = contents1.read().unwrap::<Brace>().0.into_iter();
assert_eq!(contents2.read(), 'b');
assert_eq!(contents2.read(), EndOfFile);
let mut contents2 = contents1.read().unwrap::<Round>().0.into_iter();
assert_eq!(contents2.read(), 'c');
assert_eq!(contents2.read(), 'd');
assert_eq!(contents2.read(), EndOfFile);
assert_eq!(contents1.read(), EndOfFile);
let mut contents1 = parser.read().unwrap::<Brace>().0.into_iter();
assert_eq!(contents1.read(), EndOfFile);
assert_eq!(parser.read(), EndOfFile);
}
#[test]
fn inside_out() {
fn noop(parser: impl Stream) -> impl Stream { parser }
fn round(parser: impl Stream) -> impl Stream {
noop(Brackets::new('(', ')', |contents| {
let contents = noop(contents.into_iter()).read_all();
Box::new(Round(contents))
}, parser))
}
fn square(parser: impl Stream) -> impl Stream {
round(Brackets::new('[', ']', |contents| {
let contents = round(contents.into_iter()).read_all();
Box::new(Square(contents))
}, parser))
}
fn brace(parser: impl Stream) -> impl Stream {
square(Brackets::new('{', '}', |contents| {
let contents = square(contents.into_iter()).read_all();
Box::new(Brace(contents))
}, parser))
}
let mut parser = brace(Characters::new("a{b[c(d(e[f{g}]))]}", true));
assert_eq!(parser.read(), 'a');
let mut contents1 = parser.read().unwrap::<Brace>().0.into_iter();
assert_eq!(contents1.read(), 'b');
let mut contents2 = contents1.read().unwrap::<Square>().0.into_iter();
assert_eq!(contents2.read(), 'c');
let mut contents3 = contents2.read().unwrap::<Round>().0.into_iter();
assert_eq!(contents3.read(), 'd');
let mut contents4 = contents3.read().unwrap::<Round>().0.into_iter();
assert_eq!(contents4.read(), 'e');
let mut contents5 = contents4.read().unwrap::<Square>().0.into_iter();
assert_eq!(contents5.read(), 'f');
let mut contents6 = contents5.read().unwrap::<Brace>().0.into_iter();
assert_eq!(contents6.read(), 'g');
assert_eq!(contents6.read(), EndOfFile);
assert_eq!(contents5.read(), EndOfFile);
assert_eq!(contents4.read(), EndOfFile);
assert_eq!(contents3.read(), EndOfFile);
assert_eq!(contents2.read(), EndOfFile);
assert_eq!(contents1.read(), EndOfFile);
assert_eq!(parser.read(), EndOfFile);
}
}