diff options
Diffstat (limited to 'src/lisp/eval.rs')
-rw-r--r-- | src/lisp/eval.rs | 275 |
1 files changed, 275 insertions, 0 deletions
diff --git a/src/lisp/eval.rs b/src/lisp/eval.rs new file mode 100644 index 0000000..63c963c --- /dev/null +++ b/src/lisp/eval.rs | |||
@@ -0,0 +1,275 @@ | |||
1 | use crate::{ | ||
2 | app::AppState, | ||
3 | lisp::{ | ||
4 | error::LispError, | ||
5 | expr::{LispExpr, LispFunction}, | ||
6 | number::LispNumber, | ||
7 | Environment, | ||
8 | }, | ||
9 | primitive, | ||
10 | }; | ||
11 | |||
12 | use log::{error, info}; | ||
13 | |||
14 | pub fn with_prelude() -> Environment { | ||
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 | env | ||
119 | } | ||
120 | |||
121 | pub fn eval( | ||
122 | expr: &LispExpr, | ||
123 | app: &mut AppState, | ||
124 | extra_env: Option<&Environment>, | ||
125 | ) -> Result<LispExpr, LispError> { | ||
126 | match expr { | ||
127 | LispExpr::Unit => Ok(expr.clone()), | ||
128 | LispExpr::StringLit(_) => Ok(expr.clone()), | ||
129 | LispExpr::Number(_) => Ok(expr.clone()), | ||
130 | LispExpr::BoolLit(_) => Ok(expr.clone()), | ||
131 | LispExpr::Ident(ref id) => { | ||
132 | lookup_extended(extra_env.unwrap_or(&Environment::new()), &app.lisp_env, id) | ||
133 | } | ||
134 | LispExpr::List(li) => { | ||
135 | let func_expr = &li[0]; | ||
136 | match func_expr { | ||
137 | LispExpr::Ident(s) => match s.as_ref() { | ||
138 | "define" => define_var(&li[1..], app), | ||
139 | "set!" => set_var(&li[1..], app), | ||
140 | "lambda" => create_lambda(&li[1..]), | ||
141 | _ => { | ||
142 | let func_expr = eval(&func_expr, app, extra_env)?; | ||
143 | match func_expr { | ||
144 | LispExpr::PrimitiveFunc(f) => { | ||
145 | let mut args = Vec::new(); | ||
146 | for item in li[1..].iter() { | ||
147 | args.push(eval(item, app, extra_env)?); | ||
148 | } | ||
149 | f.call(&args, app) | ||
150 | } | ||
151 | LispExpr::Function(f) => { | ||
152 | info!("eval custom func"); | ||
153 | let mut args = Vec::new(); | ||
154 | for item in li[1..].iter() { | ||
155 | args.push(eval(item, app, extra_env)?); | ||
156 | } | ||
157 | if f.params.len() != args.len() { | ||
158 | info!("too many or too little number of args"); | ||
159 | Err(LispError::EvalError) // too many or too little number of args | ||
160 | } else { | ||
161 | let mut local_env: Environment = | ||
162 | f.params.into_iter().zip(args).collect(); | ||
163 | local_env.extend(app.lisp_env.clone()); | ||
164 | if let Some(env) = extra_env { | ||
165 | local_env.extend(env.clone()); | ||
166 | } | ||
167 | if f.body.is_empty() { | ||
168 | return Ok(LispExpr::Unit); | ||
169 | } else { | ||
170 | eval(&LispExpr::List(f.body), app, Some(&local_env)) | ||
171 | } | ||
172 | } | ||
173 | } | ||
174 | _ => Err(LispError::EvalError), | ||
175 | } | ||
176 | } | ||
177 | }, | ||
178 | _ => Err(LispError::EvalError), | ||
179 | } | ||
180 | } | ||
181 | _ => Err(LispError::ParseError), | ||
182 | } | ||
183 | } | ||
184 | |||
185 | pub fn define_var(args: &[LispExpr], app: &mut AppState) -> Result<LispExpr, LispError> { | ||
186 | if args.len() != 2 { | ||
187 | error!("Invalid arity for `define`"); | ||
188 | return Err(LispError::EvalError); | ||
189 | } | ||
190 | match args { | ||
191 | [LispExpr::Ident(id), expr] => { | ||
192 | let value = eval(&expr, app, None)?; | ||
193 | app.lisp_env.insert(id.into(), value); | ||
194 | return Ok(LispExpr::Unit); | ||
195 | } | ||
196 | _ => { | ||
197 | error!("Invalid usage of `define`"); | ||
198 | return Err(LispError::EvalError); | ||
199 | } | ||
200 | } | ||
201 | } | ||
202 | |||
203 | pub fn set_var(args: &[LispExpr], app: &mut AppState) -> Result<LispExpr, LispError> { | ||
204 | if args.len() != 2 { | ||
205 | error!("Invalid arity for `define`"); | ||
206 | return Err(LispError::EvalError); | ||
207 | } | ||
208 | match args { | ||
209 | [LispExpr::Ident(id), expr] => { | ||
210 | let value = eval(&expr, app, None)?; | ||
211 | return app | ||
212 | .lisp_env | ||
213 | .insert(id.into(), value) | ||
214 | .ok_or(LispError::EvalError); | ||
215 | } | ||
216 | _ => { | ||
217 | error!("Invalid usage of `define`"); | ||
218 | return Err(LispError::EvalError); | ||
219 | } | ||
220 | } | ||
221 | } | ||
222 | |||
223 | pub fn create_lambda(cdr: &[LispExpr]) -> Result<LispExpr, LispError> { | ||
224 | if cdr.len() != 2 { | ||
225 | // needs params and body | ||
226 | error!("needs params and body"); | ||
227 | return Err(LispError::EvalError); | ||
228 | } | ||
229 | info!("creating lambda"); | ||
230 | match cdr { | ||
231 | [LispExpr::List(params), LispExpr::List(body)] | ||
232 | if params.iter().all(|p| matches!(p, LispExpr::Ident(_))) => | ||
233 | { | ||
234 | return Ok(LispExpr::Function(LispFunction { | ||
235 | params: params | ||
236 | .into_iter() | ||
237 | .map(|p| unwrap_ident(p.clone())) | ||
238 | .collect::<Vec<_>>(), | ||
239 | body: body.clone(), | ||
240 | })); | ||
241 | } | ||
242 | _ => { | ||
243 | error!("Invalid usage of `define`"); | ||
244 | return Err(LispError::EvalError); | ||
245 | } | ||
246 | } | ||
247 | } | ||
248 | |||
249 | pub fn lookup_extended( | ||
250 | local: &Environment, | ||
251 | super_: &Environment, | ||
252 | key: &str, | ||
253 | ) -> Result<LispExpr, LispError> { | ||
254 | if let Some(e) = local.get(key) { | ||
255 | Ok(e.clone()) | ||
256 | } else if let Some(e) = super_.get(key) { | ||
257 | Ok(e.clone()) | ||
258 | } else { | ||
259 | Err(LispError::EvalError) | ||
260 | } | ||
261 | } | ||
262 | |||
263 | pub fn unwrap_number(n: &LispExpr) -> &LispNumber { | ||
264 | match n { | ||
265 | LispExpr::Number(i) => i, | ||
266 | _ => panic!("unwrap_number expected number"), | ||
267 | } | ||
268 | } | ||
269 | |||
270 | pub fn unwrap_ident(i: LispExpr) -> String { | ||
271 | match i { | ||
272 | LispExpr::Ident(i) => i, | ||
273 | _ => panic!("unwrap_ident expected string"), | ||
274 | } | ||
275 | } | ||