Added error-handling and more comments
Getty Ritter
10 years ago
1 | #![feature(macro_rules)] | |
1 | use std::task; | |
2 | 2 | |
3 |
#[deriving(Eq,PartialEq,Clone,Show |
|
3 | #[deriving(Eq,PartialEq,Clone,Show,Send)] | |
4 | 4 | enum Term { |
5 | 5 | Num(int), |
6 | 6 | Var(String), |
9 | 9 | Let(String, Box<Term>, Box<Term>), |
10 | 10 | } |
11 | 11 | |
12 | // The following are wrappers over λ-terms to simplify writing | |
13 | // allocations. It really does help, as you can see in main. | |
12 | 14 | fn num(n: int) -> Box<Term> { |
13 | 15 | box Num(n) |
14 | 16 | } |
29 | 31 | box Let(x.to_string(), y, z) |
30 | 32 | } |
31 | 33 | |
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)] | |
33 | 39 | enum Val { |
34 | 40 | VNum(int), |
35 | 41 | VLam(String, Box<Term>, Box<Env>), |
36 | 42 | } |
37 | 43 | |
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)] | |
39 | 48 | enum Env { |
40 | 49 | Empty, |
41 | 50 | Binding(String, Box<Val>, Box<Env>), |
42 | 51 | } |
43 | 52 | |
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. | |
44 | 55 | fn lookup(s: &String, e: &Env) -> Box<Val> { |
45 | 56 | match *e { |
46 | 57 | Empty => { fail!(format!("Couldn't find {} in environment", s)) } |
54 | 65 | } |
55 | 66 | } |
56 | 67 | |
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. | |
57 | 71 | fn lcalc_eval(t: &Term, e: &Env) -> Box<Val> { |
58 | 72 | match t { |
59 | &Num(num) => { box VNum(num) } | |
60 | &Var(ref str) => { lookup(str, e) } | |
61 |
& |
|
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 | } | |
62 | 82 | &App(box ref f, box ref x) => { |
63 | 83 | match *lcalc_eval(f, e) { |
64 | 84 | VLam(ref arg, box ref body, box ref env) => { |
67 | 87 | box env.clone()); |
68 | 88 | lcalc_eval(body, &newEnv) |
69 | 89 | } |
70 |
_ => fail!( |
|
90 | _ => fail!("Tried to apply a non-function!") | |
71 | 91 | } |
72 | 92 | } |
73 | 93 | &Let(ref s, box ref t, box ref b) => { |
76 | 96 | box e.clone()); |
77 | 97 | lcalc_eval(b, &newEnv) |
78 | 98 | } |
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, | |
79 | 114 | } |
80 | 115 | } |
81 | 116 | |
90 | 125 | num(2)), |
91 | 126 | app(var("f"), |
92 | 127 | 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)); | |
93 | 132 | 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)); | |
96 | 137 | } |