diff options
-rw-r--r-- | src/app.rs | 6 | ||||
-rw-r--r-- | src/lisp/expr.rs | 26 | ||||
-rw-r--r-- | src/lisp/prelude.rs | 46 |
3 files changed, 58 insertions, 20 deletions
@@ -369,12 +369,6 @@ impl<'ctx> AppState<'ctx> { | |||
369 | self.canvas.fill_rect(rect!(canvas_pt.x(), canvas_pt.y(), 2, 2)).unwrap(); | 369 | self.canvas.fill_rect(rect!(canvas_pt.x(), canvas_pt.y(), 2, 2)).unwrap(); |
370 | } | 370 | } |
371 | 371 | ||
372 | // self.canvas | ||
373 | // .set_draw_color(if self.active_color { WHITE } else { BLACK }); | ||
374 | // self.canvas | ||
375 | // .fill_rect(Rect::from_center(primary.area().center(), 12, 12)) | ||
376 | // .unwrap(); | ||
377 | |||
378 | let mouse_coords = if let Some((x, y)) = self.idx_at_coord(self.mouse) { | 372 | let mouse_coords = if let Some((x, y)) = self.idx_at_coord(self.mouse) { |
379 | format!("{:3}, {:3}", x + 1, y + 1) | 373 | format!("{:3}, {:3}", x + 1, y + 1) |
380 | } else { | 374 | } else { |
diff --git a/src/lisp/expr.rs b/src/lisp/expr.rs index 31476c9..aeaa2d8 100644 --- a/src/lisp/expr.rs +++ b/src/lisp/expr.rs | |||
@@ -62,6 +62,7 @@ pub enum LispExpr { | |||
62 | Unit, | 62 | Unit, |
63 | Number(LispNumber), | 63 | Number(LispNumber), |
64 | List(Vec<LispExpr>), | 64 | List(Vec<LispExpr>), |
65 | DottedList(Vec<LispExpr>), | ||
65 | StringLit(String), | 66 | StringLit(String), |
66 | BoolLit(bool), | 67 | BoolLit(bool), |
67 | Ident(Ident), | 68 | Ident(Ident), |
@@ -157,7 +158,14 @@ impl LispExpr { | |||
157 | pub fn unwrap_list(&self) -> Vec<LispExpr> { | 158 | pub fn unwrap_list(&self) -> Vec<LispExpr> { |
158 | match &self { | 159 | match &self { |
159 | LispExpr::List(p) => p.clone(), | 160 | LispExpr::List(p) => p.clone(), |
160 | _ => panic!("attempt to call `unwrap_number` on invalid type"), | 161 | _ => panic!("attempt to call `unwrap_list` on invalid type"), |
162 | } | ||
163 | } | ||
164 | |||
165 | pub fn unwrap_dotted_list(&self) -> Vec<LispExpr> { | ||
166 | match &self { | ||
167 | LispExpr::DottedList(p) => p.clone(), | ||
168 | _ => panic!("attempt to call `unwrap_dotted_list` on invalid type"), | ||
161 | } | 169 | } |
162 | } | 170 | } |
163 | 171 | ||
@@ -209,6 +217,13 @@ impl fmt::Display for LispExpr { | |||
209 | &l.iter().map(|expr| format!("{}", expr)).collect::<Vec<_>>()[..].join(" ") | 217 | &l.iter().map(|expr| format!("{}", expr)).collect::<Vec<_>>()[..].join(" ") |
210 | )?; | 218 | )?; |
211 | } | 219 | } |
220 | LispExpr::DottedList(l) => { | ||
221 | write!(f, "(")?; | ||
222 | for item in &l[..l.len() - 1] { | ||
223 | write!(f, "{} ", item)?; | ||
224 | } | ||
225 | write!(f, ". {})", &l[l.len() - 1])?; | ||
226 | } | ||
212 | LispExpr::StringLit(s) => write!(f, "{:?}", s)?, | 227 | LispExpr::StringLit(s) => write!(f, "{:?}", s)?, |
213 | LispExpr::Char(c) => write!(f, "{}", c)?, | 228 | LispExpr::Char(c) => write!(f, "{}", c)?, |
214 | LispExpr::BoolLit(b) => { | 229 | LispExpr::BoolLit(b) => { |
@@ -237,7 +252,14 @@ impl fmt::Debug for LispExpr { | |||
237 | match self { | 252 | match self { |
238 | LispExpr::Unit => f.debug_tuple("Unit").finish(), | 253 | LispExpr::Unit => f.debug_tuple("Unit").finish(), |
239 | LispExpr::Number(n) => write!(f, "Number({:?})", n), | 254 | LispExpr::Number(n) => write!(f, "Number({:?})", n), |
240 | LispExpr::List(l) => f.debug_list().entries(l.iter()).finish(), | 255 | LispExpr::List(l) => { |
256 | write!(f, "List ")?; | ||
257 | f.debug_list().entries(l.iter()).finish() | ||
258 | }, | ||
259 | LispExpr::DottedList(l) => { | ||
260 | write!(f, "DottedList ")?; | ||
261 | f.debug_list().entries(l.iter()).finish() | ||
262 | }, | ||
241 | LispExpr::StringLit(s) => write!(f, "String({:?})", s), | 263 | LispExpr::StringLit(s) => write!(f, "String({:?})", s), |
242 | LispExpr::Char(c) => write!(f, "Char({:?})", c), | 264 | LispExpr::Char(c) => write!(f, "Char({:?})", c), |
243 | LispExpr::BoolLit(b) => write!(f, "Bool({:?})", b), | 265 | LispExpr::BoolLit(b) => write!(f, "Bool({:?})", b), |
diff --git a/src/lisp/prelude.rs b/src/lisp/prelude.rs index 372bb00..add405c 100644 --- a/src/lisp/prelude.rs +++ b/src/lisp/prelude.rs | |||
@@ -153,8 +153,11 @@ pub fn new_env() -> Result<Environment, LispError> { | |||
153 | (LispExpr::Unit, LispExpr::Unit) => Ok(true), | 153 | (LispExpr::Unit, LispExpr::Unit) => Ok(true), |
154 | (LispExpr::Number(s), LispExpr::Number(o)) => Ok(s == o), | 154 | (LispExpr::Number(s), LispExpr::Number(o)) => Ok(s == o), |
155 | (LispExpr::List(s), LispExpr::List(o)) => Ok(s.iter().zip(o).all(|(a, b)| a == b)), | 155 | (LispExpr::List(s), LispExpr::List(o)) => Ok(s.iter().zip(o).all(|(a, b)| a == b)), |
156 | (LispExpr::DottedList(s), LispExpr::DottedList(o)) => Ok(s.iter().zip(o).all(|(a, b)| a == b)), | ||
156 | (LispExpr::List(s), LispExpr::Unit) => Ok(s.len() == 0), | 157 | (LispExpr::List(s), LispExpr::Unit) => Ok(s.len() == 0), |
157 | (LispExpr::Unit, LispExpr::List(s)) => Ok(s.len() == 0), | 158 | (LispExpr::Unit, LispExpr::List(s)) => Ok(s.len() == 0), |
159 | (LispExpr::DottedList(_), LispExpr::Unit) => Ok(false), | ||
160 | (LispExpr::Unit, LispExpr::DottedList(_)) => Ok(false), | ||
158 | (LispExpr::StringLit(s), LispExpr::StringLit(o)) => Ok(s == o), | 161 | (LispExpr::StringLit(s), LispExpr::StringLit(o)) => Ok(s == o), |
159 | (LispExpr::Char(s), LispExpr::Char(o)) => Ok(s == o), | 162 | (LispExpr::Char(s), LispExpr::Char(o)) => Ok(s == o), |
160 | (LispExpr::BoolLit(s), LispExpr::BoolLit(o)) => Ok(s == o), | 163 | (LispExpr::BoolLit(s), LispExpr::BoolLit(o)) => Ok(s == o), |
@@ -244,18 +247,6 @@ pub fn new_env() -> Result<Environment, LispError> { | |||
244 | } | 247 | } |
245 | }); | 248 | }); |
246 | 249 | ||
247 | primitive!(env, Arity::Exact(2), "cons", |args, _| { | ||
248 | if type_match!(args, 1 => LispExpr::Unit) { | ||
249 | return Ok(LispExpr::List(vec![args[0].clone()])); | ||
250 | } else if !type_match!(args, 1 => LispExpr::List(_)) { | ||
251 | return Ok(LispExpr::List(vec![args[0].clone(), args[1].clone()])); | ||
252 | } else { | ||
253 | let mut rest = args[1].unwrap_list(); | ||
254 | rest.insert(0, args[0].clone()); | ||
255 | return Ok(LispExpr::List(rest)); | ||
256 | } | ||
257 | }); | ||
258 | |||
259 | primitive!(env, Arity::Exact(0), "grid-enabled?", |_, app| { | 250 | primitive!(env, Arity::Exact(0), "grid-enabled?", |_, app| { |
260 | Ok(LispExpr::BoolLit(app.grid.enabled)) | 251 | Ok(LispExpr::BoolLit(app.grid.enabled)) |
261 | }); | 252 | }); |
@@ -285,9 +276,30 @@ pub fn new_env() -> Result<Environment, LispError> { | |||
285 | return Ok(LispExpr::Unit); | 276 | return Ok(LispExpr::Unit); |
286 | }); | 277 | }); |
287 | 278 | ||
279 | primitive!(env, Arity::Exact(2), "cons", |args, _| { | ||
280 | if type_match!(args, 1 => LispExpr::Unit) { | ||
281 | return Ok(LispExpr::List(vec![args[0].clone()])); | ||
282 | } else if type_match!(args, 1 => LispExpr::DottedList(_)) { | ||
283 | // cons of anything to an improper list is an improper list | ||
284 | let mut rest = args[1].unwrap_dotted_list(); | ||
285 | rest.insert(0, args[0].clone()); | ||
286 | return Ok(LispExpr::DottedList(rest)); | ||
287 | } else if type_match!(args, 1 => LispExpr::List(_)) { | ||
288 | // cons of anything to a proper list is a proper list | ||
289 | let mut rest = args[1].unwrap_list(); | ||
290 | rest.insert(0, args[0].clone()); | ||
291 | return Ok(LispExpr::List(rest)); | ||
292 | } else { | ||
293 | // attempt to cons non-lists | ||
294 | return Ok(LispExpr::DottedList(vec![args[0].clone(), args[1].clone()])); | ||
295 | } | ||
296 | }); | ||
297 | |||
288 | primitive!(env, Arity::Exact(1), "car", |args, _| { | 298 | primitive!(env, Arity::Exact(1), "car", |args, _| { |
289 | if type_match!(args, 0 => LispExpr::List(_)) { | 299 | if type_match!(args, 0 => LispExpr::List(_)) { |
290 | return Ok(args[0].unwrap_list().swap_remove(0)); | 300 | return Ok(args[0].unwrap_list().swap_remove(0)); |
301 | } else if type_match!(args, 0 => LispExpr::DottedList(_)) { | ||
302 | return Ok(args[0].unwrap_dotted_list().swap_remove(0)); | ||
291 | } else if type_match!(args, 0 => LispExpr::Unit) { | 303 | } else if type_match!(args, 0 => LispExpr::Unit) { |
292 | return Err(EvalError::AccessEmptyList.into()); | 304 | return Err(EvalError::AccessEmptyList.into()); |
293 | } else { | 305 | } else { |
@@ -297,6 +309,7 @@ pub fn new_env() -> Result<Environment, LispError> { | |||
297 | 309 | ||
298 | primitive!(env, Arity::Exact(1), "cdr", |args, _| { | 310 | primitive!(env, Arity::Exact(1), "cdr", |args, _| { |
299 | if type_match!(args, 0 => LispExpr::List(_)) { | 311 | if type_match!(args, 0 => LispExpr::List(_)) { |
312 | // cdr of a proper list is a proper list | ||
300 | let mut ls = args[0].unwrap_list(); | 313 | let mut ls = args[0].unwrap_list(); |
301 | if ls.len() == 0 { | 314 | if ls.len() == 0 { |
302 | return Err(EvalError::AccessEmptyList.into()); | 315 | return Err(EvalError::AccessEmptyList.into()); |
@@ -306,6 +319,15 @@ pub fn new_env() -> Result<Environment, LispError> { | |||
306 | ls.remove(0); | 319 | ls.remove(0); |
307 | return Ok(LispExpr::List(ls)); | 320 | return Ok(LispExpr::List(ls)); |
308 | } | 321 | } |
322 | } else if type_match!(args, 0 => LispExpr::DottedList(_)){ | ||
323 | // cdr of an improper list is an improper list or an atom | ||
324 | let ls = args[0].unwrap_dotted_list(); | ||
325 | if ls.len() == 2 { | ||
326 | return Ok(ls.into_iter().last().unwrap()); | ||
327 | } else { | ||
328 | // should be unreachable | ||
329 | return Err(EvalError::AccessEmptyList.into()); | ||
330 | } | ||
309 | } else if type_match!(args, 0 => LispExpr::Unit) { | 331 | } else if type_match!(args, 0 => LispExpr::Unit) { |
310 | return Err(EvalError::AccessEmptyList.into()); | 332 | return Err(EvalError::AccessEmptyList.into()); |
311 | } else { | 333 | } else { |