diff --git a/src/lisp/environment.rs b/src/lisp/environment.rs index 913eff6..a798fd5 100644 --- a/src/lisp/environment.rs +++ b/src/lisp/environment.rs @@ -36,6 +36,12 @@ impl EnvironmentLayer { } } +impl From> for EnvironmentLayer { + fn from(map: HashMap) -> Self { + EnvironmentLayer { symbols: map } + } +} + impl<'a> Environment<'a> { /// Construct an empty `Environment`. pub fn new() -> Self { diff --git a/src/lisp/expression.rs b/src/lisp/expression.rs index 0cdff2a..3fed1a9 100644 --- a/src/lisp/expression.rs +++ b/src/lisp/expression.rs @@ -1,4 +1,5 @@ use super::environment::{Environment, EnvironmentLayer}; +use std::collections::HashMap; #[derive(Clone, Debug, PartialEq, PartialOrd)] /// A sum type of all possible lisp expressions. @@ -175,7 +176,10 @@ fn eval(env: &Environment, expr: Expression) -> Result { a => Err(EvalError::NotAFunction(a)), }, Expression::Quote(e) => Ok(*e), - Expression::Symbol(s) => env.get(&s).ok_or(EvalError::SymbolNotBound(s)).cloned(), + Expression::Symbol(s) => eval( + env, + env.get(&s).ok_or(EvalError::SymbolNotBound(s)).cloned()?, + ), x => Ok(x), } } @@ -207,6 +211,25 @@ fn prelude_lambda(_env: &Environment, expr: Expression) -> Result Result { + let [bindings, body] = expr.try_into()?; + + let bindings = CellIterator::new(bindings) + .map(|e| { + let (s, e) = e?.try_into()?; + if let Expression::Symbol(s) = s { + Ok((s, eval(env, e)?)) + } else { + Err(EvalError::ArgumentError( + "Let bindings must be an alist with elements (symbol . expr)".to_string(), + )) + } + }) + .collect::, EvalError>>()?; + + eval(&env.overlay(bindings.into()), body) +} + fn prelude_if(env: &Environment, expr: Expression) -> Result { let [predicate, e_then, e_else] = expr.try_into()?; @@ -260,6 +283,7 @@ pub fn eval_prelude(expr: Expression) -> Result { prelude.set("==".to_string(), Expression::Function(prelude_eq)); prelude.set("<".to_string(), Expression::Function(prelude_lt)); prelude.set(">".to_string(), Expression::Function(prelude_gt)); + prelude.set("let".to_string(), Expression::Function(prelude_let)); eval(&prelude, expr) } diff --git a/src/main.rs b/src/main.rs index 341aae7..dc61e2e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -38,8 +38,17 @@ fn main() { .into(), ] .into(), - Expression::Integer(4), - Expression::Integer(7), + Expression::Integer(5), + vec![ + Expression::Symbol("let".to_string()), + vec![Expression::Cell( + Box::new(Expression::Symbol("y".to_string())), + Box::new(Expression::Integer(7)), + )] + .into(), + Expression::Symbol("y".to_string()), + ] + .into(), ] .into();