aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/app.rs101
-rw-r--r--src/bitmap.rs22
-rw-r--r--src/brush.rs4
-rw-r--r--src/cli.rs32
-rw-r--r--src/command.rs8
-rw-r--r--src/consts.rs6
-rw-r--r--src/dither.rs40
-rw-r--r--src/guide.rs16
-rw-r--r--src/lisp/eval.rs55
-rw-r--r--src/lisp/expr.rs46
-rw-r--r--src/lisp/lex.rs25
-rw-r--r--src/lisp/number.rs6
-rw-r--r--src/lisp/parse.rs159
-rw-r--r--src/lisp/prelude.rs122
-rw-r--r--src/main.rs8
-rw-r--r--src/message.rs20
-rw-r--r--src/undo.rs16
-rw-r--r--src/utils.rs6
18 files changed, 344 insertions, 348 deletions
diff --git a/src/app.rs b/src/app.rs
index 33d4934..f42b48c 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -108,10 +108,10 @@ impl<'ctx> AppState<'ctx> {
108 let y_min = self.start.y(); 108 let y_min = self.start.y();
109 let x_max = self.start.x() + (self.width() * self.zoom as u32) as i32; 109 let x_max = self.start.x() + (self.width() * self.zoom as u32) as i32;
110 let y_max = self.start.y() + (self.height() * self.zoom as u32) as i32; 110 let y_max = self.start.y() + (self.height() * self.zoom as u32) as i32;
111 return ( 111 (
112 Point::new(x_min, y_min), 112 Point::new(x_min, y_min),
113 Point::new(x_max as i32, y_max as i32), 113 Point::new(x_max as i32, y_max as i32),
114 ); 114 )
115 } 115 }
116 116
117 pub fn change_active_color(&mut self) { 117 pub fn change_active_color(&mut self) {
@@ -125,7 +125,7 @@ impl<'ctx> AppState<'ctx> {
125 let rel_p = p - self.start; 125 let rel_p = p - self.start;
126 // reduce p based on zoom and cell size 126 // reduce p based on zoom and cell size
127 let (sx, sy) = (rel_p.x() / self.zoom as i32, rel_p.y() / self.zoom as i32); 127 let (sx, sy) = (rel_p.x() / self.zoom as i32, rel_p.y() / self.zoom as i32);
128 return Some((sx as u32, sy as u32)); 128 Some((sx as u32, sy as u32))
129 } else { 129 } else {
130 None 130 None
131 } 131 }
@@ -254,7 +254,7 @@ impl<'ctx> AppState<'ctx> {
254 let op = self 254 let op = self
255 .current_operation 255 .current_operation
256 .drain(..) 256 .drain(..)
257 .filter(|v| !v.old == v.new) 257 .filter(|v| v.old != v.new)
258 .collect::<Vec<_>>(); 258 .collect::<Vec<_>>();
259 self.undo_stack.push(ModifyRecord::Paint(op)); 259 self.undo_stack.push(ModifyRecord::Paint(op));
260 } 260 }
@@ -267,7 +267,7 @@ impl<'ctx> AppState<'ctx> {
267 let (x2, y2) = (p.0 * (1 + self.zoom as u32), p.1 * (1 + self.zoom as u32)); 267 let (x2, y2) = (p.0 * (1 + self.zoom as u32), p.1 * (1 + self.zoom as u32));
268 let diffx = x2 as i32 - x1 as i32; 268 let diffx = x2 as i32 - x1 as i32;
269 let diffy = y2 as i32 - y1 as i32; 269 let diffy = y2 as i32 - y1 as i32;
270 self.start = self.start - Point::from((diffx, diffy)); 270 self.start -= Point::from((diffx, diffy));
271 } 271 }
272 self.zoom += 1; 272 self.zoom += 1;
273 } 273 }
@@ -280,7 +280,7 @@ impl<'ctx> AppState<'ctx> {
280 let (x2, y2) = (p.0 * (self.zoom as u32 - 1), p.1 * (self.zoom as u32 - 1)); 280 let (x2, y2) = (p.0 * (self.zoom as u32 - 1), p.1 * (self.zoom as u32 - 1));
281 let diffx = x2 as i32 - x1 as i32; 281 let diffx = x2 as i32 - x1 as i32;
282 let diffy = y2 as i32 - y1 as i32; 282 let diffy = y2 as i32 - y1 as i32;
283 self.start = self.start - Point::from((diffx, diffy)); 283 self.start -= Point::from((diffx, diffy));
284 } 284 }
285 self.zoom -= 1; 285 self.zoom -= 1;
286 } 286 }
@@ -310,7 +310,7 @@ impl<'ctx> AppState<'ctx> {
310 310
311 pub fn eval_command(&mut self) { 311 pub fn eval_command(&mut self) {
312 let lisp_expr = &self.command_box.text; 312 let lisp_expr = &self.command_box.text;
313 let mut parser = Parser::new(Lexer::new(lisp_expr, 0)); 313 let mut parser = Parser::new(Lexer::new(lisp_expr));
314 let res = parser.parse_single_expr(); 314 let res = parser.parse_single_expr();
315 match res { 315 match res {
316 Ok(expr) => { 316 Ok(expr) => {
@@ -393,7 +393,7 @@ impl<'ctx> AppState<'ctx> {
393 let mouse_coords = if let Some((x, y)) = self.idx_at_coord(self.mouse) { 393 let mouse_coords = if let Some((x, y)) = self.idx_at_coord(self.mouse) {
394 format!("{:3}, {:3}", x + 1, y + 1) 394 format!("{:3}, {:3}", x + 1, y + 1)
395 } else { 395 } else {
396 format!("---, ---") 396 String::from("---, ---")
397 }; 397 };
398 let status_text = format!( 398 let status_text = format!(
399 "{} [PT {}][KIND {}]", 399 "{} [PT {}][KIND {}]",
@@ -478,32 +478,29 @@ impl<'ctx> AppState<'ctx> {
478 } 478 }
479 } 479 }
480 } 480 }
481 match self.brush { 481 if let Brush::Line(LineBrush { start, size, .. }) = self.brush {
482 Brush::Line(LineBrush { start, size, .. }) => { 482 let size = self.zoom as u32 * (size as u32 + 5);
483 let size = self.zoom as u32 * (size as u32 + 5); 483 if let (Some(from), Some(to)) = (start, pt) {
484 if let (Some(from), Some(to)) = (start, pt) { 484 let line = self.pixmap.get_line(from, to.into());
485 let line = self.pixmap.get_line(from, to.into()); 485 draw_text(
486 draw_text( 486 &mut self.canvas,
487 &mut self.canvas, 487 self.ttf_context,
488 self.ttf_context, 488 format!("{}°", positive_angle_with_x(from, to.into())),
489 format!("{}°", positive_angle_with_x(from, to.into())), 489 PINK,
490 PINK, 490 (self.mouse.0 + size as i32, self.mouse.1 + size as i32),
491 (self.mouse.0 + size as i32, self.mouse.1 + size as i32), 491 );
492 ); 492 for MapPoint { x, y } in line.into_iter() {
493 for MapPoint { x, y } in line.into_iter() { 493 self.canvas.set_draw_color(PINK);
494 self.canvas.set_draw_color(PINK); 494 self.canvas
495 self.canvas 495 .fill_rect(Rect::new(
496 .fill_rect(Rect::new( 496 x as i32 * cs as i32 + self.start.x(),
497 x as i32 * cs as i32 + self.start.x(), 497 y as i32 * cs as i32 + self.start.y(),
498 y as i32 * cs as i32 + self.start.y(), 498 cs,
499 cs, 499 cs,
500 cs, 500 ))
501 )) 501 .unwrap();
502 .unwrap();
503 }
504 } 502 }
505 } 503 }
506 _ => {}
507 } 504 }
508 } 505 }
509 506
@@ -530,14 +527,12 @@ impl<'ctx> AppState<'ctx> {
530 527
531 fn draw_symmetry(&mut self) { 528 fn draw_symmetry(&mut self) {
532 let Symmetry { x, y } = self.symmetry; 529 let Symmetry { x, y } = self.symmetry;
533 x.and_then(|line| { 530 if let Some(line) = x {
534 self.draw_line_to_grid(line, Axis::X, CYAN); 531 self.draw_line_to_grid(line, Axis::X, CYAN)
535 Some(()) 532 }
536 }); 533 if let Some(line) = y {
537 y.and_then(|line| { 534 self.draw_line_to_grid(line, Axis::Y, CYAN)
538 self.draw_line_to_grid(line, Axis::Y, CYAN); 535 }
539 Some(())
540 });
541 } 536 }
542 537
543 fn draw_guides(&mut self) { 538 fn draw_guides(&mut self) {
@@ -626,7 +621,7 @@ impl<'ctx> AppState<'ctx> {
626 .build() 621 .build()
627 .map_err(|e| AppError::Sdl(e.to_string()))?; 622 .map_err(|e| AppError::Sdl(e.to_string()))?;
628 623
629 let data = start_data.unwrap_or(vec![false; (width * height) as usize]); 624 let data = start_data.unwrap_or_else(|| vec![false; (width * height) as usize]);
630 let pixmap = Pixmap::new_with(width, height, data); 625 let pixmap = Pixmap::new_with(width, height, data);
631 let mut app = Self { 626 let mut app = Self {
632 active_color: true, 627 active_color: true,
@@ -668,7 +663,7 @@ impl<'ctx> AppState<'ctx> {
668 let image = self.export().encode().unwrap(); 663 let image = self.export().encode().unwrap();
669 let mut file = File::create(file_name).map_err(AppError::File)?; 664 let mut file = File::create(file_name).map_err(AppError::File)?;
670 file.write_all(&image[..]).map_err(AppError::File)?; 665 file.write_all(&image[..]).map_err(AppError::File)?;
671 return Ok(()); 666 Ok(())
672 } 667 }
673 668
674 pub fn run(&mut self) { 669 pub fn run(&mut self) {
@@ -799,21 +794,21 @@ impl<'ctx> AppState<'ctx> {
799 start, 794 start,
800 extend, 795 extend,
801 }) => { 796 }) => {
802 if start.is_none() { 797 if let Some(s) = start {
798 if let Ok(o) = self.paint_line(s, pt, val, size) {
799 self.current_operation.extend(o);
800 self.brush = Brush::Line(LineBrush {
801 size,
802 start: if extend { contact } else { None },
803 extend,
804 });
805 }
806 } else {
803 self.brush = Brush::Line(LineBrush { 807 self.brush = Brush::Line(LineBrush {
804 size, 808 size,
805 start: contact, 809 start: contact,
806 extend, 810 extend,
807 }); 811 });
808 } else if let Ok(o) =
809 self.paint_line(start.unwrap(), pt, val, size)
810 {
811 self.current_operation.extend(o);
812 self.brush = Brush::Line(LineBrush {
813 size,
814 start: if extend { contact } else { None },
815 extend,
816 });
817 } 812 }
818 } 813 }
819 Brush::Fill => { 814 Brush::Fill => {
@@ -827,7 +822,7 @@ impl<'ctx> AppState<'ctx> {
827 for o in operation.iter() { 822 for o in operation.iter() {
828 // this `set` is unchecked because the returned 823 // this `set` is unchecked because the returned
829 // value of flood_fill is checked to be within pixmap 824 // value of flood_fill is checked to be within pixmap
830 self.pixmap.set(o.clone(), replacement); 825 self.pixmap.set(*o, replacement);
831 } 826 }
832 self.current_operation.extend( 827 self.current_operation.extend(
833 operation 828 operation
diff --git a/src/bitmap.rs b/src/bitmap.rs
index 8010a4b..2f96a99 100644
--- a/src/bitmap.rs
+++ b/src/bitmap.rs
@@ -29,7 +29,7 @@ impl TryFrom<(i32, i32)> for MapPoint {
29 type Error = (); 29 type Error = ();
30 fn try_from((x, y): (i32, i32)) -> Result<Self, Self::Error> { 30 fn try_from((x, y): (i32, i32)) -> Result<Self, Self::Error> {
31 if x < 0 || y < 0 { 31 if x < 0 || y < 0 {
32 return Err(()); 32 Err(())
33 } else { 33 } else {
34 Ok(MapPoint { 34 Ok(MapPoint {
35 x: x as u32, 35 x: x as u32,
@@ -43,7 +43,7 @@ impl TryFrom<(i64, i64)> for MapPoint {
43 type Error = (); 43 type Error = ();
44 fn try_from((x, y): (i64, i64)) -> Result<Self, Self::Error> { 44 fn try_from((x, y): (i64, i64)) -> Result<Self, Self::Error> {
45 if x < 0 || y < 0 { 45 if x < 0 || y < 0 {
46 return Err(()); 46 Err(())
47 } else { 47 } else {
48 Ok(MapPoint { 48 Ok(MapPoint {
49 x: x as u32, 49 x: x as u32,
@@ -79,18 +79,6 @@ pub enum Axis {
79 Y, 79 Y,
80} 80}
81 81
82impl Into<LispExpr> for Axis {
83 fn into(self) -> LispExpr {
84 LispExpr::Quote(
85 Box::new(LispExpr::Ident(match self {
86 Self::X => "X".into(),
87 Self::Y => "Y".into(),
88 })),
89 1,
90 )
91 }
92}
93
94impl TryFrom<&LispExpr> for Axis { 82impl TryFrom<&LispExpr> for Axis {
95 type Error = EvalError; 83 type Error = EvalError;
96 fn try_from(value: &LispExpr) -> Result<Self, Self::Error> { 84 fn try_from(value: &LispExpr) -> Result<Self, Self::Error> {
@@ -224,7 +212,7 @@ where
224 circle.extend((x - dy.abs() + 1..x + dy.abs()).map(|x| (x, y + dx))); 212 circle.extend((x - dy.abs() + 1..x + dy.abs()).map(|x| (x, y + dx)));
225 circle.extend((x - dy.abs() + 1..x + dy.abs()).map(|x| (x, y - dx))); 213 circle.extend((x - dy.abs() + 1..x + dy.abs()).map(|x| (x, y - dx)));
226 } 214 }
227 dy = dy + 1; 215 dy += 1;
228 if err < 0 { 216 if err < 0 {
229 err = err + 2 * dy + 1; 217 err = err + 2 * dy + 1;
230 } else { 218 } else {
@@ -234,7 +222,7 @@ where
234 } 222 }
235 circle 223 circle
236 .into_iter() 224 .into_iter()
237 .flat_map(|pt| MapPoint::try_from(pt)) 225 .flat_map(MapPoint::try_from)
238 .filter(|&pt| self.contains(pt)) 226 .filter(|&pt| self.contains(pt))
239 .collect() 227 .collect()
240 } 228 }
@@ -279,7 +267,7 @@ where
279 } 267 }
280 coordinates 268 coordinates
281 .into_iter() 269 .into_iter()
282 .flat_map(|pt| MapPoint::try_from(pt)) 270 .flat_map(MapPoint::try_from)
283 .filter(|&pt| self.contains(pt)) 271 .filter(|&pt| self.contains(pt))
284 .collect() 272 .collect()
285 } 273 }
diff --git a/src/brush.rs b/src/brush.rs
index 8ff0cda..8557ba7 100644
--- a/src/brush.rs
+++ b/src/brush.rs
@@ -66,8 +66,8 @@ impl Brush {
66 66
67 pub fn size(&self) -> Option<u8> { 67 pub fn size(&self) -> Option<u8> {
68 match self { 68 match self {
69 Brush::Line(LineBrush { size, .. }) => Some(size.clone()), 69 Brush::Line(LineBrush { size, .. }) => Some(*size),
70 Brush::Circle(CircleBrush { size }) => Some(size.clone()), 70 Brush::Circle(CircleBrush { size }) => Some(*size),
71 _ => None, 71 _ => None,
72 } 72 }
73 } 73 }
diff --git a/src/cli.rs b/src/cli.rs
index 1dbd098..6357355 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -4,7 +4,7 @@ use std::{
4 path::{Path, PathBuf}, 4 path::{Path, PathBuf},
5}; 5};
6 6
7pub static HELP_TEXT: &'static str = " 7pub static HELP_TEXT: &str = "
8Usage 8Usage
9----- 9-----
10 10
@@ -83,11 +83,11 @@ pub fn parse_args() -> Result<Config, CliError> {
83 if !Path::new(s).is_file() { 83 if !Path::new(s).is_file() {
84 return Err(CliError::FileDoesNotExist); 84 return Err(CliError::FileDoesNotExist);
85 } 85 }
86 return Ok(Config::ExistingProject { 86 Ok(Config::ExistingProject {
87 file_name: PathBuf::from(s), 87 file_name: PathBuf::from(s),
88 }); 88 })
89 } 89 }
90 None => return Ok(Config::Help), 90 None => Ok(Config::Help),
91 }, 91 },
92 _ => Err(CliError::SubCommandParseError), 92 _ => Err(CliError::SubCommandParseError),
93 } 93 }
@@ -104,25 +104,23 @@ fn new_project(args: &mut pico_args::Arguments) -> Result<Config, CliError> {
104 .opt_value_from_fn(["-d", "--dimensions"], parse_dimensions) 104 .opt_value_from_fn(["-d", "--dimensions"], parse_dimensions)
105 .map_err(|_| CliError::DimensionParseError)? 105 .map_err(|_| CliError::DimensionParseError)?
106 .unwrap_or((200, 200)); 106 .unwrap_or((200, 200));
107 return Ok(Config::NewProject { 107 Ok(Config::NewProject {
108 file_name: file_name.ok(), 108 file_name: file_name.ok(),
109 dimensions, 109 dimensions,
110 }); 110 })
111} 111}
112 112
113fn parse_dimensions(input: &str) -> Result<(u32, u32), CliError> { 113fn parse_dimensions(input: &str) -> Result<(u32, u32), CliError> {
114 let dimensions: Vec<&str> = input.split('x').collect(); 114 let dimensions: Vec<&str> = input.split('x').collect();
115 match &dimensions[..] { 115 match &dimensions[..] {
116 [width, height] => { 116 [width, height] => Ok((
117 return Ok(( 117 width
118 width 118 .parse::<u32>()
119 .parse::<u32>() 119 .map_err(|_| CliError::DimensionParseError)?,
120 .map_err(|_| CliError::DimensionParseError)?, 120 height
121 height 121 .parse::<u32>()
122 .parse::<u32>() 122 .map_err(|_| CliError::DimensionParseError)?,
123 .map_err(|_| CliError::DimensionParseError)?, 123 )),
124 )) 124 _ => Err(CliError::DimensionParseError),
125 }
126 _ => return Err(CliError::DimensionParseError),
127 } 125 }
128} 126}
diff --git a/src/command.rs b/src/command.rs
index 383389e..52ff7c8 100644
--- a/src/command.rs
+++ b/src/command.rs
@@ -111,7 +111,7 @@ impl CommandBox {
111 return; 111 return;
112 } 112 }
113 if let Some(idx) = self.hist_idx { 113 if let Some(idx) = self.hist_idx {
114 if !(idx + 1 >= self.history.items.len()) { 114 if idx + 1 < self.history.items.len() {
115 self.hist_idx = Some(idx + 1); 115 self.hist_idx = Some(idx + 1);
116 self.text = self.get_from_hist(); 116 self.text = self.get_from_hist();
117 self.cursor_end(); 117 self.cursor_end();
@@ -138,6 +138,12 @@ impl CommandBox {
138 } 138 }
139} 139}
140 140
141impl std::default::Default for CommandBox {
142 fn default() -> Self {
143 CommandBox::new()
144 }
145}
146
141#[derive(Debug)] 147#[derive(Debug)]
142pub struct History<T> { 148pub struct History<T> {
143 pub items: Vec<T>, 149 pub items: Vec<T>,
diff --git a/src/consts.rs b/src/consts.rs
index 3f9ecb9..0b43f0a 100644
--- a/src/consts.rs
+++ b/src/consts.rs
@@ -8,6 +8,6 @@ pub mod colors {
8 pub const PINK: Color = Color::RGB(255, 50, 153); 8 pub const PINK: Color = Color::RGB(255, 50, 153);
9} 9}
10 10
11pub const FONT_PATH: &'static str = "./assets/NerdInput-Regular.ttf"; 11pub const FONT_PATH: &str = "./assets/NerdInput-Regular.ttf";
12pub const STDLIB_PATH: &'static str = "./src/lisp/std.lisp"; 12pub const STDLIB_PATH: &str = "./src/lisp/std.lisp";
13pub const RC_PATH: &'static str = "/home/np/.config/sdl-tests"; 13pub const RC_PATH: &str = "/home/np/.config/sdl-tests";
diff --git a/src/dither.rs b/src/dither.rs
index 99e2839..25c59c8 100644
--- a/src/dither.rs
+++ b/src/dither.rs
@@ -26,16 +26,16 @@ pub fn bayer(level: u8, pt: MapPoint) -> bool {
26 let MapPoint { x, y } = pt; 26 let MapPoint { x, y } = pt;
27 let r = (x % 4, y % 4); 27 let r = (x % 4, y % 4);
28 match level { 28 match level {
29 01 => r == (0, 0) || bayer(00, pt), 29 1 => r == (0, 0) || bayer(0, pt),
30 02 => r == (2, 2) || bayer(01, pt), 30 2 => r == (2, 2) || bayer(1, pt),
31 03 => r == (0, 2) || bayer(02, pt), 31 3 => r == (0, 2) || bayer(2, pt),
32 04 => r == (2, 0) || bayer(03, pt), 32 4 => r == (2, 0) || bayer(3, pt),
33 05 => r == (1, 1) || bayer(04, pt), 33 5 => r == (1, 1) || bayer(4, pt),
34 06 => r == (3, 3) || bayer(05, pt), 34 6 => r == (3, 3) || bayer(5, pt),
35 07 => r == (1, 3) || bayer(06, pt), 35 7 => r == (1, 3) || bayer(6, pt),
36 08 => r == (3, 1) || bayer(07, pt), 36 8 => r == (3, 1) || bayer(7, pt),
37 09 => r == (0, 1) || bayer(08, pt), 37 9 => r == (0, 1) || bayer(8, pt),
38 10 => r == (0, 3) || bayer(09, pt), 38 10 => r == (0, 3) || bayer(9, pt),
39 11 => r == (2, 3) || bayer(10, pt), 39 11 => r == (2, 3) || bayer(10, pt),
40 12 => r == (2, 1) || bayer(11, pt), 40 12 => r == (2, 1) || bayer(11, pt),
41 13 => r == (1, 0) || bayer(12, pt), 41 13 => r == (1, 0) || bayer(12, pt),
@@ -73,16 +73,16 @@ pub fn rylander(level: u8, pt: MapPoint) -> bool {
73 let MapPoint { x, y } = pt; 73 let MapPoint { x, y } = pt;
74 let r = (x % 4, y % 4); 74 let r = (x % 4, y % 4);
75 match level { 75 match level {
76 01 => r == (1, 3) || rylander(00, pt), 76 1 => r == (1, 3) || rylander(0, pt),
77 02 => r == (3, 1) || rylander(01, pt), 77 2 => r == (3, 1) || rylander(1, pt),
78 03 => r == (3, 3) || rylander(02, pt), 78 3 => r == (3, 3) || rylander(2, pt),
79 04 => r == (1, 1) || rylander(03, pt), 79 4 => r == (1, 1) || rylander(3, pt),
80 05 => r == (1, 2) || rylander(04, pt), 80 5 => r == (1, 2) || rylander(4, pt),
81 06 => r == (3, 0) || rylander(05, pt), 81 6 => r == (3, 0) || rylander(5, pt),
82 07 => r == (3, 2) || rylander(06, pt), 82 7 => r == (3, 2) || rylander(6, pt),
83 08 => r == (1, 0) || rylander(07, pt), 83 8 => r == (1, 0) || rylander(7, pt),
84 09 => r == (0, 3) || rylander(08, pt), 84 9 => r == (0, 3) || rylander(8, pt),
85 10 => r == (2, 1) || rylander(09, pt), 85 10 => r == (2, 1) || rylander(9, pt),
86 11 => r == (2, 3) || rylander(10, pt), 86 11 => r == (2, 3) || rylander(10, pt),
87 12 => r == (0, 1) || rylander(11, pt), 87 12 => r == (0, 1) || rylander(11, pt),
88 13 => r == (0, 2) || rylander(12, pt), 88 13 => r == (0, 2) || rylander(12, pt),
diff --git a/src/guide.rs b/src/guide.rs
index 85d0594..0416e5c 100644
--- a/src/guide.rs
+++ b/src/guide.rs
@@ -1,21 +1,7 @@
1use crate::{ 1use crate::bitmap::Axis;
2 bitmap::Axis,
3 lisp::{expr::LispExpr, number::LispNumber},
4};
5
6use std::convert::Into;
7 2
8#[derive(Debug, Hash, PartialEq, Eq, Copy, Clone)] 3#[derive(Debug, Hash, PartialEq, Eq, Copy, Clone)]
9pub struct Guide { 4pub struct Guide {
10 pub axis: Axis, 5 pub axis: Axis,
11 pub offset: u32, 6 pub offset: u32,
12} 7}
13
14impl Into<LispExpr> for Guide {
15 fn into(self) -> LispExpr {
16 LispExpr::List(vec![
17 self.axis.into(),
18 LispExpr::Number(LispNumber::Integer(self.offset as i64)),
19 ])
20 }
21}
diff --git a/src/lisp/eval.rs b/src/lisp/eval.rs
index 9276ef5..329b6ab 100644
--- a/src/lisp/eval.rs
+++ b/src/lisp/eval.rs
@@ -76,7 +76,7 @@ where
76 self.eval(&LispExpr::List(f.body.clone())) 76 self.eval(&LispExpr::List(f.body.clone()))
77 }; 77 };
78 self.app.lisp_env.pop(); 78 self.app.lisp_env.pop();
79 return result; 79 result
80 } 80 }
81 } 81 }
82 LispExpr::List(_) => { 82 LispExpr::List(_) => {
@@ -108,7 +108,7 @@ where
108 error!("Unable to create global definition"); 108 error!("Unable to create global definition");
109 return Err(EvalError::BadForm.into()); 109 return Err(EvalError::BadForm.into());
110 } 110 }
111 return Ok(LispExpr::Unit); 111 Ok(LispExpr::Unit)
112 } 112 }
113 [LispExpr::List(shorthand), LispExpr::List(body)] => { 113 [LispExpr::List(shorthand), LispExpr::List(body)] => {
114 // (define (func arg) <body>) shorthand 114 // (define (func arg) <body>) shorthand
@@ -130,12 +130,12 @@ where
130 130
131 let local_env = &mut self.app.lisp_env.last_mut(); 131 let local_env = &mut self.app.lisp_env.last_mut();
132 if let Some(env) = local_env { 132 if let Some(env) = local_env {
133 env.insert(id.into(), value); 133 env.insert(id, value);
134 } else { 134 } else {
135 error!("Unable to create global definition"); 135 error!("Unable to create global definition");
136 return Err(EvalError::BadForm.into()); 136 return Err(EvalError::BadForm.into());
137 } 137 }
138 return Ok(LispExpr::Unit); 138 Ok(LispExpr::Unit)
139 } 139 }
140 _ => { 140 _ => {
141 error!("Invalid usage of `define`"); 141 error!("Invalid usage of `define`");
@@ -154,17 +154,16 @@ where
154 let value = self.eval(&expr)?; 154 let value = self.eval(&expr)?;
155 let local_env = self.app.lisp_env.last_mut(); 155 let local_env = self.app.lisp_env.last_mut();
156 if let Some(env) = local_env { 156 if let Some(env) = local_env {
157 return env 157 env.insert(id.into(), value)
158 .insert(id.into(), value) 158 .ok_or_else(|| EvalError::UnboundVariable(id.into()).into())
159 .ok_or(EvalError::UnboundVariable(id.into()).into());
160 } else { 159 } else {
161 error!("Unable to set in global env!"); 160 error!("Unable to set in global env!");
162 return Err(EvalError::BadForm.into()); 161 Err(EvalError::BadForm.into())
163 } 162 }
164 } 163 }
165 _ => { 164 _ => {
166 error!("Invalid usage of `set!`"); 165 error!("Invalid usage of `set!`");
167 return Err(EvalError::BadForm.into()); 166 Err(EvalError::BadForm.into())
168 } 167 }
169 } 168 }
170 } 169 }
@@ -172,15 +171,15 @@ where
172 pub fn eval_if(&mut self, args: &[LispExpr]) -> Result<LispExpr, LispError> { 171 pub fn eval_if(&mut self, args: &[LispExpr]) -> Result<LispExpr, LispError> {
173 let arity = Arity::Exact(3); 172 let arity = Arity::Exact(3);
174 if !arity.check(args) { 173 if !arity.check(args) {
175 return Err(arity.to_error()); 174 Err(arity.to_error())
176 } else { 175 } else {
177 match args { 176 match args {
178 [predicate, then, else_] => { 177 [predicate, then, else_] => {
179 let predicate = self.eval(&predicate)?; 178 let predicate = self.eval(&predicate)?;
180 if matches!(predicate, LispExpr::BoolLit(false)) { 179 if matches!(predicate, LispExpr::BoolLit(false)) {
181 return self.eval(&else_); 180 self.eval(&else_)
182 } else { 181 } else {
183 return self.eval(&then); 182 self.eval(&then)
184 } 183 }
185 } 184 }
186 _ => { 185 _ => {
@@ -194,7 +193,7 @@ where
194 let arity = Arity::Atleast(1); 193 let arity = Arity::Atleast(1);
195 let valid_cond_stmt = |expr: &LispExpr| matches!(expr, LispExpr::List(v) if v.len() == 2); 194 let valid_cond_stmt = |expr: &LispExpr| matches!(expr, LispExpr::List(v) if v.len() == 2);
196 if !arity.check(args) { 195 if !arity.check(args) {
197 return Err(arity.to_error()); 196 Err(arity.to_error())
198 } else { 197 } else {
199 for cond_stmt in args { 198 for cond_stmt in args {
200 if valid_cond_stmt(cond_stmt) { 199 if valid_cond_stmt(cond_stmt) {
@@ -211,7 +210,7 @@ where
211 return Err(EvalError::BadForm.into()); 210 return Err(EvalError::BadForm.into());
212 } 211 }
213 } 212 }
214 return Ok(LispExpr::Unit); 213 Ok(LispExpr::Unit)
215 } 214 }
216 } 215 }
217 216
@@ -220,7 +219,7 @@ where
220 let valid_binding_stmt = 219 let valid_binding_stmt =
221 |expr: &LispExpr| matches!(expr, LispExpr::List(v) if v.len() == 2); 220 |expr: &LispExpr| matches!(expr, LispExpr::List(v) if v.len() == 2);
222 if !arity.check(args) { 221 if !arity.check(args) {
223 return Err(arity.to_error()); 222 Err(arity.to_error())
224 } else { 223 } else {
225 let nested_env = Environment::new(); 224 let nested_env = Environment::new();
226 self.app.lisp_env.push(nested_env); 225 self.app.lisp_env.push(nested_env);
@@ -247,11 +246,11 @@ where
247 } 246 }
248 let result = self.eval(&body); 247 let result = self.eval(&body);
249 self.app.lisp_env.pop(); 248 self.app.lisp_env.pop();
250 return result; 249 result
251 } 250 }
252 _ => { 251 _ => {
253 error!("bad `let` form"); 252 error!("bad `let` form");
254 return Err(EvalError::BadForm.into()); 253 Err(EvalError::BadForm.into())
255 } 254 }
256 } 255 }
257 } 256 }
@@ -277,32 +276,28 @@ pub fn create_lambda(cdr: &[LispExpr]) -> Result<LispExpr, LispError> {
277 return Err(arity.to_error()); 276 return Err(arity.to_error());
278 } 277 }
279 match cdr { 278 match cdr {
280 [LispExpr::List(params), LispExpr::List(body)] if type_match!(params, (..) => LispExpr::Ident(_)) => 279 [LispExpr::List(params), LispExpr::List(body)] if type_match!(params, (..) => LispExpr::Ident(_)) => {
281 { 280 Ok(LispExpr::Function(LispFunction {
282 return Ok(LispExpr::Function(LispFunction { 281 params: params.iter().map(|p| p.unwrap_ident()).collect::<Vec<_>>(),
283 params: params
284 .into_iter()
285 .map(|p| p.unwrap_ident())
286 .collect::<Vec<_>>(),
287 body: body.clone(), 282 body: body.clone(),
288 })); 283 }))
289 } 284 }
290 _ => { 285 _ => {
291 error!("Invalid usage of `lambda`"); 286 error!("Invalid usage of `lambda`");
292 return Err(EvalError::BadForm.into()); 287 Err(EvalError::BadForm.into())
293 } 288 }
294 } 289 }
295} 290}
296 291
297pub fn lookup(env_list: &[Environment], key: &str) -> Result<LispExpr, LispError> { 292pub fn lookup(env_list: &[Environment], key: &str) -> Result<LispExpr, LispError> {
298 if env_list.is_empty() { 293 if env_list.is_empty() {
299 return Err(EvalError::UnboundVariable(key.into()).into()); 294 Err(EvalError::UnboundVariable(key.into()).into())
300 } else { 295 } else {
301 let local_env = env_list.last().unwrap(); 296 let local_env = env_list.last().unwrap();
302 if let Some(val) = local_env.get(key) { 297 if let Some(val) = local_env.get(key) {
303 return Ok(val.clone()); 298 Ok(val.clone())
304 } else { 299 } else {
305 return lookup(&env_list[..env_list.len() - 1], key); 300 lookup(&env_list[..env_list.len() - 1], key)
306 } 301 }
307 } 302 }
308} 303}
@@ -314,7 +309,7 @@ mod tests {
314 use crate::lisp::{expr::LispExpr, lex::Lexer, number::LispNumber, parse::Parser}; 309 use crate::lisp::{expr::LispExpr, lex::Lexer, number::LispNumber, parse::Parser};
315 310
316 fn run(code: &str, app: &mut AppState) -> LispExpr { 311 fn run(code: &str, app: &mut AppState) -> LispExpr {
317 let mut parser = Parser::new(Lexer::new(code, 0)); 312 let mut parser = Parser::new(Lexer::new(code));
318 let mut evaluator = Evaluator { 313 let mut evaluator = Evaluator {
319 app, 314 app,
320 context: Vec::new(), 315 context: Vec::new(),
diff --git a/src/lisp/expr.rs b/src/lisp/expr.rs
index d2066e7..692f951 100644
--- a/src/lisp/expr.rs
+++ b/src/lisp/expr.rs
@@ -1,12 +1,18 @@
1use std::{cmp::PartialEq, convert::TryFrom, fmt}; 1use std::{
2 cmp::PartialEq,
3 convert::{From, TryFrom},
4 fmt,
5};
2 6
3use crate::{ 7use crate::{
4 app::AppState, 8 app::AppState,
9 bitmap::Axis,
10 guide::Guide,
5 lisp::{ 11 lisp::{
6 error::{EvalError, LispError}, 12 error::{EvalError, LispError},
7 eval::lookup, 13 eval::lookup,
8 number::LispNumber, 14 number::LispNumber,
9 EnvList, 15 Environment,
10 }, 16 },
11}; 17};
12 18
@@ -113,7 +119,7 @@ impl LispExpr {
113 } 119 }
114 } 120 }
115 121
116 pub fn compare(&self, other: &Self, envs: &EnvList) -> Result<BoolLit, LispError> { 122 pub fn compare(&self, other: &Self, envs: &[Environment]) -> Result<BoolLit, LispError> {
117 match (self, other) { 123 match (self, other) {
118 (LispExpr::Unit, LispExpr::Unit) => Ok(true), 124 (LispExpr::Unit, LispExpr::Unit) => Ok(true),
119 (LispExpr::Number(s), LispExpr::Number(o)) => Ok(s == o), 125 (LispExpr::Number(s), LispExpr::Number(o)) => Ok(s == o),
@@ -121,8 +127,8 @@ impl LispExpr {
121 .iter() 127 .iter()
122 .zip(o) 128 .zip(o)
123 .all(|(a, b)| matches!(a.compare(b, envs), Ok(true)))), 129 .all(|(a, b)| matches!(a.compare(b, envs), Ok(true)))),
124 (LispExpr::List(s), LispExpr::Unit) => Ok(s.len() == 0), 130 (LispExpr::List(s), LispExpr::Unit) => Ok(s.is_empty()),
125 (LispExpr::Unit, LispExpr::List(s)) => Ok(s.len() == 0), 131 (LispExpr::Unit, LispExpr::List(s)) => Ok(s.is_empty()),
126 (LispExpr::StringLit(s), LispExpr::StringLit(o)) => Ok(s == o), 132 (LispExpr::StringLit(s), LispExpr::StringLit(o)) => Ok(s == o),
127 (LispExpr::Char(s), LispExpr::Char(o)) => Ok(s == o), 133 (LispExpr::Char(s), LispExpr::Char(o)) => Ok(s == o),
128 (LispExpr::BoolLit(s), LispExpr::BoolLit(o)) => Ok(s == o), 134 (LispExpr::BoolLit(s), LispExpr::BoolLit(o)) => Ok(s == o),
@@ -152,7 +158,7 @@ impl LispExpr {
152 // have these be code gen'd somehow 158 // have these be code gen'd somehow
153 pub fn unwrap_number(&self) -> LispNumber { 159 pub fn unwrap_number(&self) -> LispNumber {
154 match &self { 160 match &self {
155 LispExpr::Number(p) => p.clone(), 161 LispExpr::Number(p) => *p,
156 _ => panic!("attempt to call `unwrap_number` on invalid type"), 162 _ => panic!("attempt to call `unwrap_number` on invalid type"),
157 } 163 }
158 } 164 }
@@ -200,10 +206,7 @@ impl LispExpr {
200 } 206 }
201 207
202 pub fn cast_bool(&self) -> bool { 208 pub fn cast_bool(&self) -> bool {
203 match &self { 209 !matches!(self, LispExpr::BoolLit(false))
204 LispExpr::BoolLit(false) => false,
205 _ => true,
206 }
207 } 210 }
208} 211}
209 212
@@ -347,3 +350,26 @@ impl TryFrom<LispExpr> for BoolLit {
347 } 350 }
348 } 351 }
349} 352}
353
354// conversion implementations
355
356impl From<Axis> for LispExpr {
357 fn from(axis: Axis) -> LispExpr {
358 LispExpr::Quote(
359 Box::new(LispExpr::Ident(match axis {
360 Axis::X => "X".into(),
361 Axis::Y => "Y".into(),
362 })),
363 1,
364 )
365 }
366}
367
368impl From<Guide> for LispExpr {
369 fn from(guide: Guide) -> LispExpr {
370 LispExpr::List(vec![
371 guide.axis.into(),
372 LispExpr::Number(LispNumber::Integer(guide.offset as i64)),
373 ])
374 }
375}
diff --git a/src/lisp/lex.rs b/src/lisp/lex.rs
index 2088421..754a23f 100644
--- a/src/lisp/lex.rs
+++ b/src/lisp/lex.rs
@@ -102,16 +102,11 @@ impl<'src, 'file> SpanDisplay<'src, 'file> {
102pub struct Lexer<'input> { 102pub struct Lexer<'input> {
103 input: &'input str, 103 input: &'input str,
104 cur_pos: u32, 104 cur_pos: u32,
105 offset: u32,
106} 105}
107 106
108impl<'a> Lexer<'a> { 107impl<'a> Lexer<'a> {
109 pub fn new(input: &'a str, offset: u32) -> Self { 108 pub fn new(input: &'a str) -> Self {
110 Self { 109 Self { input, cur_pos: 0 }
111 input,
112 cur_pos: 0,
113 offset,
114 }
115 } 110 }
116 111
117 pub fn next_token(&mut self) -> Result<(Span, Token<'a>), ParseError> { 112 pub fn next_token(&mut self) -> Result<(Span, Token<'a>), ParseError> {
@@ -166,11 +161,11 @@ impl<'a> Lexer<'a> {
166 return Ok((sp, token)); 161 return Ok((sp, token));
167 } 162 }
168 self.input = &self.input[..0]; 163 self.input = &self.input[..0];
169 return Ok((Span::empty(self.cur_pos), Token::End)); 164 Ok((Span::empty(self.cur_pos), Token::End))
170 } 165 }
171} 166}
172 167
173fn parse_number<'a>(mut input: &'a str) -> Result<(usize, Token<'a>), ParseErrorKind> { 168fn parse_number(mut input: &str) -> Result<(usize, Token<'_>), ParseErrorKind> {
174 let mut dot = false; 169 let mut dot = false;
175 let mut minus = false; 170 let mut minus = false;
176 let mut size = 0; 171 let mut size = 0;
@@ -186,7 +181,7 @@ fn parse_number<'a>(mut input: &'a str) -> Result<(usize, Token<'a>), ParseError
186 } 181 }
187 } 182 }
188 183
189 while let Some(chr) = chars.next() { 184 for chr in chars {
190 if chr.is_digit(10) { 185 if chr.is_digit(10) {
191 size += 1; 186 size += 1;
192 } else if chr == '.' { 187 } else if chr == '.' {
@@ -210,10 +205,10 @@ fn parse_number<'a>(mut input: &'a str) -> Result<(usize, Token<'a>), ParseError
210 } else { 205 } else {
211 Token::Integer(&input[..size]) 206 Token::Integer(&input[..size])
212 }; 207 };
213 return Ok((size, tok)); 208 Ok((size, tok))
214} 209}
215 210
216fn parse_string<'a>(input: &'a str) -> Result<(usize, Token<'a>), ParseErrorKind> { 211fn parse_string(input: &str) -> Result<(usize, Token<'_>), ParseErrorKind> {
217 // count opening quote 212 // count opening quote
218 let mut size = 1; 213 let mut size = 1;
219 let mut closed = false; 214 let mut closed = false;
@@ -260,7 +255,7 @@ fn consume_comment(start: usize, chars: &mut CharIndices) -> usize {
260 last - start + 1 255 last - start + 1
261} 256}
262 257
263fn parse_name<'a>(input: &'a str) -> Result<(usize, Token<'a>), ParseErrorKind> { 258fn parse_name(input: &str) -> Result<(usize, Token<'_>), ParseErrorKind> {
264 for (ind, chr) in input.char_indices() { 259 for (ind, chr) in input.char_indices() {
265 if !is_ident(chr) { 260 if !is_ident(chr) {
266 return Ok((ind, Token::Name(&input[..ind]))); 261 return Ok((ind, Token::Name(&input[..ind])));
@@ -269,7 +264,7 @@ fn parse_name<'a>(input: &'a str) -> Result<(usize, Token<'a>), ParseErrorKind>
269 return Ok((input.len(), Token::Name(input))); 264 return Ok((input.len(), Token::Name(input)));
270} 265}
271 266
272fn parse_char<'a>(input: &'a str) -> Result<(usize, Token<'a>), ParseErrorKind> { 267fn parse_char(input: &str) -> Result<(usize, Token<'_>), ParseErrorKind> {
273 // first two chars of input are '#' and '\' 268 // first two chars of input are '#' and '\'
274 let chr = &input[..3]; 269 let chr = &input[..3];
275 return Ok((chr.len(), Token::Char(chr))); 270 return Ok((chr.len(), Token::Char(chr)));
@@ -284,7 +279,7 @@ mod tests {
284 } 279 }
285 280
286 fn tokens(input: &str) -> Vec<(Span, Token)> { 281 fn tokens(input: &str) -> Vec<(Span, Token)> {
287 let mut lexer = Lexer::new(input, 0); 282 let mut lexer = Lexer::new(input);
288 let mut tokens = Vec::new(); 283 let mut tokens = Vec::new();
289 loop { 284 loop {
290 match lexer.next_token().unwrap() { 285 match lexer.next_token().unwrap() {
diff --git a/src/lisp/number.rs b/src/lisp/number.rs
index 4ca890a..4824e21 100644
--- a/src/lisp/number.rs
+++ b/src/lisp/number.rs
@@ -16,14 +16,14 @@ impl LispNumber {
16 pub fn div(self, rhs: Self) -> Result<LispNumber, LispError> { 16 pub fn div(self, rhs: Self) -> Result<LispNumber, LispError> {
17 use LispNumber::*; 17 use LispNumber::*;
18 if rhs == Integer(0) || rhs == Float(0.) { 18 if rhs == Integer(0) || rhs == Float(0.) {
19 return Err(EvalError::DivByZero.into()); 19 Err(EvalError::DivByZero.into())
20 } else { 20 } else {
21 return Ok(match (self, rhs) { 21 Ok(match (self, rhs) {
22 (Integer(a), Integer(b)) => Float(a as f64 / b as f64), 22 (Integer(a), Integer(b)) => Float(a as f64 / b as f64),
23 (Float(a), Integer(b)) => Float(a / b as f64), 23 (Float(a), Integer(b)) => Float(a / b as f64),
24 (Integer(a), Float(b)) => Float(a as f64 / b), 24 (Integer(a), Float(b)) => Float(a as f64 / b),
25 (Float(a), Float(b)) => Float(a / b), 25 (Float(a), Float(b)) => Float(a / b),
26 }); 26 })
27 } 27 }
28 } 28 }
29 pub fn unwrap_integer(self) -> i64 { 29 pub fn unwrap_integer(self) -> i64 {
diff --git a/src/lisp/parse.rs b/src/lisp/parse.rs
index 737e7ad..8aeb672 100644
--- a/src/lisp/parse.rs
+++ b/src/lisp/parse.rs
@@ -1,5 +1,5 @@
1use crate::lisp::{ 1use crate::lisp::{
2 error::{LispError, ParseError, ParseErrorKind}, 2 error::{ParseError, ParseErrorKind},
3 lex::{Lexer, Span, Token}, 3 lex::{Lexer, Span, Token},
4 number::LispNumber, 4 number::LispNumber,
5 LispExpr, 5 LispExpr,
@@ -30,97 +30,91 @@ impl<'lex> Parser<'lex> {
30 let mut total_backticks = 0; 30 let mut total_backticks = 0;
31 loop { 31 loop {
32 let (span, token) = self.next()?; 32 let (span, token) = self.next()?;
33 let r: Result<LispExpr, ParseError> = match token { 33 let r: Result<LispExpr, ParseError> =
34 Token::LeftParen => { 34 match token {
35 stack.push(Group::Parens(Vec::new())); 35 Token::LeftParen => {
36 continue; 36 stack.push(Group::Parens(Vec::new()));
37 } 37 continue;
38 Token::RightParen => { 38 }
39 let group = stack 39 Token::RightParen => {
40 .pop() 40 let group = stack
41 .ok_or_else(|| (ParseError::new(span, ParseErrorKind::UnmatchedParen)))?; 41 .pop()
42 match group { 42 .ok_or_else(|| ParseError::new(span, ParseErrorKind::UnmatchedParen))?;
43 Group::Parens(v) => { 43 match group {
44 if v.len() == 0 { 44 Group::Parens(v) => {
45 Ok(LispExpr::Unit) 45 if v.is_empty() {
46 } else { 46 Ok(LispExpr::Unit)
47 Ok(LispExpr::List(v)) 47 } else {
48 Ok(LispExpr::List(v))
49 }
48 } 50 }
51 _ => Err(ParseError::new(
52 span,
53 ParseErrorKind::UnexpectedToken {
54 expected: "expression",
55 found: "(",
56 },
57 )),
49 } 58 }
50 _ => Err(From::from(ParseError::new(
51 span,
52 ParseErrorKind::UnexpectedToken {
53 expected: "expression",
54 found: "(",
55 },
56 ))),
57 } 59 }
58 } 60 Token::Float(f) => f
59 Token::Float(f) => f 61 .parse::<f64>()
60 .parse::<f64>() 62 .map(|n| LispExpr::Number(LispNumber::Float(n)))
61 .map(|n| LispExpr::Number(LispNumber::Float(n))) 63 .map_err(|_| ParseError::new(span, ParseErrorKind::LiteralParseError)),
62 .map_err(|_| ParseError::new(span, ParseErrorKind::LiteralParseError).into()), 64 Token::Integer(i) => i
63 Token::Integer(i) => i 65 .parse::<i64>()
64 .parse::<i64>() 66 .map(|n| LispExpr::Number(LispNumber::Integer(n)))
65 .map(|n| LispExpr::Number(LispNumber::Integer(n))) 67 .map_err(|_| ParseError::new(span, ParseErrorKind::LiteralParseError)),
66 .map_err(|_| ParseError::new(span, ParseErrorKind::LiteralParseError).into()), 68 Token::String(s) => Ok(LispExpr::StringLit(s[1..s.len() - 1].into())),
67 Token::String(s) => Ok(LispExpr::StringLit(s[1..s.len() - 1].into())), 69 Token::Char(s) => Ok(LispExpr::Char(s.chars().nth(2).ok_or_else(|| {
68 Token::Char(s) => {
69 Ok(LispExpr::Char(s.chars().nth(2).ok_or_else(|| {
70 ParseError::new(span, ParseErrorKind::LiteralParseError) 70 ParseError::new(span, ParseErrorKind::LiteralParseError)
71 })?)) 71 })?)),
72 } 72 Token::Name(n) => Ok(name_expr(n)),
73 Token::Name(n) => Ok(name_expr(n)), 73 Token::BackQuote => {
74 Token::BackQuote => { 74 total_backticks += 1;
75 total_backticks += 1; 75 if let Some(&mut Group::Backticks(ref mut n)) = stack.last_mut() {
76 if let Some(&mut Group::Backticks(ref mut n)) = stack.last_mut() { 76 *n += 1;
77 *n += 1; 77 continue;
78 }
79 stack.push(Group::Backticks(1));
78 continue; 80 continue;
79 } 81 }
80 stack.push(Group::Backticks(1)); 82 Token::Comma => {
81 continue; 83 if total_backticks <= 0 {
82 } 84 return Err(ParseError::new(span, ParseErrorKind::UnbalancedComma));
83 Token::Comma => { 85 }
84 if total_backticks <= 0 { 86 total_backticks -= 1;
85 return Err(ParseError::new(span, ParseErrorKind::UnbalancedComma)); 87 if let Some(&mut Group::Backticks(ref mut n)) = stack.last_mut() {
86 } 88 *n -= 1;
87 total_backticks -= 1; 89 continue;
88 if let Some(&mut Group::Backticks(ref mut n)) = stack.last_mut() { 90 }
89 *n -= 1; 91 stack.push(Group::Backticks(-1));
90 continue; 92 continue;
91 } 93 }
92 stack.push(Group::Backticks(-1)); 94 Token::CommaAt => {
93 continue; 95 if total_backticks <= 0 {
94 } 96 return Err(ParseError::new(span, ParseErrorKind::UnbalancedComma));
95 Token::CommaAt => { 97 }
96 if total_backticks <= 0 { 98 total_backticks -= 1;
97 return Err(ParseError::new(span, ParseErrorKind::UnbalancedComma)); 99 stack.push(Group::CommaAt);
100 continue;
98 } 101 }
99 total_backticks -= 1; 102 Token::Quote => {
100 stack.push(Group::CommaAt); 103 if let Some(&mut Group::Quotes(ref mut n)) = stack.last_mut() {
101 continue; 104 *n += 1;
102 } 105 continue;
103 Token::Quote => { 106 }
104 if let Some(&mut Group::Quotes(ref mut n)) = stack.last_mut() { 107 stack.push(Group::Quotes(1));
105 *n += 1;
106 continue; 108 continue;
107 } 109 }
108 stack.push(Group::Quotes(1)); 110 Token::End => {
109 continue; 111 if stack.iter().any(|group| matches!(*group, Group::Parens(_))) {
110 } 112 Err(ParseError::new(span, ParseErrorKind::MissingCloseParen))
111 Token::End => { 113 } else {
112 let any_paren = stack.iter().any(|group| match *group { 114 Err(ParseError::new(span, ParseErrorKind::UnexpectedEof))
113 Group::Parens(_) => true, 115 }
114 _ => false,
115 });
116
117 if any_paren {
118 Err(ParseError::new(span, ParseErrorKind::MissingCloseParen))
119 } else {
120 Err(ParseError::new(span, ParseErrorKind::UnexpectedEof))
121 } 116 }
122 } 117 };
123 };
124 let mut v = r?; 118 let mut v = r?;
125 loop { 119 loop {
126 match stack.last_mut() { 120 match stack.last_mut() {
@@ -181,8 +175,7 @@ impl<'lex> Parser<'lex> {
181 expected: "EOF", 175 expected: "EOF",
182 found: token.name(), 176 found: token.name(),
183 }, 177 },
184 ) 178 )),
185 .into()),
186 } 179 }
187 } 180 }
188 181
@@ -210,7 +203,7 @@ fn name_expr(input: &str) -> LispExpr {
210mod tests { 203mod tests {
211 use super::*; 204 use super::*;
212 fn parse(input: &str) -> Result<LispExpr, ParseError> { 205 fn parse(input: &str) -> Result<LispExpr, ParseError> {
213 let mut parser = Parser::new(Lexer::new(input, 0)); 206 let mut parser = Parser::new(Lexer::new(input));
214 207
215 parser.parse_single_expr() 208 parser.parse_single_expr()
216 } 209 }
diff --git a/src/lisp/prelude.rs b/src/lisp/prelude.rs
index aebff98..fee787e 100644
--- a/src/lisp/prelude.rs
+++ b/src/lisp/prelude.rs
@@ -66,7 +66,7 @@ pub fn new_env() -> Result<Environment, LispError> {
66 66
67 primitive!(env, Arity::Atleast(2), "+", |args, _| { 67 primitive!(env, Arity::Atleast(2), "+", |args, _| {
68 let nums = args 68 let nums = args
69 .into_iter() 69 .iter()
70 .map(|arg| arg.try_into()) 70 .map(|arg| arg.try_into())
71 .collect::<Result<Vec<&LispNumber>, LispError>>()?; 71 .collect::<Result<Vec<&LispNumber>, LispError>>()?;
72 return Ok(LispExpr::Number( 72 return Ok(LispExpr::Number(
@@ -76,10 +76,10 @@ pub fn new_env() -> Result<Environment, LispError> {
76 76
77 primitive!(env, Arity::Atleast(2), "-", |args, _| { 77 primitive!(env, Arity::Atleast(2), "-", |args, _| {
78 let nums = args 78 let nums = args
79 .into_iter() 79 .iter()
80 .map(|arg| arg.try_into()) 80 .map(|arg| arg.try_into())
81 .collect::<Result<Vec<&LispNumber>, LispError>>()?; 81 .collect::<Result<Vec<&LispNumber>, LispError>>()?;
82 let mut acc = nums[0].clone(); 82 let mut acc = *nums[0];
83 for arg in nums.into_iter().skip(1) { 83 for arg in nums.into_iter().skip(1) {
84 acc = acc - *arg; 84 acc = acc - *arg;
85 } 85 }
@@ -88,7 +88,7 @@ pub fn new_env() -> Result<Environment, LispError> {
88 88
89 primitive!(env, Arity::Atleast(2), "*", |args, _| { 89 primitive!(env, Arity::Atleast(2), "*", |args, _| {
90 let nums = args 90 let nums = args
91 .into_iter() 91 .iter()
92 .map(|arg| arg.try_into()) 92 .map(|arg| arg.try_into())
93 .collect::<Result<Vec<&LispNumber>, LispError>>()?; 93 .collect::<Result<Vec<&LispNumber>, LispError>>()?;
94 return Ok(LispExpr::Number( 94 return Ok(LispExpr::Number(
@@ -98,10 +98,10 @@ pub fn new_env() -> Result<Environment, LispError> {
98 98
99 primitive!(env, Arity::Atleast(2), "/", |args, _| { 99 primitive!(env, Arity::Atleast(2), "/", |args, _| {
100 let nums = args 100 let nums = args
101 .into_iter() 101 .iter()
102 .map(|arg| arg.try_into()) 102 .map(|arg| arg.try_into())
103 .collect::<Result<Vec<&LispNumber>, LispError>>()?; 103 .collect::<Result<Vec<&LispNumber>, LispError>>()?;
104 let mut acc = nums[0].clone(); 104 let mut acc = *nums[0];
105 for arg in nums.into_iter().skip(1) { 105 for arg in nums.into_iter().skip(1) {
106 acc = acc.div(*arg)?; 106 acc = acc.div(*arg)?;
107 } 107 }
@@ -139,7 +139,7 @@ pub fn new_env() -> Result<Environment, LispError> {
139 }); 139 });
140 140
141 primitive!(env, Arity::Atleast(1), "begin", |args, _| { 141 primitive!(env, Arity::Atleast(1), "begin", |args, _| {
142 Ok(args.into_iter().last().unwrap().clone()) 142 Ok(args.iter().last().unwrap().clone())
143 }); 143 });
144 144
145 primitive!(env, Arity::Exact(0), "quit", |_, app| { 145 primitive!(env, Arity::Exact(0), "quit", |_, app| {
@@ -157,8 +157,8 @@ pub fn new_env() -> Result<Environment, LispError> {
157 (LispExpr::DottedList(s), LispExpr::DottedList(o)) => { 157 (LispExpr::DottedList(s), LispExpr::DottedList(o)) => {
158 Ok(s.iter().zip(o).all(|(a, b)| a == b)) 158 Ok(s.iter().zip(o).all(|(a, b)| a == b))
159 } 159 }
160 (LispExpr::List(s), LispExpr::Unit) => Ok(s.len() == 0), 160 (LispExpr::List(s), LispExpr::Unit) => Ok(s.is_empty()),
161 (LispExpr::Unit, LispExpr::List(s)) => Ok(s.len() == 0), 161 (LispExpr::Unit, LispExpr::List(s)) => Ok(s.is_empty()),
162 (LispExpr::DottedList(_), LispExpr::Unit) => Ok(false), 162 (LispExpr::DottedList(_), LispExpr::Unit) => Ok(false),
163 (LispExpr::Unit, LispExpr::DottedList(_)) => Ok(false), 163 (LispExpr::Unit, LispExpr::DottedList(_)) => Ok(false),
164 (LispExpr::StringLit(s), LispExpr::StringLit(o)) => Ok(s == o), 164 (LispExpr::StringLit(s), LispExpr::StringLit(o)) => Ok(s == o),
@@ -176,10 +176,10 @@ pub fn new_env() -> Result<Environment, LispError> {
176 176
177 primitive!(env, Arity::Atleast(2), ">", |args, _| { 177 primitive!(env, Arity::Atleast(2), ">", |args, _| {
178 let nums = args 178 let nums = args
179 .into_iter() 179 .iter()
180 .map(|arg| arg.try_into()) 180 .map(|arg| arg.try_into())
181 .collect::<Result<Vec<&LispNumber>, LispError>>()?; 181 .collect::<Result<Vec<&LispNumber>, LispError>>()?;
182 let acc = nums[0].clone(); 182 let acc = *nums[0];
183 Ok(LispExpr::BoolLit( 183 Ok(LispExpr::BoolLit(
184 nums.into_iter().skip(1).all(|&arg| acc > arg), 184 nums.into_iter().skip(1).all(|&arg| acc > arg),
185 )) 185 ))
@@ -187,10 +187,10 @@ pub fn new_env() -> Result<Environment, LispError> {
187 187
188 primitive!(env, Arity::Atleast(2), ">=", |args, _| { 188 primitive!(env, Arity::Atleast(2), ">=", |args, _| {
189 let nums = args 189 let nums = args
190 .into_iter() 190 .iter()
191 .map(|arg| arg.try_into()) 191 .map(|arg| arg.try_into())
192 .collect::<Result<Vec<&LispNumber>, LispError>>()?; 192 .collect::<Result<Vec<&LispNumber>, LispError>>()?;
193 let acc = nums[0].clone(); 193 let acc = *nums[0];
194 Ok(LispExpr::BoolLit( 194 Ok(LispExpr::BoolLit(
195 nums.into_iter().skip(1).all(|&arg| acc >= arg), 195 nums.into_iter().skip(1).all(|&arg| acc >= arg),
196 )) 196 ))
@@ -198,10 +198,10 @@ pub fn new_env() -> Result<Environment, LispError> {
198 198
199 primitive!(env, Arity::Atleast(2), "<", |args, _| { 199 primitive!(env, Arity::Atleast(2), "<", |args, _| {
200 let nums = args 200 let nums = args
201 .into_iter() 201 .iter()
202 .map(|arg| arg.try_into()) 202 .map(|arg| arg.try_into())
203 .collect::<Result<Vec<&LispNumber>, LispError>>()?; 203 .collect::<Result<Vec<&LispNumber>, LispError>>()?;
204 let acc = nums[0].clone(); 204 let acc = *nums[0];
205 Ok(LispExpr::BoolLit( 205 Ok(LispExpr::BoolLit(
206 nums.into_iter().skip(1).all(|&arg| acc < arg), 206 nums.into_iter().skip(1).all(|&arg| acc < arg),
207 )) 207 ))
@@ -209,10 +209,10 @@ pub fn new_env() -> Result<Environment, LispError> {
209 209
210 primitive!(env, Arity::Atleast(2), "<=", |args, _| { 210 primitive!(env, Arity::Atleast(2), "<=", |args, _| {
211 let nums = args 211 let nums = args
212 .into_iter() 212 .iter()
213 .map(|arg| arg.try_into()) 213 .map(|arg| arg.try_into())
214 .collect::<Result<Vec<&LispNumber>, LispError>>()?; 214 .collect::<Result<Vec<&LispNumber>, LispError>>()?;
215 let acc = nums[0].clone(); 215 let acc = *nums[0];
216 Ok(LispExpr::BoolLit( 216 Ok(LispExpr::BoolLit(
217 nums.into_iter().skip(1).all(|&arg| acc <= arg), 217 nums.into_iter().skip(1).all(|&arg| acc <= arg),
218 )) 218 ))
@@ -235,18 +235,16 @@ pub fn new_env() -> Result<Environment, LispError> {
235 .map_err(|e| LispError::Stringified(e.to_string())) 235 .map_err(|e| LispError::Stringified(e.to_string()))
236 .map(|_| LispExpr::Unit); 236 .map(|_| LispExpr::Unit);
237 } 237 }
238 return Err(EvalError::NoFileName.into()); 238 Err(EvalError::NoFileName.into())
239 }); 239 });
240 240
241 primitive!(env, Arity::Exact(1), "save-as", |args, app| { 241 primitive!(env, Arity::Exact(1), "save-as", |args, app| {
242 match &args[0] { 242 match &args[0] {
243 LispExpr::StringLit(s) => { 243 LispExpr::StringLit(s) => app
244 return app 244 .save_as(&s)
245 .save_as(&s) 245 .map_err(|e| LispError::Stringified(e.to_string()))
246 .map_err(|e| LispError::Stringified(e.to_string())) 246 .map(|_| LispExpr::Unit),
247 .map(|_| LispExpr::Unit); 247 _ => Err(EvalError::TypeMismatch.into()),
248 }
249 _ => return Err(EvalError::TypeMismatch.into()),
250 } 248 }
251 }); 249 });
252 250
@@ -261,52 +259,52 @@ pub fn new_env() -> Result<Environment, LispError> {
261 259
262 primitive!(env, Arity::Exact(0), "brush-fill", |_, app| { 260 primitive!(env, Arity::Exact(0), "brush-fill", |_, app| {
263 app.brush = Brush::Fill; 261 app.brush = Brush::Fill;
264 return Ok(LispExpr::Unit); 262 Ok(LispExpr::Unit)
265 }); 263 });
266 264
267 primitive!(env, Arity::Exact(0), "brush-circle", |_, app| { 265 primitive!(env, Arity::Exact(0), "brush-circle", |_, app| {
268 app.brush = Brush::new(0); 266 app.brush = Brush::new(0);
269 return Ok(LispExpr::Unit); 267 Ok(LispExpr::Unit)
270 }); 268 });
271 269
272 primitive!(env, Arity::Exact(0), "brush-line", |_, app| { 270 primitive!(env, Arity::Exact(0), "brush-line", |_, app| {
273 app.brush = Brush::line(0, false); 271 app.brush = Brush::line(0, false);
274 return Ok(LispExpr::Unit); 272 Ok(LispExpr::Unit)
275 }); 273 });
276 274
277 primitive!(env, Arity::Exact(0), "brush-line-extend", |_, app| { 275 primitive!(env, Arity::Exact(0), "brush-line-extend", |_, app| {
278 app.brush = Brush::line(0, true); 276 app.brush = Brush::line(0, true);
279 return Ok(LispExpr::Unit); 277 Ok(LispExpr::Unit)
280 }); 278 });
281 279
282 primitive!(env, Arity::Exact(2), "cons", |args, _| { 280 primitive!(env, Arity::Exact(2), "cons", |args, _| {
283 if type_match!(args, 1 => LispExpr::Unit) { 281 if type_match!(args, 1 => LispExpr::Unit) {
284 return Ok(LispExpr::List(vec![args[0].clone()])); 282 Ok(LispExpr::List(vec![args[0].clone()]))
285 } else if type_match!(args, 1 => LispExpr::DottedList(_)) { 283 } else if type_match!(args, 1 => LispExpr::DottedList(_)) {
286 // cons of anything to an improper list is an improper list 284 // cons of anything to an improper list is an improper list
287 let mut rest = args[1].unwrap_dotted_list(); 285 let mut rest = args[1].unwrap_dotted_list();
288 rest.insert(0, args[0].clone()); 286 rest.insert(0, args[0].clone());
289 return Ok(LispExpr::DottedList(rest)); 287 Ok(LispExpr::DottedList(rest))
290 } else if type_match!(args, 1 => LispExpr::List(_)) { 288 } else if type_match!(args, 1 => LispExpr::List(_)) {
291 // cons of anything to a proper list is a proper list 289 // cons of anything to a proper list is a proper list
292 let mut rest = args[1].unwrap_list(); 290 let mut rest = args[1].unwrap_list();
293 rest.insert(0, args[0].clone()); 291 rest.insert(0, args[0].clone());
294 return Ok(LispExpr::List(rest)); 292 Ok(LispExpr::List(rest))
295 } else { 293 } else {
296 // attempt to cons non-lists 294 // attempt to cons non-lists
297 return Ok(LispExpr::DottedList(vec![args[0].clone(), args[1].clone()])); 295 Ok(LispExpr::DottedList(vec![args[0].clone(), args[1].clone()]))
298 } 296 }
299 }); 297 });
300 298
301 primitive!(env, Arity::Exact(1), "car", |args, _| { 299 primitive!(env, Arity::Exact(1), "car", |args, _| {
302 if type_match!(args, 0 => LispExpr::List(_)) { 300 if type_match!(args, 0 => LispExpr::List(_)) {
303 return Ok(args[0].unwrap_list().swap_remove(0)); 301 Ok(args[0].unwrap_list().swap_remove(0))
304 } else if type_match!(args, 0 => LispExpr::DottedList(_)) { 302 } else if type_match!(args, 0 => LispExpr::DottedList(_)) {
305 return Ok(args[0].unwrap_dotted_list().swap_remove(0)); 303 Ok(args[0].unwrap_dotted_list().swap_remove(0))
306 } else if type_match!(args, 0 => LispExpr::Unit) { 304 } else if type_match!(args, 0 => LispExpr::Unit) {
307 return Err(EvalError::AccessEmptyList.into()); 305 Err(EvalError::AccessEmptyList.into())
308 } else { 306 } else {
309 return Err(EvalError::TypeMismatch.into()); 307 Err(EvalError::TypeMismatch.into())
310 } 308 }
311 }); 309 });
312 310
@@ -314,32 +312,32 @@ pub fn new_env() -> Result<Environment, LispError> {
314 if type_match!(args, 0 => LispExpr::List(_)) { 312 if type_match!(args, 0 => LispExpr::List(_)) {
315 // cdr of a proper list is a proper list 313 // cdr of a proper list is a proper list
316 let mut ls = args[0].unwrap_list(); 314 let mut ls = args[0].unwrap_list();
317 if ls.len() == 0 { 315 if ls.is_empty() {
318 return Err(EvalError::AccessEmptyList.into()); 316 Err(EvalError::AccessEmptyList.into())
319 } else if ls.len() == 1 { 317 } else if ls.len() == 1 {
320 return Ok(LispExpr::Unit); 318 Ok(LispExpr::Unit)
321 } else { 319 } else {
322 ls.remove(0); 320 ls.remove(0);
323 return Ok(LispExpr::List(ls)); 321 Ok(LispExpr::List(ls))
324 } 322 }
325 } else if type_match!(args, 0 => LispExpr::DottedList(_)) { 323 } else if type_match!(args, 0 => LispExpr::DottedList(_)) {
326 // cdr of an improper list is an improper list or an atom 324 // cdr of an improper list is an improper list or an atom
327 let ls = args[0].unwrap_dotted_list(); 325 let ls = args[0].unwrap_dotted_list();
328 if ls.len() == 2 { 326 if ls.len() == 2 {
329 return Ok(ls.into_iter().last().unwrap()); 327 Ok(ls.into_iter().last().unwrap())
330 } else { 328 } else {
331 // should be unreachable 329 // should be unreachable
332 return Err(EvalError::AccessEmptyList.into()); 330 Err(EvalError::AccessEmptyList.into())
333 } 331 }
334 } else if type_match!(args, 0 => LispExpr::Unit) { 332 } else if type_match!(args, 0 => LispExpr::Unit) {
335 return Err(EvalError::AccessEmptyList.into()); 333 Err(EvalError::AccessEmptyList.into())
336 } else { 334 } else {
337 return Err(EvalError::TypeMismatch.into()); 335 Err(EvalError::TypeMismatch.into())
338 } 336 }
339 }); 337 });
340 338
341 primitive!(env, Arity::Atleast(1), "list", |args, _| { 339 primitive!(env, Arity::Atleast(1), "list", |args, _| {
342 return Ok(LispExpr::List(args.to_vec())); 340 Ok(LispExpr::List(args.to_vec()))
343 }); 341 });
344 342
345 primitive!(env, Arity::Exact(1), "load-script", |args, app| { 343 primitive!(env, Arity::Exact(1), "load-script", |args, app| {
@@ -347,40 +345,40 @@ pub fn new_env() -> Result<Environment, LispError> {
347 let path = args[0].unwrap_stringlit(); 345 let path = args[0].unwrap_stringlit();
348 load_script(&path, app).map(|_| LispExpr::Unit) 346 load_script(&path, app).map(|_| LispExpr::Unit)
349 } else { 347 } else {
350 return Err(EvalError::TypeMismatch.into()); 348 Err(EvalError::TypeMismatch.into())
351 } 349 }
352 }); 350 });
353 351
354 primitive!(env, Arity::Atleast(1), "error", |args, _| { 352 primitive!(env, Arity::Atleast(1), "error", |args, _| {
355 if type_match!(args, 0 => LispExpr::StringLit(_)) { 353 if type_match!(args, 0 => LispExpr::StringLit(_)) {
356 let mut s = String::from(args[0].unwrap_stringlit()); 354 let mut s = args[0].unwrap_stringlit();
357 for arg in args.into_iter().skip(1) { 355 for arg in args.iter().skip(1) {
358 s.push_str(&format!(" {}", arg)); 356 s.push_str(&format!(" {}", arg));
359 } 357 }
360 return Err(EvalError::Custom(s).into()); 358 Err(EvalError::Custom(s).into())
361 } else { 359 } else {
362 return Err(EvalError::TypeMismatch.into()); 360 Err(EvalError::TypeMismatch.into())
363 } 361 }
364 }); 362 });
365 363
366 primitive!(env, Arity::Exact(2), "assert-eq", |args, app| { 364 primitive!(env, Arity::Exact(2), "assert-eq", |args, app| {
367 if args[0].compare(&args[1], &app.lisp_env)? { 365 if args[0].compare(&args[1], &app.lisp_env)? {
368 return Ok(LispExpr::Unit); 366 Ok(LispExpr::Unit)
369 } else { 367 } else {
370 return Err(EvalError::AssertionError { 368 Err(EvalError::AssertionError {
371 expected: args[0].clone(), 369 expected: args[0].clone(),
372 got: args[1].clone(), 370 got: args[1].clone(),
373 } 371 }
374 .into()); 372 .into())
375 } 373 }
376 }); 374 });
377 375
378 primitive!(env, Arity::Exact(0), "canvas-width", |_, app| { 376 primitive!(env, Arity::Exact(0), "canvas-width", |_, app| {
379 return Ok(LispExpr::Number(LispNumber::Integer(app.width() as i64))); 377 Ok(LispExpr::Number(LispNumber::Integer(app.width() as i64)))
380 }); 378 });
381 379
382 primitive!(env, Arity::Exact(0), "canvas-height", |_, app| { 380 primitive!(env, Arity::Exact(0), "canvas-height", |_, app| {
383 return Ok(LispExpr::Number(LispNumber::Integer(app.height() as i64))); 381 Ok(LispExpr::Number(LispNumber::Integer(app.height() as i64)))
384 }); 382 });
385 383
386 primitive!(env, Arity::Exact(3), "set-pixel!", |args, app| { 384 primitive!(env, Arity::Exact(3), "set-pixel!", |args, app| {
@@ -397,21 +395,21 @@ pub fn new_env() -> Result<Environment, LispError> {
397 .try_into() 395 .try_into()
398 .map_err(|_| -> LispError { EvalError::InvalidCoordinates((x, y)).into() })?; 396 .map_err(|_| -> LispError { EvalError::InvalidCoordinates((x, y)).into() })?;
399 if !app.pixmap.contains(set_loc) { 397 if !app.pixmap.contains(set_loc) {
400 return Err(EvalError::InvalidCoordinates((x, y)).into()); 398 Err(EvalError::InvalidCoordinates((x, y)).into())
401 } else { 399 } else {
402 let old_val = app.pixmap.set(set_loc, val); 400 let old_val = app.pixmap.set(set_loc, val);
403 app.current_operation 401 app.current_operation
404 .push(PaintRecord::new(set_loc, old_val, val)); 402 .push(PaintRecord::new(set_loc, old_val, val));
405 return Ok(LispExpr::Unit); 403 Ok(LispExpr::Unit)
406 } 404 }
407 } else { 405 } else {
408 return Err(EvalError::TypeMismatch.into()); 406 Err(EvalError::TypeMismatch.into())
409 } 407 }
410 }); 408 });
411 409
412 primitive!(env, Arity::Exact(0), "commit", |_, app| { 410 primitive!(env, Arity::Exact(0), "commit", |_, app| {
413 app.commit_operation(); 411 app.commit_operation();
414 return Ok(LispExpr::Unit); 412 Ok(LispExpr::Unit)
415 }); 413 });
416 414
417 primitive!(env, Arity::Exact(2), "add-guide!", |args, app| { 415 primitive!(env, Arity::Exact(2), "add-guide!", |args, app| {
@@ -422,9 +420,9 @@ pub fn new_env() -> Result<Environment, LispError> {
422 offset: *offset as u32, 420 offset: *offset as u32,
423 }; 421 };
424 app.guides.insert(guide, true); 422 app.guides.insert(guide, true);
425 return Ok(LispExpr::Unit); 423 Ok(LispExpr::Unit)
426 } 424 }
427 _ => return Err(EvalError::TypeMismatch.into()), 425 _ => Err(EvalError::TypeMismatch.into()),
428 } 426 }
429 }); 427 });
430 428
diff --git a/src/main.rs b/src/main.rs
index 9370abf..245ee83 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,3 +1,4 @@
1#![allow(clippy::upper_case_acronyms, clippy::vec_init_then_push)]
1mod app; 2mod app;
2mod bitmap; 3mod bitmap;
3mod brush; 4mod brush;
@@ -62,13 +63,12 @@ pub fn error_sink() -> Result<(), AppError> {
62 .run(); 63 .run();
63 } 64 }
64 } 65 }
65 return Ok(()); 66 Ok(())
66} 67}
67 68
68pub fn main() { 69pub fn main() {
69 env_logger::init(); 70 env_logger::init();
70 match error_sink() { 71 if let Err(e) = error_sink() {
71 Err(e) => error!("{}", e), 72 error!("{}", e);
72 _ => (),
73 } 73 }
74} 74}
diff --git a/src/message.rs b/src/message.rs
index 958dd68..b090b4d 100644
--- a/src/message.rs
+++ b/src/message.rs
@@ -67,13 +67,19 @@ where
67 } 67 }
68} 68}
69 69
70impl Into<Color> for MessageKind { 70impl std::default::Default for Message {
71 fn into(self) -> Color { 71 fn default() -> Self {
72 match self { 72 Message::new()
73 Self::Error => PINK, 73 }
74 Self::Info => WHITE, 74}
75 Self::Hint => CYAN, 75
76 Self::LispResult => GREY, 76impl From<MessageKind> for Color {
77 fn from(msg: MessageKind) -> Color {
78 match msg {
79 MessageKind::Error => PINK,
80 MessageKind::Info => WHITE,
81 MessageKind::Hint => CYAN,
82 MessageKind::LispResult => GREY,
77 } 83 }
78 } 84 }
79} 85}
diff --git a/src/undo.rs b/src/undo.rs
index 1044c63..5effd79 100644
--- a/src/undo.rs
+++ b/src/undo.rs
@@ -67,9 +67,10 @@ where
67 if let Some(p) = self.position { 67 if let Some(p) = self.position {
68 self.position = p.checked_sub(1); 68 self.position = p.checked_sub(1);
69 // we want to return a clone and not a reference because push deletes the item 69 // we want to return a clone and not a reference because push deletes the item
70 return Some(self.operations[p as usize].clone()); 70 Some(self.operations[p as usize].clone())
71 } else {
72 None
71 } 73 }
72 return None;
73 } 74 }
74 75
75 pub fn redo(&mut self) -> Option<T> { 76 pub fn redo(&mut self) -> Option<T> {
@@ -82,7 +83,16 @@ where
82 self.position = Some(0); 83 self.position = Some(0);
83 return Some(self.operations[0].clone()); 84 return Some(self.operations[0].clone());
84 } 85 }
85 return None; 86 None
87 }
88}
89
90impl<T> std::default::Default for UndoStack<T>
91where
92 T: Clone,
93{
94 fn default() -> Self {
95 UndoStack::new()
86 } 96 }
87} 97}
88 98
diff --git a/src/utils.rs b/src/utils.rs
index ab446f4..8c3b144 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -46,7 +46,7 @@ pub fn draw_text<S: AsRef<str>>(
46 font.set_style(sdl2::ttf::FontStyle::NORMAL); 46 font.set_style(sdl2::ttf::FontStyle::NORMAL);
47 font.set_hinting(sdl2::ttf::Hinting::Mono); 47 font.set_hinting(sdl2::ttf::Hinting::Mono);
48 let surface = font 48 let surface = font
49 .render(if text.is_empty() { " " } else { text.as_ref() }) 49 .render(if text.is_empty() { " " } else { text })
50 .blended(color) 50 .blended(color)
51 .unwrap(); 51 .unwrap();
52 let texture = texture_creator 52 let texture = texture_creator
@@ -84,7 +84,7 @@ pub fn load_script<P: AsRef<Path>>(path: P, app: &mut AppState) -> Result<(), Li
84 .read_to_string(&mut buf) 84 .read_to_string(&mut buf)
85 .map_err(EvalError::ScriptLoadError)?; 85 .map_err(EvalError::ScriptLoadError)?;
86 86
87 let mut parser = Parser::new(Lexer::new(&buf, 0)); 87 let mut parser = Parser::new(Lexer::new(&buf));
88 let mut evaluator = Evaluator { 88 let mut evaluator = Evaluator {
89 app, 89 app,
90 context: Vec::new(), 90 context: Vec::new(),
@@ -94,7 +94,7 @@ pub fn load_script<P: AsRef<Path>>(path: P, app: &mut AppState) -> Result<(), Li
94 })? { 94 })? {
95 evaluator.eval(&expr)?; 95 evaluator.eval(&expr)?;
96 } 96 }
97 return Ok(()); 97 Ok(())
98} 98}
99 99
100pub fn load_file<P: AsRef<Path>>(path: P) -> Result<Image, io::Error> { 100pub fn load_file<P: AsRef<Path>>(path: P) -> Result<Image, io::Error> {