Implement lambda application (boom!)
This commit is contained in:
@@ -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<Mapping>;
|
||||
pub type Env = Vec<Frame>;
|
||||
|
||||
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;
|
||||
@@ -56,3 +58,25 @@ pub fn define_variable(var: &Datum, val: Datum, env: &mut Env) -> Datum {
|
||||
}
|
||||
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
|
||||
}
|
||||
@@ -1,29 +1,27 @@
|
||||
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 {
|
||||
fn has_tag(vec: &Vec<Datum>, tag: &str) -> bool {
|
||||
if vec.len() == 0 {
|
||||
return false;
|
||||
}
|
||||
if let Symbol(s) = &l[0] {
|
||||
if let Symbol(s) = &vec[0] {
|
||||
if s == tag {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
@@ -31,7 +29,8 @@ fn interpret_if(args: &Vec<Datum>, 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<Datum>, env: &mut Env) -> Datum {
|
||||
}
|
||||
|
||||
fn interpret_define(args: &Vec<Datum>, 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<Datum>, env: &mut Env) -> Datum {
|
||||
|
||||
fn interpret_application(vec: &Vec<Datum>, 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<Datum>, 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),
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -56,3 +56,24 @@ pub fn sub(mut args: Vec<Datum>) -> Datum {
|
||||
}
|
||||
Number(r)
|
||||
}
|
||||
|
||||
pub fn eq(args: Vec<Datum>) -> 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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user