aboutsummaryrefslogtreecommitdiff
path: root/src/lisp/eval.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/lisp/eval.rs')
-rw-r--r--src/lisp/eval.rs275
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 @@
1use crate::{
2 app::AppState,
3 lisp::{
4 error::LispError,
5 expr::{LispExpr, LispFunction},
6 number::LispNumber,
7 Environment,
8 },
9 primitive,
10};
11
12use log::{error, info};
13
14pub 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
121pub 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
185pub 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
203pub 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
223pub 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
249pub 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
263pub fn unwrap_number(n: &LispExpr) -> &LispNumber {
264 match n {
265 LispExpr::Number(i) => i,
266 _ => panic!("unwrap_number expected number"),
267 }
268}
269
270pub fn unwrap_ident(i: LispExpr) -> String {
271 match i {
272 LispExpr::Ident(i) => i,
273 _ => panic!("unwrap_ident expected string"),
274 }
275}