diff options
author | Akshay <[email protected]> | 2021-04-10 05:53:35 +0100 |
---|---|---|
committer | Akshay <[email protected]> | 2021-04-10 05:53:35 +0100 |
commit | fac291446581b1dad939545bafd6da349c4dfd40 (patch) | |
tree | 8f300ff91e3620fadbee62b3829b76970a318b43 /src/lisp | |
parent | 7f054debdf43faa57799a319c4fe24ffcc2ec4b6 (diff) |
add `for` primitive
Diffstat (limited to 'src/lisp')
-rw-r--r-- | src/lisp/eval.rs | 63 | ||||
-rw-r--r-- | src/lisp/prelude.rs | 18 |
2 files changed, 81 insertions, 0 deletions
diff --git a/src/lisp/eval.rs b/src/lisp/eval.rs index 329b6ab..0cf267c 100644 --- a/src/lisp/eval.rs +++ b/src/lisp/eval.rs | |||
@@ -41,6 +41,7 @@ where | |||
41 | "lambda" => create_lambda(&li[1..]), | 41 | "lambda" => create_lambda(&li[1..]), |
42 | "if" => self.eval_if(&li[1..]), | 42 | "if" => self.eval_if(&li[1..]), |
43 | "cond" => self.eval_cond(&li[1..]), | 43 | "cond" => self.eval_cond(&li[1..]), |
44 | "for" => self.eval_for(&li[1..]), | ||
44 | "quote" => Ok(apply_quote(&li[1])), | 45 | "quote" => Ok(apply_quote(&li[1])), |
45 | "let" => self.eval_let(&li[1..]), | 46 | "let" => self.eval_let(&li[1..]), |
46 | _ => { | 47 | _ => { |
@@ -214,6 +215,55 @@ where | |||
214 | } | 215 | } |
215 | } | 216 | } |
216 | 217 | ||
218 | pub fn eval_for(&mut self, args: &[LispExpr]) -> Result<LispExpr, LispError> { | ||
219 | let arity = Arity::Exact(2); | ||
220 | let valid_binding_stmt = |expr: &LispExpr| | ||
221 | matches!( | ||
222 | expr, | ||
223 | LispExpr::List(v) | ||
224 | if v.len() == 2 | ||
225 | && matches!(v[0], LispExpr::Ident(_))); | ||
226 | |||
227 | if !arity.check(args) { | ||
228 | Err(arity.to_error()) | ||
229 | } else { | ||
230 | let nested_env = Environment::new(); | ||
231 | self.app.lisp_env.push(nested_env); | ||
232 | match args { | ||
233 | [binding, body] => { | ||
234 | if valid_binding_stmt(binding) { | ||
235 | let binding = binding.unwrap_list(); | ||
236 | let binding_name = binding[0].unwrap_ident(); | ||
237 | let binding_ls = self.eval(&binding[1])?; | ||
238 | if matches!(binding_ls, LispExpr::List(_)) { | ||
239 | let binding_ls = binding_ls.unwrap_list(); | ||
240 | let mut result = vec![]; | ||
241 | for bind_val in binding_ls.iter() { | ||
242 | let value = self.eval(&bind_val)?; | ||
243 | if let Some(env) = self.app.lisp_env.last_mut() { | ||
244 | env.insert(binding_name.clone(), value); | ||
245 | } | ||
246 | result.push(self.eval(body)?); | ||
247 | } | ||
248 | self.app.lisp_env.pop(); | ||
249 | Ok(LispExpr::List(result)) | ||
250 | } else { | ||
251 | error!("invalid binding form"); | ||
252 | Err(EvalError::BadForm.into()) | ||
253 | } | ||
254 | } else { | ||
255 | error!("invalid binding form"); | ||
256 | Err(EvalError::BadForm.into()) | ||
257 | } | ||
258 | } | ||
259 | _ => { | ||
260 | error!("invalid for loop args"); | ||
261 | Err(EvalError::BadForm.into()) | ||
262 | }, | ||
263 | } | ||
264 | } | ||
265 | } | ||
266 | |||
217 | pub fn eval_let(&mut self, args: &[LispExpr]) -> Result<LispExpr, LispError> { | 267 | pub fn eval_let(&mut self, args: &[LispExpr]) -> Result<LispExpr, LispError> { |
218 | let arity = Arity::Exact(2); | 268 | let arity = Arity::Exact(2); |
219 | let valid_binding_stmt = | 269 | let valid_binding_stmt = |
@@ -326,7 +376,9 @@ mod tests { | |||
326 | let mut app = AppState::init(100, 100, &sdl_context, &ttf_context, None, None).unwrap(); | 376 | let mut app = AppState::init(100, 100, &sdl_context, &ttf_context, None, None).unwrap(); |
327 | eval_arithmetic(&mut app); | 377 | eval_arithmetic(&mut app); |
328 | eval_logical(&mut app); | 378 | eval_logical(&mut app); |
379 | eval_looping(&mut app); | ||
329 | eval_quote(&mut app); | 380 | eval_quote(&mut app); |
381 | eval_std_tests(&mut app); | ||
330 | } | 382 | } |
331 | 383 | ||
332 | fn eval_arithmetic(app: &mut AppState) { | 384 | fn eval_arithmetic(app: &mut AppState) { |
@@ -380,4 +432,15 @@ mod tests { | |||
380 | assert!(!run("(not #t)", app).cast_bool()); | 432 | assert!(!run("(not #t)", app).cast_bool()); |
381 | assert_eq!(run("(not #f)", app), run("(not (not #t))", app)); | 433 | assert_eq!(run("(not #f)", app), run("(not (not #t))", app)); |
382 | } | 434 | } |
435 | |||
436 | fn eval_looping(app: &mut AppState) { | ||
437 | assert_eq!( | ||
438 | run("(for (i '(1 2 3)) (+ i 2))", app), | ||
439 | run("(map (lambda (x) (+ x 2)) '(1 2 3))", app) | ||
440 | ); | ||
441 | } | ||
442 | |||
443 | fn eval_std_tests(app: &mut AppState) { | ||
444 | crate::utils::load_script("src/lisp/test.lisp", app).unwrap(); | ||
445 | } | ||
383 | } | 446 | } |
diff --git a/src/lisp/prelude.rs b/src/lisp/prelude.rs index f5ff13a..e24ade5 100644 --- a/src/lisp/prelude.rs +++ b/src/lisp/prelude.rs | |||
@@ -437,5 +437,23 @@ pub fn new_env() -> Result<Environment, LispError> { | |||
437 | } | 437 | } |
438 | }); | 438 | }); |
439 | 439 | ||
440 | primitive!(env, Arity::Exact(2), "range", |args, _| { | ||
441 | if type_match!( | ||
442 | args, | ||
443 | 0 => LispExpr::Number(LispNumber::Integer(_)), | ||
444 | 1 => LispExpr::Number(LispNumber::Integer(_))) | ||
445 | { | ||
446 | let lower = args[0].unwrap_number().unwrap_integer(); | ||
447 | let upper = args[1].unwrap_number().unwrap_integer(); | ||
448 | Ok(LispExpr::List( | ||
449 | (lower..upper) | ||
450 | .map(|i| LispExpr::Number(LispNumber::Integer(i))) | ||
451 | .collect::<Vec<_>>(), | ||
452 | )) | ||
453 | } else { | ||
454 | Err(EvalError::TypeMismatch.into()) | ||
455 | } | ||
456 | }); | ||
457 | |||
440 | Ok(env) | 458 | Ok(env) |
441 | } | 459 | } |