diff --git a/src/environment.rs b/src/environment.rs index ed6a132..2cb3d76 100644 --- a/src/environment.rs +++ b/src/environment.rs @@ -1,21 +1,23 @@ use crate::parser::make_symbol; use crate::parser::Datum; +use std::cell::RefCell; use std::mem; +use std::rc::Rc; type Mapping = (Datum, Datum); #[derive(Debug)] -pub struct Frame<'a> { +pub struct Frame { mappings: Vec, - outer: Option<&'a Frame<'a>>, + outer: Option>>, } -pub type Env<'a> = Frame<'a>; +pub type Env = Rc>; -pub fn get_global_environment<'a>() -> Env<'a> { +pub fn get_global_environment() -> Env { use crate::parser::Datum::Primitive; use crate::primitives::*; - Frame { + let f = Frame { mappings: vec![ (make_symbol("+"), Primitive(add)), (make_symbol("*"), Primitive(mul)), @@ -34,68 +36,67 @@ pub fn get_global_environment<'a>() -> Env<'a> { (make_symbol("display"), Primitive(display)), ], outer: None, - } + }; + Rc::new(RefCell::new(f)) } pub fn lookup_variable_value(var: &Datum, env: &Env) -> Datum { - if let Datum::Symbol(symbol_name) = var { - for mapping in &env.mappings { - if let (Datum::Symbol(current_name), datum) = mapping { - if symbol_name == current_name { - return datum.clone(); - } + let symbol_name = Datum::as_str(var).expect("LOOKUP-VARIABLE-VALUE -- not a symbol"); + let frame = env.borrow(); + + for mapping in &frame.mappings { + if let (Datum::Symbol(current_name), datum) = mapping { + if symbol_name == *current_name { + return datum.clone(); } } - match env.outer { - Some(outer_env) => lookup_variable_value(var, outer_env), - None => panic!("LOOKUP-VARIABLE-VALUE -- unbound-variable-error {:?}", var), - } - } else { - panic!("LOOKUP-VARIABLE-VALUE -- not-a-symbol {:?}", var) + } + + match &frame.outer { + Some(outer_env) => lookup_variable_value(var, &outer_env), + _ => panic!("LOOKUP-VARIABLE-VALUE -- does not exist {}", var), } } pub fn define_variable(var: &Datum, val: Datum, env: &mut Env) -> Datum { - if let Datum::Symbol(symbol_name) = var { - // Assign to variable if it already exits - for mapping in &mut env.mappings { - if let (Datum::Symbol(current_name), _) = mapping { - if symbol_name == current_name { - mapping.1 = val; - return var.clone(); - } + let symbol_name = Datum::as_str(var).expect("DEFINE-VARIABLE -- not a symbol"); + let mut frame = env.borrow_mut(); + + // Assign to variable if it already exits + for mapping in &mut frame.mappings { + if let (Datum::Symbol(current_name), _) = mapping { + if symbol_name == *current_name { + mapping.1 = val; + return var.clone(); } } - // Create variable because it does not exist yet - let pair = (var.clone(), val); - env.mappings.push(pair); - } else { - panic!("DEFINE-VARIABLE -- not-a-symbol {:?}", var) } + + // Create variable because it does not exist yet + let pair = (var.clone(), val); + frame.mappings.push(pair); var.clone() } -pub fn set_variable(var: &Datum, val: Datum, env: &mut Env) -> Datum { - if let Datum::Symbol(symbol_name) = var { - for mapping in &mut env.mappings { - if let (Datum::Symbol(current_name), _) = mapping { - if symbol_name == current_name { - mapping.1 = val; - return var.clone(); - } +pub fn set_variable(var: &Datum, val: Datum, env: &Env) -> Datum { + let symbol_name = Datum::as_str(var).expect("SET-VARIABLE -- not a symbol"); + let mut frame = env.borrow_mut(); + for mapping in &mut frame.mappings { + if let (Datum::Symbol(current_name), _) = mapping { + if symbol_name == *current_name { + mapping.1 = val; + return var.clone(); } } - match env.outer { - // XXX: search outer environments here - // Some(outer_env) => set_variable(var, val, outer_env), - _ => panic!("SET-VARIABLE -- unbound-var {:?}", var), - } - } else { - panic!("DEFINE-VARIABLE -- not-a-symbol {:?}", var) + } + + match &frame.outer { + Some(outer_env) => set_variable(var, val, &outer_env), + _ => panic!("LOOKUP-VARIABLE-VALUE -- does not exist {}", var), } } -pub fn extend<'a>(vars: Datum, vals: Datum, env: &'a Env) -> Env<'a> { +pub fn extend(vars: Datum, vals: Datum, env: &mut Env) -> Env { let mut mappings = vec![]; if let (Datum::List(mut vars), Datum::List(mut vals)) = (vars, vals) { if vars.len() != vals.len() { @@ -108,8 +109,10 @@ pub fn extend<'a>(vars: Datum, vals: Datum, env: &'a Env) -> Env<'a> { } else { panic!("EXTEND-ENVIRONMENT -- vars or vals not lists") } - Frame { - mappings: mappings, - outer: Some(env), - } + + let f = Frame { + mappings, + outer: Some(env.clone()), + }; + Rc::new(RefCell::new(f)) } diff --git a/src/interpreter.rs b/src/interpreter.rs index fe86ece..94f1b5c 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -72,11 +72,11 @@ fn interpret_define(args: &Vec, env: &mut Env) -> Datum { } } - // XXX: a procedure includes the current environment let body = args[2..].to_vec(); let proc = Procedure { parameters: parameters, body: body, + env: env.clone(), }; environment::define_variable(definition_var, proc, env) } @@ -156,15 +156,30 @@ fn interpret_application(vec: &Vec, env: &mut Env) -> Datum { match &args[0] { Primitive(proc) => proc(args), - Lambda { parameters, body } | Procedure { parameters, body } => { - let lambda_args = args[1..].to_vec(); - let mut new_env = - environment::extend(List(parameters.to_vec()), List(lambda_args), env); + Lambda { parameters, body } => { + let lambda_args = List(args[1..].to_vec()); + let lambda_params = List(parameters.to_vec()); + let mut new_env = environment::extend(lambda_params, lambda_args, env); for i in 0..(body.len() - 1) { interpret(&body[i], &mut new_env); } interpret(&body[body.len() - 1], &mut new_env) } + Procedure { + parameters, + body, + env, + } => { + let lambda_args = List(args[1..].to_vec()); + let lambda_params = List(parameters.to_vec()); + let mut proc_env = env.clone(); + let mut new_env = environment::extend(lambda_params, lambda_args, &mut proc_env); + for i in 0..(body.len() - 1) { + interpret(&body[i], &mut new_env); + } + // This is my attempt to implement tail-call optimiation. + interpret(&body[body.len() - 1], &mut new_env) + } _ => panic!("INTERPRET-APPLICATION -- not-aplicable {:?}", args[0]), } } diff --git a/src/parser.rs b/src/parser.rs index 06bae0c..c489621 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,3 +1,4 @@ +use crate::environment::Env; use crate::lexer::Token; use crate::lexer::Tokens; use std::cmp::Ordering; @@ -19,6 +20,7 @@ pub enum Datum { Procedure { parameters: Vec, body: Vec, + env: Env, }, Unspecified, } @@ -80,6 +82,15 @@ impl PartialOrd for Datum { } } +impl Datum { + pub fn as_str(&self) -> Option { + match self { + Datum::Symbol(s) => Some(s.to_string()), + _ => None, + } + } +} + pub fn parse(tokens: Tokens) -> Datum { let (datum, _) = parse_datum(&tokens, 0); datum