use crate::lexer::*;
use crate::object::*;
use std::error::Error;
use std::fmt;
use std::rc::Rc;
#[derive(Debug)]
pub struct ParseError {
err: String,
}
impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Parse error: {}", self.err)
}
}
impl Error for ParseError {}
pub fn parse(program: &str) -> Result<Object, ParseError> {
let token_result = tokenize(program);
if token_result.is_err() {
return Err(ParseError {
err: format!("{}", token_result.err().unwrap()),
});
}
let mut tokens = token_result
.unwrap()
.into_iter()
.rev()
.collect::<Vec<_>>();
let parsed_list = parse_list(&mut tokens)?;
Ok(parsed_list)
}
fn parse_list(
tokens: &mut Vec<Token>,
) -> Result<Object, ParseError> {
let token = tokens.pop();
if token != Some(Token::LParen) {
return Err(ParseError {
err: format!("Expected LParen, found {:?}", token),
});
}
let mut list: Vec<Object> = Vec::new();
while !tokens.is_empty() {
let token = tokens.pop();
if token.is_none() {
return Err(ParseError {
err: "Did not find enough tokens".to_string(),
});
}
let t = token.unwrap();
match t {
Token::Keyword(k) => list.push(Object::Keyword(k)),
Token::BinaryOp(b) => list.push(Object::BinaryOp(b)),
Token::Integer(n) => list.push(Object::Integer(n)),
Token::Float(f) => list.push(Object::Float(f)),
Token::String(s) => list.push(Object::String(s)),
Token::Symbol(s) => list.push(Object::Symbol(s)),
Token::LParen => {
tokens.push(Token::LParen);
let sub_list = parse_list(tokens)?;
list.push(sub_list);
}
Token::RParen => {
return Ok(Object::List(Rc::new(list)));
}
}
}
Ok(Object::List(Rc::new(list)))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_add() {
let list = parse("(+ 1 2)").unwrap();
assert_eq!(
list,
Object::List(Rc::new(vec![
Object::BinaryOp("+".to_string()),
Object::Integer(1),
Object::Integer(2),
]))
);
}
#[test]
fn test_area_of_a_circle() {
let program = "(
(define r 10)
(define pi 314)
(* pi (* r r))
)";
let list = parse(program).unwrap();
assert_eq!(
list,
Object::List(Rc::new(vec![
Object::List(Rc::new(vec![
Object::Keyword("define".to_string()),
Object::Symbol("r".to_string()),
Object::Integer(10),
])),
Object::List(Rc::new(vec![
Object::Keyword("define".to_string()),
Object::Symbol("pi".to_string()),
Object::Integer(314),
])),
Object::List(Rc::new(vec![
Object::BinaryOp("*".to_string()),
Object::Symbol("pi".to_string()),
Object::List(Rc::new(vec![
Object::BinaryOp("*".to_string()),
Object::Symbol("r".to_string()),
Object::Symbol("r".to_string()),
])),
])),
]))
);
}
}