lispers/src/lisp/eval.rs

95 lines
2.9 KiB
Rust

use super::environment::Environment;
use super::environment::EnvironmentLayer;
use super::expression::Expression;
#[derive(Debug)]
/// All possible evaluation errors
pub enum EvalError {
SymbolNotBound(String),
NotAFunction(Expression),
NotANumber(Expression),
ArgumentError(String),
TypeError(String),
NotASymbol(Expression),
}
/// A CellIterator is a convenience struct to iterate a linked cons list.
/// The Iterator returns Ok(Expression) as long, as there are elements in the list.
/// Err(EvalError) is returned when the right side of a cons cell is not another cons cell or nil.
pub struct CellIterator {
expr: Option<Expression>,
}
impl CellIterator {
pub fn new(expr: Expression) -> CellIterator {
CellIterator { expr: Some(expr) }
}
}
impl Iterator for CellIterator {
type Item = Result<Expression, EvalError>;
fn next(&mut self) -> Option<Self::Item> {
if let Some(expr) = self.expr.take() {
match expr {
Expression::Cell(head, tail) => {
self.expr = Some(*tail);
return Some(Ok(*head));
}
Expression::Nil => {
return None;
}
_ => {
return Some(Err(EvalError::TypeError(
"Expected a cell or nil".to_string(),
)));
}
}
} else {
None
}
}
}
/// Dispatch an anonymous function call. Evaluates `body` in `env`, binding `args` to `argument_symbols`
fn dispatch_anonymous_function(
env: &Environment,
argument_symbols: Vec<String>,
body: Expression,
args: Expression,
) -> Result<Expression, EvalError> {
let mut args: Vec<Expression> = args.try_into()?;
let mut overlay = EnvironmentLayer::new();
if args.len() != argument_symbols.len() {
return Err(EvalError::ArgumentError(format!(
"Exprected {} arguments, got {}",
argument_symbols.len(),
args.len()
)));
}
for (arg, symbol) in args.iter_mut().zip(argument_symbols.iter()) {
overlay.set(symbol.to_owned(), arg.to_owned());
}
eval(&env.overlay(overlay), body)
}
/// Evaluate an expression inside an environment
pub fn eval(env: &Environment, expr: Expression) -> Result<Expression, EvalError> {
match expr {
Expression::Cell(lhs, rhs) => match eval(env, *lhs)? {
Expression::Function(f) => f(env, *rhs),
Expression::AnonymousFunction {
argument_symbols,
body,
} => dispatch_anonymous_function(env, argument_symbols, *body, *rhs),
a => Err(EvalError::NotAFunction(a)),
},
Expression::Quote(e) => Ok(*e),
Expression::Symbol(s) => eval(env, env.get(&s).ok_or(EvalError::SymbolNotBound(s))?),
x => Ok(x),
}
}