Fix env model resolves #11 - I call this good enough :)
This commit is contained in:
@@ -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();
|
||||||
|
|
||||||
|
for mapping in &frame.mappings {
|
||||||
if let (Datum::Symbol(current_name), datum) = mapping {
|
if let (Datum::Symbol(current_name), datum) = mapping {
|
||||||
if symbol_name == current_name {
|
if symbol_name == *current_name {
|
||||||
return datum.clone();
|
return datum.clone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
match env.outer {
|
|
||||||
Some(outer_env) => lookup_variable_value(var, outer_env),
|
match &frame.outer {
|
||||||
None => panic!("LOOKUP-VARIABLE-VALUE -- unbound-variable-error {:?}", var),
|
Some(outer_env) => lookup_variable_value(var, &outer_env),
|
||||||
}
|
_ => panic!("LOOKUP-VARIABLE-VALUE -- does not exist {}", var),
|
||||||
} else {
|
|
||||||
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");
|
||||||
|
let mut frame = env.borrow_mut();
|
||||||
|
|
||||||
// Assign to variable if it already exits
|
// Assign to variable if it already exits
|
||||||
for mapping in &mut env.mappings {
|
for mapping in &mut frame.mappings {
|
||||||
if let (Datum::Symbol(current_name), _) = mapping {
|
if let (Datum::Symbol(current_name), _) = mapping {
|
||||||
if symbol_name == current_name {
|
if symbol_name == *current_name {
|
||||||
mapping.1 = val;
|
mapping.1 = val;
|
||||||
return var.clone();
|
return var.clone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create variable because it does not exist yet
|
// Create variable because it does not exist yet
|
||||||
let pair = (var.clone(), val);
|
let pair = (var.clone(), val);
|
||||||
env.mappings.push(pair);
|
frame.mappings.push(pair);
|
||||||
} else {
|
|
||||||
panic!("DEFINE-VARIABLE -- not-a-symbol {:?}", var)
|
|
||||||
}
|
|
||||||
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();
|
||||||
|
for mapping in &mut frame.mappings {
|
||||||
if let (Datum::Symbol(current_name), _) = mapping {
|
if let (Datum::Symbol(current_name), _) = mapping {
|
||||||
if symbol_name == current_name {
|
if symbol_name == *current_name {
|
||||||
mapping.1 = val;
|
mapping.1 = val;
|
||||||
return var.clone();
|
return var.clone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
match env.outer {
|
|
||||||
// XXX: search outer environments here
|
match &frame.outer {
|
||||||
// Some(outer_env) => set_variable(var, val, outer_env),
|
Some(outer_env) => set_variable(var, val, &outer_env),
|
||||||
_ => panic!("SET-VARIABLE -- unbound-var {:?}", var),
|
_ => 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))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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]),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user