diff options
Diffstat (limited to 'src/lisp/prelude.rs')
-rw-r--r-- | src/lisp/prelude.rs | 104 |
1 files changed, 91 insertions, 13 deletions
diff --git a/src/lisp/prelude.rs b/src/lisp/prelude.rs index d8a930b..dffd9f4 100644 --- a/src/lisp/prelude.rs +++ b/src/lisp/prelude.rs | |||
@@ -3,11 +3,12 @@ use crate::{ | |||
3 | lisp::{ | 3 | lisp::{ |
4 | error::{EvalError, LispError}, | 4 | error::{EvalError, LispError}, |
5 | eval::eval, | 5 | eval::eval, |
6 | expr::{is_ident, Arity, LispExpr}, | 6 | expr::{Arity, LispExpr}, |
7 | number::LispNumber, | 7 | number::LispNumber, |
8 | Environment, | 8 | Environment, |
9 | }, | 9 | }, |
10 | primitive, | 10 | primitive, |
11 | utils::load_script, | ||
11 | }; | 12 | }; |
12 | 13 | ||
13 | use std::{convert::TryInto, fs::File, io::Write}; | 14 | use std::{convert::TryInto, fs::File, io::Write}; |
@@ -27,7 +28,7 @@ macro_rules! primitive { | |||
27 | 28 | ||
28 | #[macro_export] | 29 | #[macro_export] |
29 | macro_rules! type_match { | 30 | macro_rules! type_match { |
30 | ($args:expr, $($range:expr => $kind:pat),+) => { | 31 | ($args:expr, $($range:literal => $kind:pat),+) => { |
31 | { | 32 | { |
32 | let mut temp_vec = vec![]; | 33 | let mut temp_vec = vec![]; |
33 | $( | 34 | $( |
@@ -35,10 +36,21 @@ macro_rules! type_match { | |||
35 | )+ | 36 | )+ |
36 | temp_vec.iter().all(|&t| t) | 37 | temp_vec.iter().all(|&t| t) |
37 | } | 38 | } |
39 | }; | ||
40 | ($args:expr, $($range:expr => $kind:pat),+) => { | ||
41 | { | ||
42 | let mut temp_vec = vec![]; | ||
43 | $( | ||
44 | for arg in &$args[$range] { | ||
45 | temp_vec.push(matches!(arg, $kind)); | ||
46 | } | ||
47 | )+ | ||
48 | temp_vec.iter().all(|&t| t) | ||
49 | } | ||
38 | } | 50 | } |
39 | } | 51 | } |
40 | 52 | ||
41 | pub fn new_env() -> Environment { | 53 | pub fn new_env() -> Result<Environment, LispError> { |
42 | let mut env = Environment::new(); | 54 | let mut env = Environment::new(); |
43 | 55 | ||
44 | primitive!(env, Arity::Atleast(2), "+", |args, _| { | 56 | primitive!(env, Arity::Atleast(2), "+", |args, _| { |
@@ -113,7 +125,7 @@ pub fn new_env() -> Environment { | |||
113 | }); | 125 | }); |
114 | 126 | ||
115 | primitive!(env, Arity::Exact(1), "not", |args, _| { | 127 | primitive!(env, Arity::Exact(1), "not", |args, _| { |
116 | if matches!(&args[0], LispExpr::BoolLit(false)) { | 128 | if type_match!(args, 0 => LispExpr::BoolLit(false)) { |
117 | Ok(LispExpr::BoolLit(true)) | 129 | Ok(LispExpr::BoolLit(true)) |
118 | } else { | 130 | } else { |
119 | Ok(LispExpr::BoolLit(false)) | 131 | Ok(LispExpr::BoolLit(false)) |
@@ -121,11 +133,7 @@ pub fn new_env() -> Environment { | |||
121 | }); | 133 | }); |
122 | 134 | ||
123 | primitive!(env, Arity::Atleast(1), "begin", |args, _| { | 135 | primitive!(env, Arity::Atleast(1), "begin", |args, _| { |
124 | if args.is_empty() { | 136 | Ok(args.into_iter().last().unwrap().clone()) |
125 | Err(EvalError::ArgumentCount(Arity::Atleast(1)).into()) | ||
126 | } else { | ||
127 | Ok(args.into_iter().last().unwrap().clone()) | ||
128 | } | ||
129 | }); | 137 | }); |
130 | 138 | ||
131 | primitive!(env, Arity::Exact(0), "quit", |_, app| { | 139 | primitive!(env, Arity::Exact(0), "quit", |_, app| { |
@@ -136,7 +144,7 @@ pub fn new_env() -> Environment { | |||
136 | primitive!(env, Arity::Exact(2), "eq?", |args, app| { | 144 | primitive!(env, Arity::Exact(2), "eq?", |args, app| { |
137 | let s = &args[0]; | 145 | let s = &args[0]; |
138 | let o = &args[1]; | 146 | let o = &args[1]; |
139 | info!("comparing {} {}", s, o); | 147 | info!("comparing s: {} and o: {}", s, o); |
140 | let result = s.compare(o, &app.lisp_env); | 148 | let result = s.compare(o, &app.lisp_env); |
141 | result.map(LispExpr::BoolLit) | 149 | result.map(LispExpr::BoolLit) |
142 | }); | 150 | }); |
@@ -188,7 +196,7 @@ pub fn new_env() -> Environment { | |||
188 | primitive!(env, Arity::Exact(1), "string-len", |args, _| { | 196 | primitive!(env, Arity::Exact(1), "string-len", |args, _| { |
189 | if type_match!(args, 0 => LispExpr::StringLit(_)) { | 197 | if type_match!(args, 0 => LispExpr::StringLit(_)) { |
190 | Ok(LispExpr::Number(LispNumber::Integer( | 198 | Ok(LispExpr::Number(LispNumber::Integer( |
191 | args[0].as_ref().len() as i64, | 199 | args[0].unwrap_stringlit().len() as i64, |
192 | ))) | 200 | ))) |
193 | } else { | 201 | } else { |
194 | Err(EvalError::TypeMismatch.into()) | 202 | Err(EvalError::TypeMismatch.into()) |
@@ -218,7 +226,7 @@ pub fn new_env() -> Environment { | |||
218 | 0 | 226 | 0 |
219 | }; | 227 | }; |
220 | if let [LispExpr::Quote(kind, _)] = args { | 228 | if let [LispExpr::Quote(kind, _)] = args { |
221 | if is_ident(kind) { | 229 | if matches!(kind.as_ref(), LispExpr::Ident(_)) { |
222 | match (&**kind).as_ref() { | 230 | match (&**kind).as_ref() { |
223 | "fill" => app.brush = Brush::Fill, | 231 | "fill" => app.brush = Brush::Fill, |
224 | "circle" => app.brush = Brush::new(old_size), | 232 | "circle" => app.brush = Brush::new(old_size), |
@@ -250,9 +258,79 @@ pub fn new_env() -> Environment { | |||
250 | } | 258 | } |
251 | }); | 259 | }); |
252 | 260 | ||
261 | primitive!(env, Arity::Exact(2), "filter", |args, app| { | ||
262 | let mut apply_filter = | ||
263 | |func: &LispExpr, ls: &Vec<LispExpr>| -> Result<Vec<LispExpr>, LispError> { | ||
264 | let mut result = vec![]; | ||
265 | for arg in ls.into_iter() { | ||
266 | if eval(&LispExpr::List(vec![func.clone(), arg.clone()]), app)?.cast_bool() { | ||
267 | result.push(arg.clone()) | ||
268 | } | ||
269 | } | ||
270 | Ok(result) | ||
271 | }; | ||
272 | if matches!(&args[0], LispExpr::Function(_) | LispExpr::PrimitiveFunc(_)) { | ||
273 | match &args[1] { | ||
274 | LispExpr::List(ls) => return Ok(LispExpr::List(apply_filter(&args[0], ls)?)), | ||
275 | _ => return Err(EvalError::TypeMismatch.into()), | ||
276 | } | ||
277 | } else { | ||
278 | return Err(EvalError::TypeMismatch.into()); | ||
279 | } | ||
280 | }); | ||
281 | |||
282 | primitive!(env, Arity::Exact(1), "car", |args, _| { | ||
283 | if type_match!(args, 0 => LispExpr::List(_)) { | ||
284 | return Ok(args[0].unwrap_list().swap_remove(0)); | ||
285 | } else if type_match!(args, 0 => LispExpr::Unit) { | ||
286 | return Err(EvalError::AccessEmptyList.into()); | ||
287 | } else { | ||
288 | return Err(EvalError::TypeMismatch.into()); | ||
289 | } | ||
290 | }); | ||
291 | |||
292 | primitive!(env, Arity::Exact(1), "cdr", |args, _| { | ||
293 | if type_match!(args, 0 => LispExpr::List(_)) { | ||
294 | let mut ls = args[0].unwrap_list(); | ||
295 | if ls.len() == 0 { | ||
296 | return Err(EvalError::AccessEmptyList.into()); | ||
297 | } else if ls.len() == 1 { | ||
298 | return Ok(LispExpr::Unit); | ||
299 | } else { | ||
300 | ls.remove(0); | ||
301 | return Ok(LispExpr::List(ls)); | ||
302 | } | ||
303 | } else if type_match!(args, 0 => LispExpr::Unit) { | ||
304 | return Err(EvalError::AccessEmptyList.into()); | ||
305 | } else { | ||
306 | return Err(EvalError::TypeMismatch.into()); | ||
307 | } | ||
308 | }); | ||
309 | |||
253 | primitive!(env, Arity::Atleast(1), "list", |args, _| { | 310 | primitive!(env, Arity::Atleast(1), "list", |args, _| { |
254 | return Ok(LispExpr::List(args.to_vec())); | 311 | return Ok(LispExpr::List(args.to_vec())); |
255 | }); | 312 | }); |
256 | 313 | ||
257 | env | 314 | primitive!(env, Arity::Exact(1), "load-script", |args, app| { |
315 | if type_match!(args, 0 => LispExpr::StringLit(_)) { | ||
316 | let path = args[0].unwrap_stringlit(); | ||
317 | load_script(&path, app).map(|_| LispExpr::Unit) | ||
318 | } else { | ||
319 | return Err(EvalError::TypeMismatch.into()); | ||
320 | } | ||
321 | }); | ||
322 | |||
323 | primitive!(env, Arity::Atleast(1), "error", |args, _| { | ||
324 | if type_match!(args, 0 => LispExpr::StringLit(_)) { | ||
325 | let mut s = String::from(args[0].unwrap_stringlit()); | ||
326 | for arg in args.into_iter().skip(1) { | ||
327 | s.push_str(&format!(" {}", arg)); | ||
328 | } | ||
329 | return Err(EvalError::Custom(s).into()); | ||
330 | } else { | ||
331 | return Err(EvalError::TypeMismatch.into()); | ||
332 | } | ||
333 | }); | ||
334 | |||
335 | Ok(env) | ||
258 | } | 336 | } |