extern crate pest;

use std::io::{self, Write};

use pest::error::Error;
use pest::iterators::Pairs;
use pest::{state, ParseResult, Parser, ParserState};

#[allow(dead_code, non_camel_case_types)]
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
enum Rule {
    expr,
    paren,
    paren_end,
}

struct ParenParser;

impl Parser<Rule> for ParenParser {
    fn parse(rule: Rule, input: &str) -> Result<Pairs<Rule>, Error<Rule>> {
        fn expr(state: Box<ParserState<Rule>>) -> ParseResult<Box<ParserState<Rule>>> {
            state.sequence(|s| s.repeat(paren).and_then(|s| s.end_of_input()))
        }

        fn paren(state: Box<ParserState<Rule>>) -> ParseResult<Box<ParserState<Rule>>> {
            state.rule(Rule::paren, |s| {
                s.sequence(|s| {
                    s.match_string("(")
                        .and_then(|s| {
                            s.optional(|s| {
                                s.sequence(|s| {
                                    s.lookahead(true, |s| s.match_string("("))
                                        .and_then(|s| s.repeat(paren))
                                })
                            })
                        })
                        .and_then(|s| s.rule(Rule::paren_end, |s| s.match_string(")")))
                })
            })
        }

        state(input, |state| match rule {
            Rule::expr => expr(state),
            Rule::paren => paren(state),
            _ => unreachable!(),
        })
    }
}

#[derive(Debug)]
struct Paren(Vec<Paren>);

fn expr(pairs: Pairs<Rule>) -> Vec<Paren> {
    pairs
        .filter(|p| p.as_rule() == Rule::paren)
        .map(|p| Paren(expr(p.into_inner())))
        .collect()
}

fn main() {
    loop {
        let mut line = String::new();

        print!("> ");
        io::stdout().flush().unwrap();

        io::stdin().read_line(&mut line).unwrap();
        line.pop();

        match ParenParser::parse(Rule::expr, &line) {
            Ok(pairs) => println!("{:?}", expr(pairs)),
            Err(e) => println!("\n{}", e),
        };
    }
}
