use super::{
    ast::{Expr, Spanned},
    lexer::Token,
};
use chumsky::{prelude::*, Stream};
use logos::Logos;

/// Parse source string into a Expr
pub fn parse(src: &str) -> Result<Vec<Spanned<Expr>>, Vec<Simple<Token<'_>>>> {
    let lexer = Token::lexer(src);
    let len = lexer.source().len();
    parser().parse(Stream::from_iter(len..len + 1, lexer.spanned()))
}

fn parser<'a>() -> impl Parser<Token<'a>, Vec<Spanned<Expr<'a>>>, Error = Simple<Token<'a>>> {
    recursive(|expr| {
        let atom = select! {
            Token::Symbol(s) => Expr::Symbol(s.into()),
            Token::Keyword(k) => Expr::Keyword(k.into()),
            Token::String(s) => Expr::String(s.into()),
            Token::Number(n) => Expr::Number(n),
        };

        let list = expr
            .clone()
            .repeated()
            .delimited_by(just(Token::LeftParen), just(Token::RightParen));

        let vector = just(Token::Octothrope)
            .ignore_then(list.clone())
            .map(Expr::Vector);
            
        let list = list.map(Expr::List);

        let quote = just(Token::Quote)
            .ignore_then(expr)
            .map(Box::new)
            .map(Expr::Quote);

        atom.or(list)
            .or(vector)
            .or(quote)
            .map_with_span(Spanned::new)
    })
    .repeated()
    .then_ignore(end())
}

#[cfg(test)]
mod tests {
    use super::*;

    fn assert_parse<'a>(src: &'a str, expected: &'a [Spanned<Expr<'a>>]) {
        assert_eq!(parse(src).unwrap(), expected);
    }
}