diff options
| author | Arnaud Bailly <arnaud.bailly@iohk.io> | 2024-09-25 14:11:41 +0200 |
|---|---|---|
| committer | Arnaud Bailly <arnaud.bailly@iohk.io> | 2024-09-25 14:11:41 +0200 |
| commit | 778881eda5b0955fb4c845fb29097f048491f007 (patch) | |
| tree | a476547ca3286fc5e20a83198d658da7b1b18540 /rust/src | |
| parent | 90dfe422f9c7058bebde0abf7eb3c7675c8fbf66 (diff) | |
| download | lambda-nantes-778881eda5b0955fb4c845fb29097f048491f007.tar.gz | |
Let creates a new environment
Diffstat (limited to 'rust/src')
| -rw-r--r-- | rust/src/lambda.rs | 56 |
1 files changed, 45 insertions, 11 deletions
diff --git a/rust/src/lambda.rs b/rust/src/lambda.rs index 9d810f9..97b6f08 100644 --- a/rust/src/lambda.rs +++ b/rust/src/lambda.rs @@ -1,5 +1,5 @@ use rand::Rng; -use std::fs::read_to_string; +use std::{collections::HashMap, fs::read_to_string}; mod ast; use ast::*; @@ -7,20 +7,45 @@ use ast::*; mod parser; use parser::*; -struct Environment { - bindings: std::collections::HashMap<String, Value>, +#[derive(Debug, PartialEq)] +struct Environment<'a> { + parent: Box<Option<&'a Environment<'a>>>, + bindings: HashMap<String, Value>, } -impl Environment { +impl<'a> Environment<'a> { fn new() -> Self { Environment { - bindings: std::collections::HashMap::new(), + parent: Box::new(None), + bindings: HashMap::new(), } } fn bind(&mut self, var: &str, value: &Value) { self.bindings.insert(var.to_string(), value.clone()); } + + fn extends(&'a self) -> Self { + Environment { + parent: Box::new(Some(self)), + bindings: HashMap::new(), + } + } + + fn lookup(&self, var: &str) -> Option<&Value> { + self.bindings.get(var).or_else(|| match *self.parent { + Some(parent) => parent.lookup(var), + None => None, + }) + } + + fn binds(&self, s: &str) -> bool { + self.bindings.contains_key(s) + || match *self.parent { + Some(parent) => parent.binds(s), + None => false, + } + } } pub fn eval_file(file_name: &str) -> String { @@ -45,28 +70,31 @@ fn eval(arg: &Value, env: &mut Environment) -> Value { Value::Bool(true) } Value::Let(var, value, expr) => { - env.bind(var, value); - eval(expr, env) + let mut newenv = env.extends(); + newenv.bind(var, value); + eval(expr, &mut newenv) } Value::App(l, r) => { let result = apply(&eval(l, env), r); - if is_reducible(&result) { + if is_reducible(&result, env) { eval(&result, env) } else { result } } - Value::Sym(var) => env.bindings.get(var).unwrap_or(arg).clone(), + Value::Sym(var) => env.lookup(var).unwrap_or(arg).clone(), other => other.clone(), } } -fn is_reducible(result: &Value) -> bool { +fn is_reducible(result: &Value, env: &Environment) -> bool { match result { Value::App(l, r) => match **l { Value::Lam(_, _) => true, - _ => is_reducible(l) || is_reducible(r), + _ => is_reducible(l, env) || is_reducible(r, env), }, + Value::Let(_, _, _) => true, + Value::Sym(s) => env.binds(s), Value::Lam(_, _) => false, _ => false, } @@ -176,4 +204,10 @@ mod lambda_test { let values = parse("(let (foo (lam x x)) (foo 12))"); assert_eq!(vec![Value::Num(12)], eval_all(&values)); } + + #[test] + fn let_expressions_introduce_new_scope_for_bindings() { + let values = parse("(let (foo (lam x x)) ((let (foo foo) foo) 13))"); + assert_eq!(vec![Value::Num(13)], eval_all(&values)); + } } |
