From 390c5d1b487d4090ebfba6a938049b8f070c7051 Mon Sep 17 00:00:00 2001 From: Felix Martin Date: Sat, 29 May 2021 13:50:26 -0400 Subject: [PATCH] Implement lambda application (boom!) --- src/environment.rs | 42 ++++++++++++++++++----- src/interpreter.rs | 83 ++++++++++++++++++++++++++++++---------------- src/main.rs | 4 +-- src/primitives.rs | 21 ++++++++++++ 4 files changed, 111 insertions(+), 39 deletions(-) diff --git a/src/environment.rs b/src/environment.rs index f907190..f9bf56a 100644 --- a/src/environment.rs +++ b/src/environment.rs @@ -1,23 +1,25 @@ use crate::parser::Datum; -use crate::parser::Datum::{Procedure, Symbol}; use crate::parser::make_symbol; use crate::primitives; +use std::mem; type Mapping = (Datum, Datum); type Frame = Vec; pub type Env = Vec; pub fn get_global_environment() -> Env { - vec![vec![(make_symbol("+"), Procedure(primitives::add)), - (make_symbol("*"), Procedure(primitives::mul)), - (make_symbol("-"), Procedure(primitives::sub))]] + vec![vec![(make_symbol("+"), Datum::Procedure(primitives::add)), + (make_symbol("*"), Datum::Procedure(primitives::mul)), + (make_symbol("-"), Datum::Procedure(primitives::sub)), + (make_symbol("="), Datum::Procedure(primitives::eq))]] } pub fn lookup_variable_value(var: &Datum, env: &Env) -> Datum { - if let Symbol(symbol_name) = var { - for frame in env { + if let Datum::Symbol(symbol_name) = var { + for i in (0..env.len()).rev() { + let frame = &env[i]; for mapping in frame { - if let (Symbol(current_name), datum) = mapping { + if let (Datum::Symbol(current_name), datum) = mapping { if symbol_name == current_name { return datum.clone(); } @@ -31,11 +33,11 @@ pub fn lookup_variable_value(var: &Datum, env: &Env) -> Datum { } pub fn define_variable(var: &Datum, val: Datum, env: &mut Env) -> Datum { - if let Symbol(symbol_name) = var { + if let Datum::Symbol(symbol_name) = var { // Assign to variable if it already exits if let Some(frame) = env.last_mut() { for mapping in frame { - if let (Symbol(current_name), _) = mapping { + if let (Datum::Symbol(current_name), _) = mapping { if symbol_name == current_name { mapping.1 = val; return Datum::Unspecified; @@ -55,4 +57,26 @@ pub fn define_variable(var: &Datum, val: Datum, env: &mut Env) -> Datum { panic!("DEFINE-VARIABLE -- not-a-symbol {:?}", var) } Datum::Unspecified +} + +pub fn extend_environment(vars: Datum, vals: Datum, env: &mut Env) -> Datum { + if let (Datum::List(mut vars), Datum::List(mut vals)) = (vars, vals) { + if vars.len() != vals.len() { + panic!("EXTEND-ENVIRONMENT -- length of vars and vals does not match") + } + let mut frame = vec![]; + for i in 0..vars.len() { + let pair = (mem::take(&mut vars[i]), mem::take(&mut vals[i])); + frame.push(pair); + } + env.push(frame); + } else { + panic!("EXTEND-ENVIRONMENT -- vars or vals not lists") + } + Datum::Unspecified +} + +pub fn shrink_environment(env: &mut Env) -> Datum { + env.pop(); + Datum::Unspecified } \ No newline at end of file diff --git a/src/interpreter.rs b/src/interpreter.rs index a39a2ac..1ea2d00 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -1,27 +1,25 @@ use crate::parser::Datum; use crate::parser::Datum::*; +use crate::parser::make_symbol; use crate::environment; use crate::environment::Env; -use std::mem; fn is_true(exp: &Datum) -> bool { match exp { Boolean(b) => *b, Number(_) => true, - List(_) => true, + List(v) if has_tag(v, "list") => true, _ => panic!("IS-TRUE -- unknown-truth-value {:?}", exp), } } -fn has_tag(exp: &Datum, tag: &str) -> bool { - if let List(l) = exp { - if l.len() == 0 { - return false; - } - if let Symbol(s) = &l[0] { - if s == tag { - return true; - } +fn has_tag(vec: &Vec, tag: &str) -> bool { + if vec.len() == 0 { + return false; + } + if let Symbol(s) = &vec[0] { + if s == tag { + return true; } } false @@ -31,7 +29,8 @@ fn interpret_if(args: &Vec, env: &mut Env) -> Datum { let predicate = &args[1]; let alternative = &args[2]; - if is_true(predicate) { + let predicate_interpreted = interpret(predicate, env); + if is_true(&predicate_interpreted) { interpret(alternative, env) } else { if args.len() == 4 { @@ -44,16 +43,29 @@ fn interpret_if(args: &Vec, env: &mut Env) -> Datum { } fn interpret_define(args: &Vec, env: &mut Env) -> Datum { - match args[1] { + match &args[1] { Symbol(_) => { + // regular definition let var = &args[1]; let val = interpret(&args[2], env); environment::define_variable(var, val, env) }, - List(_) => { - println!("procedure definition"); - Number(42) - } + List(v) => { + // procedure definition + let definition_var = &v[0]; + let mut parameters = vec![]; + for p in &v[1..] { + if let Symbol(_) = &p { + parameters.push(p.clone()); + } else { + panic!("INTERPRET-DEFINE -- parameter not a symbol {:?}", p); + } + } + let lambda_parameters = Datum::List(parameters); + let lambda_body = Datum::List(args[2..].to_vec()); + let lambda = Datum::List(vec![make_symbol("lambda"), lambda_parameters, lambda_body]); + environment::define_variable(definition_var, lambda, env) + }, _ => panic!("INTERPRET-DEFINE -- cannot define {:?}", args) } } @@ -68,7 +80,7 @@ fn interpret_begin(vec: &Vec, env: &mut Env) -> Datum { fn interpret_application(vec: &Vec, env: &mut Env) -> Datum { if vec.len() == 0 { - panic!("APPLICATION -- no-procedure") + panic!("INTERPRET-APPLICATION -- no-procedure") } let mut args = vec![]; @@ -77,10 +89,25 @@ fn interpret_application(vec: &Vec, env: &mut Env) -> Datum { args.push(a) } - if let Procedure(proc) = mem::take(&mut args[0]) { - proc(args) - } else { - panic!("APPLICATION -- not-aplicable {:?}", args[0]) + match &args[0] { + Procedure(proc) => proc(args), + List(v) if has_tag(v, "lambda") => { + let lambda_parameters = v[1].clone(); + let lambda_body = &v[2]; + let lambda_args = args[1..].to_vec(); + + // FIXME: this does not optimize tail-calls + environment::extend_environment(lambda_parameters, List(lambda_args), env); + let mut r = Datum::Unspecified; + if let List(seq) = lambda_body { + for i in 0..seq.len() { + r = interpret(&seq[i], env); + } + } + environment::shrink_environment(env); + r + }, + _ => panic!("INTERPRET-APPLICATION -- not-aplicable {:?}", args[0]) } } @@ -90,12 +117,12 @@ pub fn interpret(exp: &Datum, env: &mut Env) -> Datum { Number(_) => exp.clone(), Unspecified => Unspecified, Symbol(_) => environment::lookup_variable_value(exp, env), - List(v) if has_tag(&exp, "if") => interpret_if(v, env), - List(_) if has_tag(&exp, "set!") => panic!("assignment-not-supported"), - List(v) if has_tag(&exp, "define") => interpret_define(v, env), - List(_) if has_tag(&exp, "cond") => panic!("cond-not-supported"), - List(_) if has_tag(&exp, "let") => panic!("let-not-supported"), - List(v) if has_tag(&exp, "begin") => interpret_begin(v, env), + List(v) if has_tag(v, "if") => interpret_if(v, env), + List(v) if has_tag(v, "set!") => panic!("assignment-not-supported"), + List(v) if has_tag(v, "define") => interpret_define(v, env), + List(v) if has_tag(v, "cond") => panic!("cond-not-supported"), + List(v) if has_tag(v, "let") => panic!("let-not-supported"), + List(v) if has_tag(v, "begin") => interpret_begin(v, env), List(v) => interpret_application(v, env), // TODO: quoted _ => panic!("unknown-expression {:?}", exp), diff --git a/src/main.rs b/src/main.rs index 2d320a8..540d63b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,9 +5,9 @@ mod primitives; mod environment; fn main() { - // let scm_code = "(if #t (+ 1 1) (* 2 3 4))"; + // let scm_code = "(if (= 1 1) (+ 1 1) (* 2 3 4))"; // let scm_code = "(begin (define a (+ 2 2)) (* 3 2) (* a a a))"; - let scm_code = "(begin (define (f n) (* n n)) (* 3 2))"; + let scm_code = "(begin (define (f n) (if (= n 1) 1 (* n (f (- n 1))))) (f 10))"; let tokens = lexer::read(scm_code); let datum = parser::parse(tokens); let mut env = environment::get_global_environment(); diff --git a/src/primitives.rs b/src/primitives.rs index 4ba9a23..34ed86d 100644 --- a/src/primitives.rs +++ b/src/primitives.rs @@ -56,3 +56,24 @@ pub fn sub(mut args: Vec) -> Datum { } Number(r) } + +pub fn eq(args: Vec) -> Datum { + if args.len() == 1 { + Boolean(true) + } else { + let first = &args[1]; + + for i in 2..args.len() { + let current = &args[i]; + match (first, current) { + (Boolean(b1), Boolean(b2)) => {return Boolean(b1 == b2);}, + (Number(n1), Number(n2)) => {return Boolean(n1 == n2);}, + _ => { + println!("{:?} is not equal to {:?}", first, current); + return Boolean(false); + } + } + } + Boolean(true) + } +} \ No newline at end of file