diff --git a/lispers-core/src/lisp/environment.rs b/lispers-core/src/lisp/environment.rs index 10a4cce..4d6fd70 100644 --- a/lispers-core/src/lisp/environment.rs +++ b/lispers-core/src/lisp/environment.rs @@ -1,5 +1,5 @@ use super::{expression::Expression, prelude::mk_prelude}; -use std::{cell::RefCell, collections::HashMap, rc::Rc}; +use std::{cell::RefCell, collections::HashMap, fmt::Display, rc::Rc}; #[derive(PartialEq, Clone, Debug)] /// A Environment is a stack of `EnvironmentLayer`s. Each `EnvironmentLayer` is a mapping from @@ -136,6 +136,30 @@ impl Default for Environment<'_> { } } +impl Display for Environment<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(x) = &self.outer { + write!(f, "{}<{}", self.layer, x) + } else { + write!(f, "") + } + } +} + +impl Display for EnvironmentLayer { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{{{}}}", + self.symbols + .iter() + .map(|(k, v)| format!("{}={}", k, v)) + .collect::>() + .join(", ") + ) + } +} + #[test] fn test_environment() { let mut env = Environment::new(); diff --git a/lispers-core/src/lisp/eval.rs b/lispers-core/src/lisp/eval.rs index 2871735..eb45348 100644 --- a/lispers-core/src/lisp/eval.rs +++ b/lispers-core/src/lisp/eval.rs @@ -81,6 +81,7 @@ impl Iterator for CellIterator { fn dispatch_anonymous_function( env: &Environment, argument_symbols: Vec, + capture: EnvironmentLayer, body: Expression, args: Expression, ) -> Result { @@ -97,23 +98,33 @@ fn dispatch_anonymous_function( } for (arg, symbol) in args.iter_mut().zip(argument_symbols.iter()) { - overlay.set(symbol.to_owned(), eval(env, arg.to_owned())?); + overlay.set(symbol.to_owned(), eval(&env, arg.to_owned())?); } - eval(&env.overlay(overlay), body) + eval(&env.overlay(capture).overlay(overlay), body) +} + +/// Dispatch a function inside an environment +fn dispatch_function( + env: &Environment, + f: Expression, + args: Expression, +) -> Result { + match f { + Expression::Function(f) => f(env, args), + Expression::AnonymousFunction { + argument_symbols, + capture, + body, + } => dispatch_anonymous_function(env, argument_symbols, capture, *body, args), + a => Err(EvalError::NotAFunction(a)), + } } /// Evaluate an expression inside an environment pub fn eval(env: &Environment, expr: Expression) -> Result { 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::Cell(lhs, rhs) => dispatch_function(env, eval(env, *lhs)?, *rhs), Expression::Quote(e) => Ok(*e), Expression::Symbol(s) => env.get(&s).ok_or(EvalError::SymbolNotBound(s)), x => Ok(x), diff --git a/lispers-core/src/lisp/expression.rs b/lispers-core/src/lisp/expression.rs index 67883cf..e8c6bae 100644 --- a/lispers-core/src/lisp/expression.rs +++ b/lispers-core/src/lisp/expression.rs @@ -1,3 +1,4 @@ +use std::any::type_name; use std::any::Any; use std::fmt::Debug; use std::fmt::Display; @@ -7,6 +8,7 @@ use std::ops::DerefMut; use as_any::AsAny; use super::environment::Environment; +use super::environment::EnvironmentLayer; use super::eval::CellIterator; use super::eval::EvalError; @@ -131,6 +133,7 @@ pub enum Expression { /// A anonymous function expression consisting of bound symbols and a body expression. AnonymousFunction { argument_symbols: Vec, + capture: EnvironmentLayer, body: Box, }, /// A foreign data expression. @@ -159,17 +162,25 @@ impl PartialEq for Expression { ( AnonymousFunction { argument_symbols: args1, + capture: capture1, body: body1, }, AnonymousFunction { argument_symbols: args2, + capture: capture2, body: body2, }, - ) => PartialEq::eq(args1, args2) && PartialEq::eq(body1, body2), + ) => { + PartialEq::eq(args1, args2) + && PartialEq::eq(body1, body2) + && PartialEq::eq(capture1, capture2) + } (ForeignExpression(f1), ForeignExpression(f2)) => PartialEq::eq(f1, f2), (Quote(e1), Quote(e2)) => PartialEq::eq(e1, e2), (Symbol(s1), Symbol(s2)) => PartialEq::eq(s1, s2), + (Integer(i1), Integer(i2)) => PartialEq::eq(i1, i2), (Float(f1), Float(f2)) => PartialEq::eq(f1, f2), + (String(s1), String(s2)) => PartialEq::eq(s1, s2), (Nil, Nil) => true, (True, True) => true, _ => false, @@ -185,10 +196,12 @@ impl PartialOrd for Expression { ( AnonymousFunction { argument_symbols: args1, + capture: _, body: body1, }, AnonymousFunction { argument_symbols: args2, + capture: _, body: body2, }, ) => args1 @@ -198,6 +211,8 @@ impl PartialOrd for Expression { (Quote(e1), Quote(e2)) => e1.partial_cmp(e2), (Symbol(s1), Symbol(s2)) => s1.partial_cmp(s2), (Float(f1), Float(f2)) => f1.partial_cmp(f2), + (Integer(i1), Integer(i2)) => i1.partial_cmp(i2), + (String(s1), String(s2)) => s1.partial_cmp(s2), (Nil, Nil) => Some(std::cmp::Ordering::Equal), (True, True) => Some(std::cmp::Ordering::Equal), _ => None, @@ -217,13 +232,16 @@ impl TryFrom for ForeignDataWrapper { match value { Expression::ForeignExpression(f) => match f.as_any_box().downcast::() { Ok(data) => Ok(ForeignDataWrapper(data)), - Err(_) => Err(EvalError::TypeError( - "Expression is not a ForeignDataWrapper".to_string(), - )), + Err(a) => Err(EvalError::TypeError(format!( + "Expression (type={}) is not a ForeignDataWrapper of type {}", + a.type_name(), + type_name::() + ))), }, - _ => Err(EvalError::TypeError( - "Expression is not a ForeignDataWrapper".to_string(), - )), + e => Err(EvalError::TypeError(format!( + "Expression ({}) is not a ForeignDataWrapper", + e + ))), } } } @@ -409,8 +427,15 @@ impl Display for Expression { Expression::Function(_) => write!(f, ""), Expression::AnonymousFunction { argument_symbols, + capture, body, - } => write!(f, "(lambda ({}) {})", argument_symbols.join(" "), body), + } => write!( + f, + "(lambda (capture={}) ({}) {})", + capture, + argument_symbols.join(" "), + body + ), Expression::Quote(e) => write!(f, "'{}", e), Expression::Symbol(s) => write!(f, "{}", s), Expression::Integer(i) => write!(f, "{}", i), diff --git a/lispers-core/src/lisp/prelude.rs b/lispers-core/src/lisp/prelude.rs index e31628f..754d5d8 100644 --- a/lispers-core/src/lisp/prelude.rs +++ b/lispers-core/src/lisp/prelude.rs @@ -8,6 +8,7 @@ use super::eval::CellIterator; use super::eval::EvalError; use super::expression::Expression; use std::collections::HashMap; +use std::collections::VecDeque; use std::path::PathBuf; pub fn prelude_add(env: &Environment, expr: Expression) -> Result { @@ -82,7 +83,33 @@ pub fn prelude_div(env: &Environment, expr: Expression) -> Result Result { +fn capture_variables(env: &Environment, expr: &Expression) -> Result { + let mut capture: HashMap = HashMap::new(); + let mut q = VecDeque::new(); + q.push_back(expr); + + while let Some(expr) = q.pop_front() { + match expr { + Expression::Symbol(s) => { + if let Some(expr) = env.get(&s) { + capture.insert(s.clone(), expr); + } + } + Expression::Cell(x, xs) => { + q.push_back(&*x); + q.push_back(&*xs); + } + Expression::Quote(x) => { + q.push_back(&*x); + } + _ => {} + } + } + + Ok(capture.into()) +} + +pub fn prelude_lambda(env: &Environment, expr: Expression) -> Result { let [args, body]: [Expression; 2] = expr.try_into()?; let mut arg_exprs: Vec = args.try_into()?; let argument_symbols: Vec = arg_exprs @@ -92,8 +119,10 @@ pub fn prelude_lambda(_env: &Environment, expr: Expression) -> Result Err(EvalError::NotASymbol(x.to_owned())), }) .collect::, EvalError>>()?; + Ok(Expression::AnonymousFunction { argument_symbols, + capture: capture_variables(&env, &body)?, body: Box::new(body), }) } @@ -115,6 +144,7 @@ pub fn prelude_defun(env: &Environment, expr: Expression) -> Result Result Result { - let [predicate, e_then, e_else] = expr.try_into()?; + let [predicate, e_then, e_else]: [Expression; 3] = expr.try_into()?; - match eval(env, predicate)? { + let val = eval(env, predicate.clone())?; + + match val { Expression::Nil => eval(env, e_else), _ => eval(env, e_then), } @@ -348,18 +380,24 @@ pub fn prelude_include(env: &Environment, expr: Expression) -> Result for ParserError { @@ -25,6 +26,7 @@ impl Display for ParserError { ParserError::TokenizerError(t) => write!(f, "Tokenizer Error: {}", t), ParserError::UnexpectedToken(t) => write!(f, "Unexpecte Token: {}", t), ParserError::UnexpectedEndOfInput => write!(f, "Unexpected end of input."), + ParserError::RuntimeError(s) => write!(f, "{}", s), } } } diff --git a/src/bin/lisp_demo.rs b/src/bin/lisp_demo.rs index 6711a56..4c98c4b 100644 --- a/src/bin/lisp_demo.rs +++ b/src/bin/lisp_demo.rs @@ -8,15 +8,24 @@ fn main() { "(print myvar) (print 'myvar)", "(car (cons 'a 'b)) (cdr (cons 'c 'd)) (cons 'a 'b)", "(eval (car (cons 'myvar 'b)))", + "(defun test (i) (progn (println i) (* 2 i)))", + "(test 10)", "(set 'pow (lambda (a b) (if (= b 0) 1 (* a (pow a (- b 1))))))", "pow", "(pow 2 10)", "(let '((fib . (lambda (n) (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2))))))) (fib 10))", "(defun do-n-times (f n) (if (= n 0) '() (cons (f) (do-n-times f (- n 1)))))", - "(do-n-times (lambda () (print 'hello)) 5)", + "(do-n-times (lambda () (println 'hello)) 5)", "(progn (print 'hello) (print 'world))", "(load \"(defun loaded-foo (x) (+ x 1))\")", "(loaded-foo 1)", + "(defun bind-test (x) (let '((x . x)) (lambda (y) (+ x y))))", + "(set 'add10 (bind-test 10))", + "(add10 10)", + "(set 'doAdd (let '((a . 20)) (lambda () (add10 a))))", + "(doAdd)", + "(defun mkDoubleClosure (a b c) (let '((x . (+ a b)) (y . (* b c))) (lambda (z) (let '((d . (+ z x))) (* d y)))))", + "((mkDoubleClosure 1 2 3) 2)", ]; let environment = Environment::default(); @@ -31,7 +40,10 @@ fn main() { println!("Evaluating: {}", expr.clone()); match eval(&environment, expr) { Ok(e) => println!("=> {}", e), - Err(e) => println!("Error: {}", e), + Err(e) => { + println!("Error: {}", e); + return; + } } } }