gdritter repos rust-examples / 3b086aa
Added error-handling and more comments Getty Ritter 9 years ago
1 changed file(s) with 51 addition(s) and 10 deletion(s). Collapse all Expand all
1 #![feature(macro_rules)]
1 use std::task;
22
3 #[deriving(Eq,PartialEq,Clone,Show)]
3 #[deriving(Eq,PartialEq,Clone,Show,Send)]
44 enum Term {
55 Num(int),
66 Var(String),
99 Let(String, Box<Term>, Box<Term>),
1010 }
1111
12 // The following are wrappers over λ-terms to simplify writing
13 // allocations. It really does help, as you can see in main.
1214 fn num(n: int) -> Box<Term> {
1315 box Num(n)
1416 }
2931 box Let(x.to_string(), y, z)
3032 }
3133
32 #[deriving(Eq,PartialEq,Clone,Show)]
34 // A value is either a number or a closure, which has to have
35 // its environment around. We'll have to clone the environment
36 // into the closure to make sure that it stays around even if
37 // the closure is returned from the environment where it was used.
38 #[deriving(Eq,PartialEq,Clone,Show,Send)]
3339 enum Val {
3440 VNum(int),
3541 VLam(String, Box<Term>, Box<Env>),
3642 }
3743
38 #[deriving(Eq,PartialEq,Clone,Show)]
44 // I could also use a pair of a map and a parent pointer, but
45 // this is a little more TAPL-ish. Plus, we generally always
46 // bind a single variable at a time.
47 #[deriving(Eq,PartialEq,Clone,Show,Send)]
3948 enum Env {
4049 Empty,
4150 Binding(String, Box<Val>, Box<Env>),
4251 }
4352
53 // We're going to just fail out of the thread if we can't find a binding.
54 // We can always wrap this in another thread if we want to get a value.
4455 fn lookup(s: &String, e: &Env) -> Box<Val> {
4556 match *e {
4657 Empty => { fail!(format!("Couldn't find {} in environment", s)) }
5465 }
5566 }
5667
68 // The actual evaluator: this does some heap allocation, in particular, some
69 // copying of environments and allocating the result, for which it returns
70 // ownership.
5771 fn lcalc_eval(t: &Term, e: &Env) -> Box<Val> {
5872 match t {
59 &Num(num) => { box VNum(num) }
60 &Var(ref str) => { lookup(str, e) }
61 &Lam(ref s, ref b) => { box VLam(s.clone(), b.clone(), box e.clone()) }
73 &Num(num) => {
74 box VNum(num)
75 }
76 &Var(ref str) => {
77 lookup(str, e)
78 }
79 &Lam(ref s, ref b) => {
80 box VLam(s.clone(), b.clone(), box e.clone())
81 }
6282 &App(box ref f, box ref x) => {
6383 match *lcalc_eval(f, e) {
6484 VLam(ref arg, box ref body, box ref env) => {
6787 box env.clone());
6888 lcalc_eval(body, &newEnv)
6989 }
70 _ => fail!()
90 _ => fail!("Tried to apply a non-function!")
7191 }
7292 }
7393 &Let(ref s, box ref t, box ref b) => {
7696 box e.clone());
7797 lcalc_eval(b, &newEnv)
7898 }
99 }
100 }
101
102 // This copies the arguments and evaluates it in another thread, returning
103 // None if the evaluation fails.
104 fn lcalc_eval_opt(t: &Term, e: &Env) -> Option<Box<Val>> {
105 let (tx, rx) = channel();
106 let new_term = t.clone();
107 let new_env = e.clone();
108 let result = task::try(proc() {
109 tx.send(lcalc_eval(&new_term, &new_env));
110 });
111 match result {
112 Ok(_) => Some(rx.recv()),
113 Err(_) => None,
79114 }
80115 }
81116
90125 num(2)),
91126 app(var("f"),
92127 num(4)));
128 // (λx.y)(5), which will obviously fail
129 let s3 = app(lam("x", var("y")), num(5));
130 // (2)(3), which will also obviously fail
131 let s4 = app(num(2), num(3));
93132 let e = Empty;
94 println!("s1: {:}", lcalc_eval(&*s1, &e));
95 println!("s2: {:}", lcalc_eval(&*s2, &e));
133 println!("s1: {:}", lcalc_eval_opt(&*s1, &e));
134 println!("s2: {:}", lcalc_eval_opt(&*s2, &e));
135 println!("s3: {:}", lcalc_eval_opt(&*s3, &e));
136 println!("s4: {:}", lcalc_eval_opt(&*s4, &e));
96137 }