diff options
-rw-r--r-- | src/app.rs | 101 | ||||
-rw-r--r-- | src/bitmap.rs | 22 | ||||
-rw-r--r-- | src/brush.rs | 4 | ||||
-rw-r--r-- | src/cli.rs | 32 | ||||
-rw-r--r-- | src/command.rs | 8 | ||||
-rw-r--r-- | src/consts.rs | 6 | ||||
-rw-r--r-- | src/dither.rs | 40 | ||||
-rw-r--r-- | src/guide.rs | 16 | ||||
-rw-r--r-- | src/lisp/eval.rs | 55 | ||||
-rw-r--r-- | src/lisp/expr.rs | 46 | ||||
-rw-r--r-- | src/lisp/lex.rs | 25 | ||||
-rw-r--r-- | src/lisp/number.rs | 6 | ||||
-rw-r--r-- | src/lisp/parse.rs | 159 | ||||
-rw-r--r-- | src/lisp/prelude.rs | 122 | ||||
-rw-r--r-- | src/main.rs | 8 | ||||
-rw-r--r-- | src/message.rs | 20 | ||||
-rw-r--r-- | src/undo.rs | 16 | ||||
-rw-r--r-- | src/utils.rs | 6 |
18 files changed, 344 insertions, 348 deletions
@@ -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 | ||
82 | impl 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 | |||
94 | impl TryFrom<&LispExpr> for Axis { | 82 | impl 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 | } |
@@ -4,7 +4,7 @@ use std::{ | |||
4 | path::{Path, PathBuf}, | 4 | path::{Path, PathBuf}, |
5 | }; | 5 | }; |
6 | 6 | ||
7 | pub static HELP_TEXT: &'static str = " | 7 | pub static HELP_TEXT: &str = " |
8 | Usage | 8 | Usage |
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 | ||
113 | fn parse_dimensions(input: &str) -> Result<(u32, u32), CliError> { | 113 | fn 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 | ||
141 | impl std::default::Default for CommandBox { | ||
142 | fn default() -> Self { | ||
143 | CommandBox::new() | ||
144 | } | ||
145 | } | ||
146 | |||
141 | #[derive(Debug)] | 147 | #[derive(Debug)] |
142 | pub struct History<T> { | 148 | pub 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 | ||
11 | pub const FONT_PATH: &'static str = "./assets/NerdInput-Regular.ttf"; | 11 | pub const FONT_PATH: &str = "./assets/NerdInput-Regular.ttf"; |
12 | pub const STDLIB_PATH: &'static str = "./src/lisp/std.lisp"; | 12 | pub const STDLIB_PATH: &str = "./src/lisp/std.lisp"; |
13 | pub const RC_PATH: &'static str = "/home/np/.config/sdl-tests"; | 13 | pub 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 @@ | |||
1 | use crate::{ | 1 | use crate::bitmap::Axis; |
2 | bitmap::Axis, | ||
3 | lisp::{expr::LispExpr, number::LispNumber}, | ||
4 | }; | ||
5 | |||
6 | use std::convert::Into; | ||
7 | 2 | ||
8 | #[derive(Debug, Hash, PartialEq, Eq, Copy, Clone)] | 3 | #[derive(Debug, Hash, PartialEq, Eq, Copy, Clone)] |
9 | pub struct Guide { | 4 | pub struct Guide { |
10 | pub axis: Axis, | 5 | pub axis: Axis, |
11 | pub offset: u32, | 6 | pub offset: u32, |
12 | } | 7 | } |
13 | |||
14 | impl 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 | ||
297 | pub fn lookup(env_list: &[Environment], key: &str) -> Result<LispExpr, LispError> { | 292 | pub 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 @@ | |||
1 | use std::{cmp::PartialEq, convert::TryFrom, fmt}; | 1 | use std::{ |
2 | cmp::PartialEq, | ||
3 | convert::{From, TryFrom}, | ||
4 | fmt, | ||
5 | }; | ||
2 | 6 | ||
3 | use crate::{ | 7 | use 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 | |||
356 | impl 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 | |||
368 | impl 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> { | |||
102 | pub struct Lexer<'input> { | 102 | pub 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 | ||
108 | impl<'a> Lexer<'a> { | 107 | impl<'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 | ||
173 | fn parse_number<'a>(mut input: &'a str) -> Result<(usize, Token<'a>), ParseErrorKind> { | 168 | fn 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 | ||
216 | fn parse_string<'a>(input: &'a str) -> Result<(usize, Token<'a>), ParseErrorKind> { | 211 | fn 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 | ||
263 | fn parse_name<'a>(input: &'a str) -> Result<(usize, Token<'a>), ParseErrorKind> { | 258 | fn 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 | ||
272 | fn parse_char<'a>(input: &'a str) -> Result<(usize, Token<'a>), ParseErrorKind> { | 267 | fn 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 @@ | |||
1 | use crate::lisp::{ | 1 | use 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 { | |||
210 | mod tests { | 203 | mod 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)] | ||
1 | mod app; | 2 | mod app; |
2 | mod bitmap; | 3 | mod bitmap; |
3 | mod brush; | 4 | mod 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 | ||
68 | pub fn main() { | 69 | pub 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 | ||
70 | impl Into<Color> for MessageKind { | 70 | impl 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, | 76 | impl 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 | |||
90 | impl<T> std::default::Default for UndoStack<T> | ||
91 | where | ||
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 | ||
100 | pub fn load_file<P: AsRef<Path>>(path: P) -> Result<Image, io::Error> { | 100 | pub fn load_file<P: AsRef<Path>>(path: P) -> Result<Image, io::Error> { |