diff options
Diffstat (limited to 'src/lisp/prelude.rs')
-rw-r--r-- | src/lisp/prelude.rs | 132 |
1 files changed, 95 insertions, 37 deletions
diff --git a/src/lisp/prelude.rs b/src/lisp/prelude.rs index ad94cd2..6153e4f 100644 --- a/src/lisp/prelude.rs +++ b/src/lisp/prelude.rs | |||
@@ -1,14 +1,16 @@ | |||
1 | use crate::{ | 1 | use crate::{ |
2 | lisp::{ | 2 | lisp::{ |
3 | error::{EvalError, LispError}, | 3 | error::{EvalError, LispError}, |
4 | expr::LispExpr, | 4 | expr::{Arity, LispExpr}, |
5 | number::LispNumber, | 5 | number::LispNumber, |
6 | Environment, | 6 | Environment, |
7 | }, | 7 | }, |
8 | primitive, | 8 | primitive, |
9 | }; | 9 | }; |
10 | 10 | ||
11 | use std::convert::TryInto; | 11 | use std::{convert::TryInto, fs::File, io::Write, path::PathBuf}; |
12 | |||
13 | use log::info; | ||
12 | 14 | ||
13 | #[macro_export] | 15 | #[macro_export] |
14 | macro_rules! primitive { | 16 | macro_rules! primitive { |
@@ -24,7 +26,7 @@ macro_rules! primitive { | |||
24 | pub fn new_env() -> Environment { | 26 | pub fn new_env() -> Environment { |
25 | let mut env = Environment::new(); | 27 | let mut env = Environment::new(); |
26 | 28 | ||
27 | primitive!(env, Some(2), "+", |args, _| { | 29 | primitive!(env, Arity::Atleast(2), "+", |args, _| { |
28 | let nums = args | 30 | let nums = args |
29 | .into_iter() | 31 | .into_iter() |
30 | .map(|arg| arg.try_into()) | 32 | .map(|arg| arg.try_into()) |
@@ -34,7 +36,7 @@ pub fn new_env() -> Environment { | |||
34 | )); | 36 | )); |
35 | }); | 37 | }); |
36 | 38 | ||
37 | primitive!(env, Some(2), "-", |args, _| { | 39 | primitive!(env, Arity::Atleast(2), "-", |args, _| { |
38 | let nums = args | 40 | let nums = args |
39 | .into_iter() | 41 | .into_iter() |
40 | .map(|arg| arg.try_into()) | 42 | .map(|arg| arg.try_into()) |
@@ -46,7 +48,7 @@ pub fn new_env() -> Environment { | |||
46 | Ok(LispExpr::Number(acc)) | 48 | Ok(LispExpr::Number(acc)) |
47 | }); | 49 | }); |
48 | 50 | ||
49 | primitive!(env, Some(2), "*", |args, _| { | 51 | primitive!(env, Arity::Atleast(2), "*", |args, _| { |
50 | let nums = args | 52 | let nums = args |
51 | .into_iter() | 53 | .into_iter() |
52 | .map(|arg| arg.try_into()) | 54 | .map(|arg| arg.try_into()) |
@@ -56,7 +58,7 @@ pub fn new_env() -> Environment { | |||
56 | )); | 58 | )); |
57 | }); | 59 | }); |
58 | 60 | ||
59 | primitive!(env, Some(2), "/", |args, _| { | 61 | primitive!(env, Arity::Atleast(2), "/", |args, _| { |
60 | let nums = args | 62 | let nums = args |
61 | .into_iter() | 63 | .into_iter() |
62 | .map(|arg| arg.try_into()) | 64 | .map(|arg| arg.try_into()) |
@@ -68,27 +70,12 @@ pub fn new_env() -> Environment { | |||
68 | Ok(LispExpr::Number(acc)) | 70 | Ok(LispExpr::Number(acc)) |
69 | }); | 71 | }); |
70 | 72 | ||
71 | primitive!(env, Some(0), "toggle-grid", |_, app| { | 73 | primitive!(env, Arity::Exact(0), "toggle-grid", |_, app| { |
72 | app.toggle_grid(); | 74 | app.toggle_grid(); |
73 | Ok(LispExpr::Unit) | 75 | Ok(LispExpr::Unit) |
74 | }); | 76 | }); |
75 | 77 | ||
76 | primitive!(env, Some(3), "if", |args, _| { | 78 | primitive!(env, Arity::Atleast(2), "and", |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 | 79 | if args |
93 | .iter() | 80 | .iter() |
94 | .any(|arg| matches!(arg, LispExpr::BoolLit(false))) | 81 | .any(|arg| matches!(arg, LispExpr::BoolLit(false))) |
@@ -99,7 +86,7 @@ pub fn new_env() -> Environment { | |||
99 | } | 86 | } |
100 | }); | 87 | }); |
101 | 88 | ||
102 | primitive!(env, Some(2), "or", |args, _| { | 89 | primitive!(env, Arity::Atleast(2), "or", |args, _| { |
103 | if args | 90 | if args |
104 | .iter() | 91 | .iter() |
105 | .any(|arg| matches!(arg, LispExpr::BoolLit(true))) | 92 | .any(|arg| matches!(arg, LispExpr::BoolLit(true))) |
@@ -110,30 +97,101 @@ pub fn new_env() -> Environment { | |||
110 | } | 97 | } |
111 | }); | 98 | }); |
112 | 99 | ||
113 | primitive!(env, Some(1), "not", |args, _| { | 100 | primitive!(env, Arity::Exact(1), "not", |args, _| { |
114 | match args { | 101 | if matches!(&args[0], LispExpr::BoolLit(false)) { |
115 | [val] => { | 102 | Ok(LispExpr::BoolLit(true)) |
116 | if matches!(val, LispExpr::BoolLit(false)) { | 103 | } else { |
117 | Ok(LispExpr::BoolLit(true)) | 104 | Ok(LispExpr::BoolLit(false)) |
118 | } else { | ||
119 | Ok(LispExpr::BoolLit(false)) | ||
120 | } | ||
121 | } | ||
122 | _ => Err(EvalError::ArgumentCount(Some(1)).into()), | ||
123 | } | 105 | } |
124 | }); | 106 | }); |
125 | 107 | ||
126 | primitive!(env, None, "begin", |args, _| { | 108 | primitive!(env, Arity::Atleast(1), "begin", |args, _| { |
127 | if args.is_empty() { | 109 | if args.is_empty() { |
128 | Err(EvalError::ArgumentCount(None).into()) | 110 | Err(EvalError::ArgumentCount(Arity::Atleast(1)).into()) |
129 | } else { | 111 | } else { |
130 | Ok(args.into_iter().last().unwrap().clone()) | 112 | Ok(args.into_iter().last().unwrap().clone()) |
131 | } | 113 | } |
132 | }); | 114 | }); |
133 | 115 | ||
134 | primitive!(env, Some(0), "quit", |_, app| { | 116 | primitive!(env, Arity::Exact(0), "quit", |_, app| { |
135 | app.quit(); | 117 | app.quit(); |
136 | Ok(LispExpr::Unit) | 118 | Ok(LispExpr::Unit) |
137 | }); | 119 | }); |
120 | |||
121 | primitive!(env, Arity::Exact(2), "eq?", |args, app| { | ||
122 | let s = &args[0]; | ||
123 | let o = &args[1]; | ||
124 | info!("comparing {} {}", s, o); | ||
125 | let result = s.compare(o, &app.lisp_env); | ||
126 | result.map(LispExpr::BoolLit) | ||
127 | }); | ||
128 | |||
129 | primitive!(env, Arity::Exact(2), ">", |args, _| { | ||
130 | let nums = args | ||
131 | .into_iter() | ||
132 | .map(|arg| arg.try_into()) | ||
133 | .collect::<Result<Vec<&LispNumber>, LispError>>()?; | ||
134 | let acc = nums[0].clone(); | ||
135 | Ok(LispExpr::BoolLit( | ||
136 | nums.into_iter().skip(1).all(|&arg| acc > arg), | ||
137 | )) | ||
138 | }); | ||
139 | |||
140 | primitive!(env, Arity::Exact(2), ">=", |args, _| { | ||
141 | let nums = args | ||
142 | .into_iter() | ||
143 | .map(|arg| arg.try_into()) | ||
144 | .collect::<Result<Vec<&LispNumber>, LispError>>()?; | ||
145 | let acc = nums[0].clone(); | ||
146 | Ok(LispExpr::BoolLit( | ||
147 | nums.into_iter().skip(1).all(|&arg| acc >= arg), | ||
148 | )) | ||
149 | }); | ||
150 | |||
151 | primitive!(env, Arity::Exact(2), "<", |args, _| { | ||
152 | let nums = args | ||
153 | .into_iter() | ||
154 | .map(|arg| arg.try_into()) | ||
155 | .collect::<Result<Vec<&LispNumber>, LispError>>()?; | ||
156 | let acc = nums[0].clone(); | ||
157 | Ok(LispExpr::BoolLit( | ||
158 | nums.into_iter().skip(1).all(|&arg| acc < arg), | ||
159 | )) | ||
160 | }); | ||
161 | |||
162 | primitive!(env, Arity::Exact(2), "<=", |args, _| { | ||
163 | let nums = args | ||
164 | .into_iter() | ||
165 | .map(|arg| arg.try_into()) | ||
166 | .collect::<Result<Vec<&LispNumber>, LispError>>()?; | ||
167 | let acc = nums[0].clone(); | ||
168 | Ok(LispExpr::BoolLit( | ||
169 | nums.into_iter().skip(1).all(|&arg| acc <= arg), | ||
170 | )) | ||
171 | }); | ||
172 | |||
173 | primitive!(env, Arity::Exact(1), "string-len", |args, _| { | ||
174 | match &args[0] { | ||
175 | LispExpr::StringLit(s) => Ok(LispExpr::Number(LispNumber::Integer(s.len() as i64))), | ||
176 | _ => Err(EvalError::TypeMismatch.into()), | ||
177 | } | ||
178 | }); | ||
179 | |||
180 | primitive!(env, Arity::Atmost(1), "save", |args, app| { | ||
181 | let image = app.export().encode().unwrap(); | ||
182 | if args.len() == 1 && matches!(&args[0], LispExpr::StringLit(_)) { | ||
183 | let mut buffer = File::create(&args[0].as_ref()).unwrap(); | ||
184 | buffer.write_all(&image[..]).unwrap(); | ||
185 | } else if let Some(p) = app.file_name { | ||
186 | let file_name = p; | ||
187 | let mut buffer = File::create(&file_name).unwrap(); | ||
188 | buffer.write_all(&image[..]).unwrap(); | ||
189 | } else { | ||
190 | return Err(EvalError::NoFileName.into()); | ||
191 | } | ||
192 | |||
193 | return Ok(LispExpr::Unit); | ||
194 | }); | ||
195 | |||
138 | env | 196 | env |
139 | } | 197 | } |