Fix env model resolves #11 - I call this good enough :)

This commit is contained in:
2021-06-03 21:22:41 -04:00
parent 9d0003ff14
commit af7fc21cde
3 changed files with 85 additions and 56 deletions

View File

@@ -1,21 +1,23 @@
use crate::parser::make_symbol; use crate::parser::make_symbol;
use crate::parser::Datum; use crate::parser::Datum;
use std::cell::RefCell;
use std::mem; use std::mem;
use std::rc::Rc;
type Mapping = (Datum, Datum); type Mapping = (Datum, Datum);
#[derive(Debug)] #[derive(Debug)]
pub struct Frame<'a> { pub struct Frame {
mappings: Vec<Mapping>, mappings: Vec<Mapping>,
outer: Option<&'a Frame<'a>>, outer: Option<Rc<RefCell<Frame>>>,
} }
pub type Env<'a> = Frame<'a>; pub type Env = Rc<RefCell<Frame>>;
pub fn get_global_environment<'a>() -> Env<'a> { pub fn get_global_environment() -> Env {
use crate::parser::Datum::Primitive; use crate::parser::Datum::Primitive;
use crate::primitives::*; use crate::primitives::*;
Frame { let f = Frame {
mappings: vec![ mappings: vec![
(make_symbol("+"), Primitive(add)), (make_symbol("+"), Primitive(add)),
(make_symbol("*"), Primitive(mul)), (make_symbol("*"), Primitive(mul)),
@@ -34,68 +36,67 @@ pub fn get_global_environment<'a>() -> Env<'a> {
(make_symbol("display"), Primitive(display)), (make_symbol("display"), Primitive(display)),
], ],
outer: None, outer: None,
} };
Rc::new(RefCell::new(f))
} }
pub fn lookup_variable_value(var: &Datum, env: &Env) -> Datum { pub fn lookup_variable_value(var: &Datum, env: &Env) -> Datum {
if let Datum::Symbol(symbol_name) = var { let symbol_name = Datum::as_str(var).expect("LOOKUP-VARIABLE-VALUE -- not a symbol");
for mapping in &env.mappings { let frame = env.borrow();
if let (Datum::Symbol(current_name), datum) = mapping {
if symbol_name == current_name { for mapping in &frame.mappings {
return datum.clone(); 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), match &frame.outer {
} Some(outer_env) => lookup_variable_value(var, &outer_env),
} else { _ => panic!("LOOKUP-VARIABLE-VALUE -- does not exist {}", var),
panic!("LOOKUP-VARIABLE-VALUE -- not-a-symbol {:?}", var)
} }
} }
pub fn define_variable(var: &Datum, val: Datum, env: &mut Env) -> Datum { pub fn define_variable(var: &Datum, val: Datum, env: &mut Env) -> Datum {
if let Datum::Symbol(symbol_name) = var { let symbol_name = Datum::as_str(var).expect("DEFINE-VARIABLE -- not a symbol");
// Assign to variable if it already exits let mut frame = env.borrow_mut();
for mapping in &mut env.mappings {
if let (Datum::Symbol(current_name), _) = mapping { // Assign to variable if it already exits
if symbol_name == current_name { for mapping in &mut frame.mappings {
mapping.1 = val; if let (Datum::Symbol(current_name), _) = mapping {
return var.clone(); 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() var.clone()
} }
pub fn set_variable(var: &Datum, val: Datum, env: &mut Env) -> Datum { pub fn set_variable(var: &Datum, val: Datum, env: &Env) -> Datum {
if let Datum::Symbol(symbol_name) = var { let symbol_name = Datum::as_str(var).expect("SET-VARIABLE -- not a symbol");
for mapping in &mut env.mappings { let mut frame = env.borrow_mut();
if let (Datum::Symbol(current_name), _) = mapping { for mapping in &mut frame.mappings {
if symbol_name == current_name { if let (Datum::Symbol(current_name), _) = mapping {
mapping.1 = val; if symbol_name == *current_name {
return var.clone(); mapping.1 = val;
} return var.clone();
} }
} }
match env.outer { }
// XXX: search outer environments here
// Some(outer_env) => set_variable(var, val, outer_env), match &frame.outer {
_ => panic!("SET-VARIABLE -- unbound-var {:?}", var), Some(outer_env) => set_variable(var, val, &outer_env),
} _ => panic!("LOOKUP-VARIABLE-VALUE -- does not exist {}", var),
} else {
panic!("DEFINE-VARIABLE -- not-a-symbol {:?}", 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![]; let mut mappings = vec![];
if let (Datum::List(mut vars), Datum::List(mut vals)) = (vars, vals) { if let (Datum::List(mut vars), Datum::List(mut vals)) = (vars, vals) {
if vars.len() != vals.len() { if vars.len() != vals.len() {
@@ -108,8 +109,10 @@ pub fn extend<'a>(vars: Datum, vals: Datum, env: &'a Env) -> Env<'a> {
} else { } else {
panic!("EXTEND-ENVIRONMENT -- vars or vals not lists") panic!("EXTEND-ENVIRONMENT -- vars or vals not lists")
} }
Frame {
mappings: mappings, let f = Frame {
outer: Some(env), mappings,
} outer: Some(env.clone()),
};
Rc::new(RefCell::new(f))
} }

View File

@@ -72,11 +72,11 @@ fn interpret_define(args: &Vec<Datum>, env: &mut Env) -> Datum {
} }
} }
// XXX: a procedure includes the current environment
let body = args[2..].to_vec(); let body = args[2..].to_vec();
let proc = Procedure { let proc = Procedure {
parameters: parameters, parameters: parameters,
body: body, body: body,
env: env.clone(),
}; };
environment::define_variable(definition_var, proc, env) environment::define_variable(definition_var, proc, env)
} }
@@ -156,15 +156,30 @@ fn interpret_application(vec: &Vec<Datum>, env: &mut Env) -> Datum {
match &args[0] { match &args[0] {
Primitive(proc) => proc(args), Primitive(proc) => proc(args),
Lambda { parameters, body } | Procedure { parameters, body } => { Lambda { parameters, body } => {
let lambda_args = args[1..].to_vec(); let lambda_args = List(args[1..].to_vec());
let mut new_env = let lambda_params = List(parameters.to_vec());
environment::extend(List(parameters.to_vec()), List(lambda_args), env); let mut new_env = environment::extend(lambda_params, lambda_args, env);
for i in 0..(body.len() - 1) { for i in 0..(body.len() - 1) {
interpret(&body[i], &mut new_env); interpret(&body[i], &mut new_env);
} }
interpret(&body[body.len() - 1], &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]), _ => panic!("INTERPRET-APPLICATION -- not-aplicable {:?}", args[0]),
} }
} }

View File

@@ -1,3 +1,4 @@
use crate::environment::Env;
use crate::lexer::Token; use crate::lexer::Token;
use crate::lexer::Tokens; use crate::lexer::Tokens;
use std::cmp::Ordering; use std::cmp::Ordering;
@@ -19,6 +20,7 @@ pub enum Datum {
Procedure { Procedure {
parameters: Vec<Datum>, parameters: Vec<Datum>,
body: Vec<Datum>, body: Vec<Datum>,
env: Env,
}, },
Unspecified, Unspecified,
} }
@@ -80,6 +82,15 @@ impl PartialOrd for Datum {
} }
} }
impl Datum {
pub fn as_str(&self) -> Option<std::string::String> {
match self {
Datum::Symbol(s) => Some(s.to_string()),
_ => None,
}
}
}
pub fn parse(tokens: Tokens) -> Datum { pub fn parse(tokens: Tokens) -> Datum {
let (datum, _) = parse_datum(&tokens, 0); let (datum, _) = parse_datum(&tokens, 0);
datum datum