diff options
-rw-r--r-- | src/lisp/eval.rs | 249 | ||||
-rw-r--r-- | src/lisp/prelude.rs | 139 | ||||
-rw-r--r-- | src/lisp/primitives.rs | 10 |
3 files changed, 224 insertions, 174 deletions
diff --git a/src/lisp/eval.rs b/src/lisp/eval.rs index 8bfa86b..5accec4 100644 --- a/src/lisp/eval.rs +++ b/src/lisp/eval.rs | |||
@@ -1,140 +1,24 @@ | |||
1 | use crate::{ | 1 | use crate::{ |
2 | app::AppState, | 2 | app::AppState, |
3 | lisp::{ | 3 | lisp::{ |
4 | error::LispError, | 4 | error::{EvalError, LispError}, |
5 | expr::{LispExpr, LispFunction}, | 5 | expr::{Ident, LispExpr, LispFunction}, |
6 | number::LispNumber, | 6 | number::LispNumber, |
7 | Environment, | 7 | EnvList, Environment, |
8 | }, | 8 | }, |
9 | primitive, | ||
10 | }; | 9 | }; |
11 | 10 | ||
12 | use log::{error, info}; | 11 | use std::convert::TryInto; |
13 | 12 | ||
14 | pub fn with_prelude() -> Environment { | 13 | use log::{error, info}; |
15 | let mut env = Environment::new(); | ||
16 | primitive!(env, Some(2), "+", |args, _| { | ||
17 | if args.iter().any(|arg| !matches!(arg, LispExpr::Number(_))) { | ||
18 | Err(LispError::EvalError) | ||
19 | } else { | ||
20 | Ok(LispExpr::Number( | ||
21 | args.iter() | ||
22 | .map(|arg| unwrap_number(arg)) | ||
23 | .fold(LispNumber::Integer(0), |acc, x| acc + *x), | ||
24 | )) | ||
25 | } | ||
26 | }); | ||
27 | primitive!(env, Some(2), "-", |args, _| { | ||
28 | if args.iter().any(|arg| !matches!(arg, LispExpr::Number(_))) { | ||
29 | Err(LispError::EvalError) | ||
30 | } else { | ||
31 | let mut acc = unwrap_number(&args[0]).clone(); | ||
32 | for arg in args.into_iter().skip(1) { | ||
33 | acc = acc - *unwrap_number(&arg); | ||
34 | } | ||
35 | Ok(LispExpr::Number(acc)) | ||
36 | } | ||
37 | }); | ||
38 | primitive!(env, Some(2), "*", |args, _| { | ||
39 | if args.iter().any(|arg| !matches!(arg, LispExpr::Number(_))) { | ||
40 | Err(LispError::EvalError) | ||
41 | } else { | ||
42 | Ok(LispExpr::Number( | ||
43 | args.iter() | ||
44 | .map(|arg| unwrap_number(arg)) | ||
45 | .fold(LispNumber::Integer(1), |acc, x| acc * *x), | ||
46 | )) | ||
47 | } | ||
48 | }); | ||
49 | primitive!(env, Some(0), "toggle-grid", |_, app| { | ||
50 | app.toggle_grid(); | ||
51 | Ok(LispExpr::Unit) | ||
52 | }); | ||
53 | primitive!(env, Some(3), "if", |args, _| { | ||
54 | match args { | ||
55 | [predicate, then, else_] => { | ||
56 | if matches!(predicate, LispExpr::BoolLit(false)) { | ||
57 | Ok(else_.clone()) | ||
58 | } else { | ||
59 | Ok(then.clone()) | ||
60 | } | ||
61 | } | ||
62 | _ => { | ||
63 | error!("invalid args for `if` primitive"); | ||
64 | Err(LispError::EvalError) | ||
65 | } | ||
66 | } | ||
67 | }); | ||
68 | primitive!(env, Some(2), "and", |args, _| { | ||
69 | if args | ||
70 | .iter() | ||
71 | .any(|arg| matches!(arg, LispExpr::BoolLit(false))) | ||
72 | { | ||
73 | Ok(LispExpr::BoolLit(false)) | ||
74 | } else { | ||
75 | Ok(LispExpr::BoolLit(true)) | ||
76 | } | ||
77 | }); | ||
78 | primitive!(env, Some(2), "or", |args, _| { | ||
79 | if args | ||
80 | .iter() | ||
81 | .any(|arg| matches!(arg, LispExpr::BoolLit(true))) | ||
82 | { | ||
83 | Ok(LispExpr::BoolLit(true)) | ||
84 | } else { | ||
85 | Ok(LispExpr::BoolLit(false)) | ||
86 | } | ||
87 | }); | ||
88 | primitive!(env, Some(1), "not", |args, _| { | ||
89 | match args { | ||
90 | [val] => { | ||
91 | if matches!(val, LispExpr::BoolLit(false)) { | ||
92 | Ok(LispExpr::BoolLit(true)) | ||
93 | } else { | ||
94 | Ok(LispExpr::BoolLit(false)) | ||
95 | } | ||
96 | } | ||
97 | _ => Err(LispError::EvalError), | ||
98 | } | ||
99 | }); | ||
100 | primitive!(env, None, "begin", |args, _| { | ||
101 | if args.is_empty() { | ||
102 | Err(LispError::EvalError) | ||
103 | } else { | ||
104 | Ok(args.into_iter().last().unwrap().clone()) | ||
105 | } | ||
106 | }); | ||
107 | primitive!(env, Some(2), "/", |args, _| { | ||
108 | if args.is_empty() || args.iter().any(|arg| !matches!(arg, LispExpr::Number(_))) { | ||
109 | Err(LispError::EvalError) | ||
110 | } else { | ||
111 | let mut acc = unwrap_number(&args[0]).clone(); | ||
112 | for arg in args.into_iter().skip(1) { | ||
113 | acc = acc.div(*unwrap_number(&arg))?; | ||
114 | } | ||
115 | Ok(LispExpr::Number(acc)) | ||
116 | } | ||
117 | }); | ||
118 | primitive!(env, Some(0), "quit", |_, app| { | ||
119 | app.quit(); | ||
120 | Ok(LispExpr::Unit) | ||
121 | }); | ||
122 | env | ||
123 | } | ||
124 | 14 | ||
125 | pub fn eval( | 15 | pub fn eval(expr: &LispExpr, app: &mut AppState) -> Result<LispExpr, LispError> { |
126 | expr: &LispExpr, | ||
127 | app: &mut AppState, | ||
128 | extra_env: Option<&Environment>, | ||
129 | ) -> Result<LispExpr, LispError> { | ||
130 | match expr { | 16 | match expr { |
131 | LispExpr::Unit => Ok(expr.clone()), | 17 | LispExpr::Unit => Ok(expr.clone()), |
132 | LispExpr::StringLit(_) => Ok(expr.clone()), | 18 | LispExpr::StringLit(_) => Ok(expr.clone()), |
133 | LispExpr::Number(_) => Ok(expr.clone()), | 19 | LispExpr::Number(_) => Ok(expr.clone()), |
134 | LispExpr::BoolLit(_) => Ok(expr.clone()), | 20 | LispExpr::BoolLit(_) => Ok(expr.clone()), |
135 | LispExpr::Ident(ref id) => { | 21 | LispExpr::Ident(ref id) => lookup_extended(&app.lisp_env, id), |
136 | lookup_extended(extra_env.unwrap_or(&Environment::new()), &app.lisp_env, id) | ||
137 | } | ||
138 | LispExpr::List(li) => { | 22 | LispExpr::List(li) => { |
139 | let func_expr = &li[0]; | 23 | let func_expr = &li[0]; |
140 | match func_expr { | 24 | match func_expr { |
@@ -143,12 +27,12 @@ pub fn eval( | |||
143 | "set!" => set_var(&li[1..], app), | 27 | "set!" => set_var(&li[1..], app), |
144 | "lambda" => create_lambda(&li[1..]), | 28 | "lambda" => create_lambda(&li[1..]), |
145 | _ => { | 29 | _ => { |
146 | let func_expr = eval(&func_expr, app, extra_env)?; | 30 | let func_expr = eval(&func_expr, app)?; |
147 | match func_expr { | 31 | match func_expr { |
148 | LispExpr::PrimitiveFunc(f) => { | 32 | LispExpr::PrimitiveFunc(f) => { |
149 | let mut args = Vec::new(); | 33 | let mut args = Vec::new(); |
150 | for item in li[1..].iter() { | 34 | for item in li[1..].iter() { |
151 | args.push(eval(item, app, extra_env)?); | 35 | args.push(eval(item, app)?); |
152 | } | 36 | } |
153 | f.call(&args, app) | 37 | f.call(&args, app) |
154 | } | 38 | } |
@@ -156,50 +40,83 @@ pub fn eval( | |||
156 | info!("eval custom func"); | 40 | info!("eval custom func"); |
157 | let mut args = Vec::new(); | 41 | let mut args = Vec::new(); |
158 | for item in li[1..].iter() { | 42 | for item in li[1..].iter() { |
159 | args.push(eval(item, app, extra_env)?); | 43 | args.push(eval(item, app)?); |
160 | } | 44 | } |
161 | if f.params.len() != args.len() { | 45 | if f.params.len() != args.len() { |
162 | info!("too many or too little number of args"); | 46 | info!("too many or too little number of args"); |
163 | Err(LispError::EvalError) // too many or too little number of args | 47 | Err(EvalError::ArgumentCount(Some(f.params.len() as u32)) |
48 | .into()) | ||
164 | } else { | 49 | } else { |
165 | let mut local_env: Environment = | 50 | let mut nested_env: Environment = |
166 | f.params.into_iter().zip(args).collect(); | 51 | f.params.into_iter().zip(args).collect(); |
167 | local_env.extend(app.lisp_env.clone()); | 52 | app.lisp_env.push(nested_env); |
168 | if let Some(env) = extra_env { | 53 | let result = if f.body.is_empty() { |
169 | local_env.extend(env.clone()); | 54 | Ok(LispExpr::Unit) |
170 | } | ||
171 | if f.body.is_empty() { | ||
172 | return Ok(LispExpr::Unit); | ||
173 | } else { | 55 | } else { |
174 | eval(&LispExpr::List(f.body), app, Some(&local_env)) | 56 | eval(&LispExpr::List(f.body), app) |
175 | } | 57 | }; |
58 | app.lisp_env.pop(); | ||
59 | return result; | ||
176 | } | 60 | } |
177 | } | 61 | } |
178 | _ => Err(LispError::EvalError), | 62 | _ => Err(EvalError::BadForm.into()), |
179 | } | 63 | } |
180 | } | 64 | } |
181 | }, | 65 | }, |
182 | _ => Err(LispError::EvalError), | 66 | _ => Err(EvalError::BadForm.into()), |
183 | } | 67 | } |
184 | } | 68 | } |
185 | _ => Err(LispError::ParseError), | 69 | _ => Err(EvalError::BadForm.into()), |
186 | } | 70 | } |
187 | } | 71 | } |
188 | 72 | ||
189 | pub fn define_var(args: &[LispExpr], app: &mut AppState) -> Result<LispExpr, LispError> { | 73 | pub fn define_var(args: &[LispExpr], app: &mut AppState) -> Result<LispExpr, LispError> { |
190 | if args.len() != 2 { | 74 | if args.len() != 2 { |
191 | error!("Invalid arity for `define`"); | 75 | error!("Invalid arity for `define`"); |
192 | return Err(LispError::EvalError); | 76 | return Err(EvalError::ArgumentCount(Some(2)).into()); |
193 | } | 77 | } |
194 | match args { | 78 | match args { |
195 | [LispExpr::Ident(id), expr] => { | 79 | [LispExpr::Ident(id), expr] => { |
196 | let value = eval(&expr, app, None)?; | 80 | let value = eval(&expr, app)?; |
197 | app.lisp_env.insert(id.into(), value); | 81 | let local_env = app.lisp_env.last_mut(); |
82 | if let Some(env) = local_env { | ||
83 | env.insert(id.into(), value); | ||
84 | } else { | ||
85 | error!("Unable to create global definition"); | ||
86 | return Err(EvalError::BadForm.into()); | ||
87 | } | ||
88 | return Ok(LispExpr::Unit); | ||
89 | } | ||
90 | [LispExpr::List(shorthand), LispExpr::List(body)] => { | ||
91 | // (define (func arg) <body>) shorthand | ||
92 | |||
93 | let id = unwrap_ident(shorthand[0].clone()); | ||
94 | let params = if shorthand.len() > 1 { | ||
95 | &shorthand[1..] | ||
96 | } else { | ||
97 | &[] | ||
98 | } | ||
99 | .to_vec() | ||
100 | .into_iter() | ||
101 | .map(|arg| arg.try_into()) | ||
102 | .collect::<Result<Vec<Ident>, LispError>>()?; | ||
103 | let value = LispExpr::Function(LispFunction { | ||
104 | params, | ||
105 | body: body.to_vec(), | ||
106 | }); | ||
107 | |||
108 | let local_env = app.lisp_env.last_mut(); | ||
109 | if let Some(env) = local_env { | ||
110 | env.insert(id.into(), value); | ||
111 | } else { | ||
112 | error!("Unable to create global definition"); | ||
113 | return Err(EvalError::BadForm.into()); | ||
114 | } | ||
198 | return Ok(LispExpr::Unit); | 115 | return Ok(LispExpr::Unit); |
199 | } | 116 | } |
200 | _ => { | 117 | _ => { |
201 | error!("Invalid usage of `define`"); | 118 | error!("Invalid usage of `define`"); |
202 | return Err(LispError::EvalError); | 119 | Err(EvalError::BadForm.into()) |
203 | } | 120 | } |
204 | } | 121 | } |
205 | } | 122 | } |
@@ -207,19 +124,24 @@ pub fn define_var(args: &[LispExpr], app: &mut AppState) -> Result<LispExpr, Lis | |||
207 | pub fn set_var(args: &[LispExpr], app: &mut AppState) -> Result<LispExpr, LispError> { | 124 | pub fn set_var(args: &[LispExpr], app: &mut AppState) -> Result<LispExpr, LispError> { |
208 | if args.len() != 2 { | 125 | if args.len() != 2 { |
209 | error!("Invalid arity for `define`"); | 126 | error!("Invalid arity for `define`"); |
210 | return Err(LispError::EvalError); | 127 | return Err(EvalError::ArgumentCount(Some(2)).into()); |
211 | } | 128 | } |
212 | match args { | 129 | match args { |
213 | [LispExpr::Ident(id), expr] => { | 130 | [LispExpr::Ident(id), expr] => { |
214 | let value = eval(&expr, app, None)?; | 131 | let value = eval(&expr, app)?; |
215 | return app | 132 | let local_env = app.lisp_env.last_mut(); |
216 | .lisp_env | 133 | if let Some(env) = local_env { |
217 | .insert(id.into(), value) | 134 | return env |
218 | .ok_or(LispError::EvalError); | 135 | .insert(id.into(), value) |
136 | .ok_or(EvalError::UnboundVariable(id.into()).into()); | ||
137 | } else { | ||
138 | error!("Unable to set in global env!"); | ||
139 | return Err(EvalError::BadForm.into()); | ||
140 | } | ||
219 | } | 141 | } |
220 | _ => { | 142 | _ => { |
221 | error!("Invalid usage of `define`"); | 143 | error!("Invalid usage of `set!`"); |
222 | return Err(LispError::EvalError); | 144 | return Err(EvalError::BadForm.into()); |
223 | } | 145 | } |
224 | } | 146 | } |
225 | } | 147 | } |
@@ -228,7 +150,7 @@ pub fn create_lambda(cdr: &[LispExpr]) -> Result<LispExpr, LispError> { | |||
228 | if cdr.len() != 2 { | 150 | if cdr.len() != 2 { |
229 | // needs params and body | 151 | // needs params and body |
230 | error!("needs params and body"); | 152 | error!("needs params and body"); |
231 | return Err(LispError::EvalError); | 153 | return Err(EvalError::ArgumentCount(Some(2)).into()); |
232 | } | 154 | } |
233 | info!("creating lambda"); | 155 | info!("creating lambda"); |
234 | match cdr { | 156 | match cdr { |
@@ -244,23 +166,22 @@ pub fn create_lambda(cdr: &[LispExpr]) -> Result<LispExpr, LispError> { | |||
244 | })); | 166 | })); |
245 | } | 167 | } |
246 | _ => { | 168 | _ => { |
247 | error!("Invalid usage of `define`"); | 169 | error!("Invalid usage of `lambda`"); |
248 | return Err(LispError::EvalError); | 170 | return Err(EvalError::BadForm.into()); |
249 | } | 171 | } |
250 | } | 172 | } |
251 | } | 173 | } |
252 | 174 | ||
253 | pub fn lookup_extended( | 175 | pub fn lookup_extended(env_list: &[Environment], key: &str) -> Result<LispExpr, LispError> { |
254 | local: &Environment, | 176 | if env_list.is_empty() { |
255 | super_: &Environment, | 177 | return Err(EvalError::UnboundVariable(key.into()).into()); |
256 | key: &str, | ||
257 | ) -> Result<LispExpr, LispError> { | ||
258 | if let Some(e) = local.get(key) { | ||
259 | Ok(e.clone()) | ||
260 | } else if let Some(e) = super_.get(key) { | ||
261 | Ok(e.clone()) | ||
262 | } else { | 178 | } else { |
263 | Err(LispError::EvalError) | 179 | let local_env = env_list.last().unwrap(); |
180 | if let Some(val) = local_env.get(key) { | ||
181 | return Ok(val.clone()); | ||
182 | } else { | ||
183 | return lookup_extended(&env_list[..env_list.len() - 1], key); | ||
184 | } | ||
264 | } | 185 | } |
265 | } | 186 | } |
266 | 187 | ||
diff --git a/src/lisp/prelude.rs b/src/lisp/prelude.rs new file mode 100644 index 0000000..ad94cd2 --- /dev/null +++ b/src/lisp/prelude.rs | |||
@@ -0,0 +1,139 @@ | |||
1 | use crate::{ | ||
2 | lisp::{ | ||
3 | error::{EvalError, LispError}, | ||
4 | expr::LispExpr, | ||
5 | number::LispNumber, | ||
6 | Environment, | ||
7 | }, | ||
8 | primitive, | ||
9 | }; | ||
10 | |||
11 | use std::convert::TryInto; | ||
12 | |||
13 | #[macro_export] | ||
14 | macro_rules! primitive { | ||
15 | ($env:expr, $arity:expr, $name:expr, $closure:expr) => { | ||
16 | let val = crate::lisp::expr::LispExpr::PrimitiveFunc(crate::lisp::expr::PrimitiveFunc { | ||
17 | arity: $arity, | ||
18 | closure: $closure, | ||
19 | }); | ||
20 | let _ = $env.insert($name.to_string(), val); | ||
21 | }; | ||
22 | } | ||
23 | |||
24 | pub fn new_env() -> Environment { | ||
25 | let mut env = Environment::new(); | ||
26 | |||
27 | primitive!(env, Some(2), "+", |args, _| { | ||
28 | let nums = args | ||
29 | .into_iter() | ||
30 | .map(|arg| arg.try_into()) | ||
31 | .collect::<Result<Vec<&LispNumber>, LispError>>()?; | ||
32 | return Ok(LispExpr::Number( | ||
33 | nums.iter().fold(LispNumber::Integer(0), |acc, &x| acc + *x), | ||
34 | )); | ||
35 | }); | ||
36 | |||
37 | primitive!(env, Some(2), "-", |args, _| { | ||
38 | let nums = args | ||
39 | .into_iter() | ||
40 | .map(|arg| arg.try_into()) | ||
41 | .collect::<Result<Vec<&LispNumber>, LispError>>()?; | ||
42 | let mut acc = nums[0].clone(); | ||
43 | for arg in nums.into_iter().skip(1) { | ||
44 | acc = acc - *arg; | ||
45 | } | ||
46 | Ok(LispExpr::Number(acc)) | ||
47 | }); | ||
48 | |||
49 | primitive!(env, Some(2), "*", |args, _| { | ||
50 | let nums = args | ||
51 | .into_iter() | ||
52 | .map(|arg| arg.try_into()) | ||
53 | .collect::<Result<Vec<&LispNumber>, LispError>>()?; | ||
54 | return Ok(LispExpr::Number( | ||
55 | nums.iter().fold(LispNumber::Integer(1), |acc, &x| acc * *x), | ||
56 | )); | ||
57 | }); | ||
58 | |||
59 | primitive!(env, Some(2), "/", |args, _| { | ||
60 | let nums = args | ||
61 | .into_iter() | ||
62 | .map(|arg| arg.try_into()) | ||
63 | .collect::<Result<Vec<&LispNumber>, LispError>>()?; | ||
64 | let mut acc = nums[0].clone(); | ||
65 | for arg in nums.into_iter().skip(1) { | ||
66 | acc = acc.div(*arg)?; | ||
67 | } | ||
68 | Ok(LispExpr::Number(acc)) | ||
69 | }); | ||
70 | |||
71 | primitive!(env, Some(0), "toggle-grid", |_, app| { | ||
72 | app.toggle_grid(); | ||
73 | Ok(LispExpr::Unit) | ||
74 | }); | ||
75 | |||
76 | primitive!(env, Some(3), "if", |args, _| { | ||
77 | match args { | ||
78 | [predicate, then, else_] => { | ||
79 | if matches!(predicate, LispExpr::BoolLit(false)) { | ||
80 | Ok(else_.clone()) | ||
81 | } else { | ||
82 | Ok(then.clone()) | ||
83 | } | ||
84 | } | ||
85 | _ => { | ||
86 | panic!("panicked at `if` expression") | ||
87 | } | ||
88 | } | ||
89 | }); | ||
90 | |||
91 | primitive!(env, Some(2), "and", |args, _| { | ||
92 | if args | ||
93 | .iter() | ||
94 | .any(|arg| matches!(arg, LispExpr::BoolLit(false))) | ||
95 | { | ||
96 | Ok(LispExpr::BoolLit(false)) | ||
97 | } else { | ||
98 | Ok(LispExpr::BoolLit(true)) | ||
99 | } | ||
100 | }); | ||
101 | |||
102 | primitive!(env, Some(2), "or", |args, _| { | ||
103 | if args | ||
104 | .iter() | ||
105 | .any(|arg| matches!(arg, LispExpr::BoolLit(true))) | ||
106 | { | ||
107 | Ok(LispExpr::BoolLit(true)) | ||
108 | } else { | ||
109 | Ok(LispExpr::BoolLit(false)) | ||
110 | } | ||
111 | }); | ||
112 | |||
113 | primitive!(env, Some(1), "not", |args, _| { | ||
114 | match args { | ||
115 | [val] => { | ||
116 | if matches!(val, LispExpr::BoolLit(false)) { | ||
117 | Ok(LispExpr::BoolLit(true)) | ||
118 | } else { | ||
119 | Ok(LispExpr::BoolLit(false)) | ||
120 | } | ||
121 | } | ||
122 | _ => Err(EvalError::ArgumentCount(Some(1)).into()), | ||
123 | } | ||
124 | }); | ||
125 | |||
126 | primitive!(env, None, "begin", |args, _| { | ||
127 | if args.is_empty() { | ||
128 | Err(EvalError::ArgumentCount(None).into()) | ||
129 | } else { | ||
130 | Ok(args.into_iter().last().unwrap().clone()) | ||
131 | } | ||
132 | }); | ||
133 | |||
134 | primitive!(env, Some(0), "quit", |_, app| { | ||
135 | app.quit(); | ||
136 | Ok(LispExpr::Unit) | ||
137 | }); | ||
138 | env | ||
139 | } | ||
diff --git a/src/lisp/primitives.rs b/src/lisp/primitives.rs deleted file mode 100644 index 1c9fe8e..0000000 --- a/src/lisp/primitives.rs +++ /dev/null | |||
@@ -1,10 +0,0 @@ | |||
1 | #[macro_export] | ||
2 | macro_rules! primitive { | ||
3 | ($env:expr, $arity:expr, $name:expr, $closure:expr) => { | ||
4 | let val = crate::lisp::expr::LispExpr::PrimitiveFunc(crate::lisp::expr::PrimitiveFunc { | ||
5 | arity: $arity, | ||
6 | closure: $closure, | ||
7 | }); | ||
8 | let _ = $env.insert($name.to_string(), val); | ||
9 | }; | ||
10 | } | ||