1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
//! Contains the functions used to evaluate an AST.

use crate::ast::{Ast, LispAtom};
use crate::env::Environment;
use crate::error::LispError;
use crate::special_forms::{eval_special_form, SPECIAL_FORMS};

/// Evaluate a lisp expression.
pub fn eval_expr(input: Ast, env: &mut Environment) -> Result<Ast, LispError> {
    match input {
        Ast::List(list) => match list.get(0) {
            Some(Ast::Atom(LispAtom::Symbol(symbol))) => {
                if let Some(special_form) = SPECIAL_FORMS.get(symbol.as_str()) {
                    eval_special_form(list, env, special_form)
                } else {
                    eval_list(list, env)
                }
            }
            _ => eval_list(list, env),
        },
        Ast::Atom(LispAtom::Symbol(symbol)) => eval_symbol(&symbol, env), // Symbols get looked up in environment
        _ => Ok(input), // Atoms and functions return themselves
    }
}

fn eval_list(list: Vec<Ast>, env: &mut Environment) -> Result<Ast, LispError> {
    // eval first item of list
    // should be Ast::Function
    // call the function on rest(list)
    let mut list = list.into_iter();

    let func = list
        .next()
        .ok_or(LispError::Type)
        .and_then(|ast| eval_expr(ast, env))?;

    let args: Vec<Ast> = list
        .map(|ast| eval_expr(ast, env))
        .collect::<Result<_, LispError>>()?;

    if let Ast::Function(func) = func {
        func.arity().check_arity(args.len())?;
        func.call(args, env)
    } else {
        Err(LispError::Type)
    }
}

fn eval_symbol(symbol: &str, env: &mut Environment) -> Result<Ast, LispError> {
    // Look up symbol in environment
    env.get(symbol).ok_or(LispError::Type)
}