diff options
Diffstat (limited to 'src/lisp/prelude.rs')
-rw-r--r-- | src/lisp/prelude.rs | 139 |
1 files changed, 139 insertions, 0 deletions
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 | } | ||