diff options
author | Akshay <[email protected]> | 2021-03-23 13:26:25 +0000 |
---|---|---|
committer | Akshay <[email protected]> | 2021-03-23 13:26:25 +0000 |
commit | 305bf638f823a41f391936712eef302bc6733d00 (patch) | |
tree | 6a9cc8a41c691bed2027c8debdc4391aab837c89 | |
parent | a0fce05399b3ee284b6c60a409fad74c23432ce8 (diff) |
expose functions to lisp interface, add primitives with macros
-rw-r--r-- | src/app.rs | 51 | ||||
-rw-r--r-- | src/lisp/env.rs | 88 | ||||
-rw-r--r-- | src/lisp/expr.rs | 19 | ||||
-rw-r--r-- | src/lisp/lex.rs | 5 | ||||
-rw-r--r-- | src/lisp/mod.rs | 2 |
5 files changed, 80 insertions, 85 deletions
@@ -13,6 +13,7 @@ use crate::{ | |||
13 | 13 | ||
14 | use std::{convert::From, fs::File, io::prelude::*}; | 14 | use std::{convert::From, fs::File, io::prelude::*}; |
15 | 15 | ||
16 | use log::{info, warn}; | ||
16 | use obi::Image; | 17 | use obi::Image; |
17 | use sdl2::{ | 18 | use sdl2::{ |
18 | event::Event, | 19 | event::Event, |
@@ -74,11 +75,11 @@ impl<'ctx> AppState<'ctx> { | |||
74 | self.start += direction.into(); | 75 | self.start += direction.into(); |
75 | } | 76 | } |
76 | 77 | ||
77 | fn width(&self) -> u32 { | 78 | pub fn width(&self) -> u32 { |
78 | self.pixmap.width | 79 | self.pixmap.width |
79 | } | 80 | } |
80 | 81 | ||
81 | fn height(&self) -> u32 { | 82 | pub fn height(&self) -> u32 { |
82 | self.pixmap.height | 83 | self.pixmap.height |
83 | } | 84 | } |
84 | 85 | ||
@@ -93,11 +94,11 @@ impl<'ctx> AppState<'ctx> { | |||
93 | ); | 94 | ); |
94 | } | 95 | } |
95 | 96 | ||
96 | fn change_active_color(&mut self) { | 97 | pub fn change_active_color(&mut self) { |
97 | self.active_color = !self.active_color; | 98 | self.active_color = !self.active_color; |
98 | } | 99 | } |
99 | 100 | ||
100 | fn idx_at_coord<P: Into<Point>>(&self, p: P) -> Option<(u32, u32)> { | 101 | pub fn idx_at_coord<P: Into<Point>>(&self, p: P) -> Option<(u32, u32)> { |
101 | let p: Point = p.into(); | 102 | let p: Point = p.into(); |
102 | if self.within_canvas(p) { | 103 | if self.within_canvas(p) { |
103 | // convert p relative to start of drawing area | 104 | // convert p relative to start of drawing area |
@@ -110,17 +111,17 @@ impl<'ctx> AppState<'ctx> { | |||
110 | } | 111 | } |
111 | } | 112 | } |
112 | 113 | ||
113 | fn within_canvas<P: Into<Point>>(&self, p: P) -> bool { | 114 | pub fn within_canvas<P: Into<Point>>(&self, p: P) -> bool { |
114 | let p: Point = p.into(); | 115 | let p: Point = p.into(); |
115 | let (mini, maxi) = self.bounds(); | 116 | let (mini, maxi) = self.bounds(); |
116 | p.x() < maxi.x() && p.y() < maxi.y() && p.x() >= mini.x() && p.y() >= mini.y() | 117 | p.x() < maxi.x() && p.y() < maxi.y() && p.x() >= mini.x() && p.y() >= mini.y() |
117 | } | 118 | } |
118 | 119 | ||
119 | fn toggle_grid(&mut self) { | 120 | pub fn toggle_grid(&mut self) { |
120 | self.grid.enabled = !self.grid.enabled; | 121 | self.grid.enabled = !self.grid.enabled; |
121 | } | 122 | } |
122 | 123 | ||
123 | fn cycle_symmetry(&mut self) { | 124 | pub fn cycle_symmetry(&mut self) { |
124 | let Symmetry { x, y } = self.symmetry; | 125 | let Symmetry { x, y } = self.symmetry; |
125 | self.symmetry = match (x, y) { | 126 | self.symmetry = match (x, y) { |
126 | (None, None) => Symmetry { | 127 | (None, None) => Symmetry { |
@@ -139,7 +140,7 @@ impl<'ctx> AppState<'ctx> { | |||
139 | } | 140 | } |
140 | } | 141 | } |
141 | 142 | ||
142 | fn paint_point<P: Into<Point>>( | 143 | pub fn paint_point<P: Into<Point>>( |
143 | &mut self, | 144 | &mut self, |
144 | center: P, | 145 | center: P, |
145 | val: bool, | 146 | val: bool, |
@@ -164,7 +165,7 @@ impl<'ctx> AppState<'ctx> { | |||
164 | Ok(modify_record) | 165 | Ok(modify_record) |
165 | } | 166 | } |
166 | 167 | ||
167 | fn paint_line<P: Into<Point>>( | 168 | pub fn paint_line<P: Into<Point>>( |
168 | &mut self, | 169 | &mut self, |
169 | start: MapPoint, | 170 | start: MapPoint, |
170 | end: P, | 171 | end: P, |
@@ -191,7 +192,7 @@ impl<'ctx> AppState<'ctx> { | |||
191 | Ok(line_modify_record) | 192 | Ok(line_modify_record) |
192 | } | 193 | } |
193 | 194 | ||
194 | fn apply_operation(&mut self, op: Operation, op_kind: OpKind) { | 195 | pub fn apply_operation(&mut self, op: Operation, op_kind: OpKind) { |
195 | for ModifyRecord { | 196 | for ModifyRecord { |
196 | point, | 197 | point, |
197 | old_val, | 198 | old_val, |
@@ -207,7 +208,7 @@ impl<'ctx> AppState<'ctx> { | |||
207 | } | 208 | } |
208 | } | 209 | } |
209 | 210 | ||
210 | fn commit_operation(&mut self) { | 211 | pub fn commit_operation(&mut self) { |
211 | if !self.current_operation.is_empty() { | 212 | if !self.current_operation.is_empty() { |
212 | let op = self | 213 | let op = self |
213 | .current_operation | 214 | .current_operation |
@@ -218,7 +219,7 @@ impl<'ctx> AppState<'ctx> { | |||
218 | } | 219 | } |
219 | } | 220 | } |
220 | 221 | ||
221 | fn zoom_in(&mut self, p: (i32, i32)) { | 222 | pub fn zoom_in(&mut self, p: (i32, i32)) { |
222 | // attempt to center around cursor | 223 | // attempt to center around cursor |
223 | if let Some(p) = self.idx_at_coord(p) { | 224 | if let Some(p) = self.idx_at_coord(p) { |
224 | let (x1, y1) = (p.0 * (self.zoom as u32), p.1 * (self.zoom as u32)); | 225 | let (x1, y1) = (p.0 * (self.zoom as u32), p.1 * (self.zoom as u32)); |
@@ -230,7 +231,7 @@ impl<'ctx> AppState<'ctx> { | |||
230 | self.zoom += 1; | 231 | self.zoom += 1; |
231 | } | 232 | } |
232 | 233 | ||
233 | fn zoom_out(&mut self, p: (i32, i32)) { | 234 | pub fn zoom_out(&mut self, p: (i32, i32)) { |
234 | if self.zoom > 1 { | 235 | if self.zoom > 1 { |
235 | // attempt to center around cursor | 236 | // attempt to center around cursor |
236 | if let Some(p) = self.idx_at_coord(p) { | 237 | if let Some(p) = self.idx_at_coord(p) { |
@@ -244,7 +245,7 @@ impl<'ctx> AppState<'ctx> { | |||
244 | } | 245 | } |
245 | } | 246 | } |
246 | 247 | ||
247 | fn center_grid(&mut self) { | 248 | pub fn center_grid(&mut self) { |
248 | let (winsize_x, winsize_y) = self.canvas.window().size(); | 249 | let (winsize_x, winsize_y) = self.canvas.window().size(); |
249 | let grid_width = self.width() * self.zoom as u32; | 250 | let grid_width = self.width() * self.zoom as u32; |
250 | let grid_height = self.height() * self.zoom as u32; | 251 | let grid_height = self.height() * self.zoom as u32; |
@@ -254,29 +255,29 @@ impl<'ctx> AppState<'ctx> { | |||
254 | ); | 255 | ); |
255 | } | 256 | } |
256 | 257 | ||
257 | fn increase_brush_size(&mut self) { | 258 | pub fn increase_brush_size(&mut self) { |
258 | self.brush_size += 1; | 259 | self.brush_size += 1; |
259 | } | 260 | } |
260 | 261 | ||
261 | fn decrease_brush_size(&mut self) { | 262 | pub fn decrease_brush_size(&mut self) { |
262 | if self.brush_size > 0 { | 263 | if self.brush_size > 0 { |
263 | self.brush_size -= 1; | 264 | self.brush_size -= 1; |
264 | } | 265 | } |
265 | } | 266 | } |
266 | 267 | ||
267 | fn reduce_intensity(&mut self) { | 268 | pub fn reduce_intensity(&mut self) { |
268 | if self.dither_level > 0 { | 269 | if self.dither_level > 0 { |
269 | self.dither_level -= 1; | 270 | self.dither_level -= 1; |
270 | } | 271 | } |
271 | } | 272 | } |
272 | 273 | ||
273 | fn increase_intensity(&mut self) { | 274 | pub fn increase_intensity(&mut self) { |
274 | if self.dither_level < 16 { | 275 | if self.dither_level < 16 { |
275 | self.dither_level += 1; | 276 | self.dither_level += 1; |
276 | } | 277 | } |
277 | } | 278 | } |
278 | 279 | ||
279 | fn eval_command(&mut self) { | 280 | pub fn eval_command(&mut self) { |
280 | let lisp_expr = &self.command_box.text; | 281 | let lisp_expr = &self.command_box.text; |
281 | let mut parser = Parser::new(Lexer::new(lisp_expr, 0)); | 282 | let mut parser = Parser::new(Lexer::new(lisp_expr, 0)); |
282 | let res = parser.parse_single_expr(); | 283 | let res = parser.parse_single_expr(); |
@@ -293,11 +294,8 @@ impl<'ctx> AppState<'ctx> { | |||
293 | let image = self.export(); | 294 | let image = self.export(); |
294 | let encoded = image.encode().unwrap(); | 295 | let encoded = image.encode().unwrap(); |
295 | let mut buffer = File::create(path).unwrap(); | 296 | let mut buffer = File::create(path).unwrap(); |
296 | eprintln!("writing to file"); | ||
297 | buffer.write_all(&encoded[..]).unwrap(); | 297 | buffer.write_all(&encoded[..]).unwrap(); |
298 | self.command_box.hist_append(); | 298 | self.command_box.hist_append(); |
299 | } else { | ||
300 | eprintln!("cmd: {}", self.command_box.text); | ||
301 | } | 299 | } |
302 | self.command_box.clear(); | 300 | self.command_box.clear(); |
303 | self.mode = Mode::Draw; | 301 | self.mode = Mode::Draw; |
@@ -332,7 +330,7 @@ impl<'ctx> AppState<'ctx> { | |||
332 | self.canvas | 330 | self.canvas |
333 | .fill_rect(rect!( | 331 | .fill_rect(rect!( |
334 | 0, | 332 | 0, |
335 | winsize_y - status_height, | 333 | winsize_y - status_height - 20, |
336 | status_width, | 334 | status_width, |
337 | status_height | 335 | status_height |
338 | )) | 336 | )) |
@@ -355,7 +353,7 @@ impl<'ctx> AppState<'ctx> { | |||
355 | self.ttf_context, | 353 | self.ttf_context, |
356 | status_text, | 354 | status_text, |
357 | BLACK, | 355 | BLACK, |
358 | (0, winsize_y - status_height), | 356 | (0, winsize_y - status_height - 20), |
359 | ); | 357 | ); |
360 | } | 358 | } |
361 | 359 | ||
@@ -462,11 +460,8 @@ impl<'ctx> AppState<'ctx> { | |||
462 | .draw_line((line_coord, 0), (line_coord, winsize_y as i32)) | 460 | .draw_line((line_coord, 0), (line_coord, winsize_y as i32)) |
463 | .unwrap(); | 461 | .unwrap(); |
464 | } | 462 | } |
465 | // if self.mode == Mode::Draw { | 463 | self.draw_statusline(); |
466 | // self.draw_statusline(); | ||
467 | // } else { | ||
468 | self.draw_command_box(); | 464 | self.draw_command_box(); |
469 | // } | ||
470 | self.draw_mouse(); | 465 | self.draw_mouse(); |
471 | } | 466 | } |
472 | 467 | ||
diff --git a/src/lisp/env.rs b/src/lisp/env.rs index ad7cc2b..94c0e05 100644 --- a/src/lisp/env.rs +++ b/src/lisp/env.rs | |||
@@ -1,8 +1,11 @@ | |||
1 | use crate::{ | 1 | use crate::{ |
2 | app::AppState, | 2 | app::AppState, |
3 | lisp::{error::LispError, expr::LispExpr, number::LispNumber, Environment}, | 3 | lisp::{error::LispError, expr::LispExpr, number::LispNumber, Environment}, |
4 | primitive, | ||
4 | }; | 5 | }; |
5 | 6 | ||
7 | use log::warn; | ||
8 | |||
6 | pub fn is_bound<S: AsRef<str>>(env: &mut Environment, name: S) -> bool { | 9 | pub fn is_bound<S: AsRef<str>>(env: &mut Environment, name: S) -> bool { |
7 | env.get(name.as_ref()).is_some() | 10 | env.get(name.as_ref()).is_some() |
8 | } | 11 | } |
@@ -13,21 +16,39 @@ pub fn new_binding<S: AsRef<str>>(env: &mut Environment, name: S, value: LispExp | |||
13 | 16 | ||
14 | pub fn with_prelude() -> Environment { | 17 | pub fn with_prelude() -> Environment { |
15 | let mut env = Environment::new(); | 18 | let mut env = Environment::new(); |
16 | new_binding( | 19 | primitive!(env, Some(2), "+", |args, _| { |
17 | &mut env, | 20 | if args.iter().any(|arg| !matches!(arg, LispExpr::Number(_))) { |
18 | "+", | 21 | Err(LispError::EvalError) |
19 | LispExpr::PrimitiveFunc(|args, _| { | 22 | } else { |
20 | if args.iter().any(|arg| !matches!(arg, LispExpr::Number(_))) { | 23 | Ok(LispExpr::Number( |
21 | Err(LispError::EvalError) | 24 | args.iter() |
22 | } else { | 25 | .map(|arg| unwrap_number(arg)) |
23 | let result = args | 26 | .fold(LispNumber::Integer(0), |acc, x| acc + *x), |
24 | .iter() | 27 | )) |
25 | .map(|n| unwrap_number(n)) | 28 | } |
26 | .fold(LispNumber::Integer(0), |acc, x| acc + *x); | 29 | }); |
27 | Ok(LispExpr::Number(result)) | 30 | primitive!(env, Some(2), "sub", |args, _| { |
31 | if args.iter().any(|arg| !matches!(arg, LispExpr::Number(_))) { | ||
32 | Err(LispError::EvalError) | ||
33 | } else { | ||
34 | let mut acc = unwrap_number(&args[0]).clone(); | ||
35 | for arg in args.into_iter().skip(1) { | ||
36 | acc = acc - *unwrap_number(&arg); | ||
28 | } | 37 | } |
29 | }), | 38 | Ok(LispExpr::Number(acc)) |
30 | ); | 39 | } |
40 | }); | ||
41 | primitive!(env, Some(2), "*", |args, _| { | ||
42 | if args.iter().any(|arg| !matches!(arg, LispExpr::Number(_))) { | ||
43 | Err(LispError::EvalError) | ||
44 | } else { | ||
45 | Ok(LispExpr::Number( | ||
46 | args.iter() | ||
47 | .map(|arg| unwrap_number(arg)) | ||
48 | .fold(LispNumber::Integer(1), |acc, x| acc * *x), | ||
49 | )) | ||
50 | } | ||
51 | }); | ||
31 | env | 52 | env |
32 | } | 53 | } |
33 | 54 | ||
@@ -56,7 +77,7 @@ pub fn eval(expr: &LispExpr, app: &mut AppState) -> Result<LispExpr, LispError> | |||
56 | for item in li[1..].iter() { | 77 | for item in li[1..].iter() { |
57 | args.push(eval(item, app)?); | 78 | args.push(eval(item, app)?); |
58 | } | 79 | } |
59 | (f)(&args, None) | 80 | f.call(&args, app) |
60 | } | 81 | } |
61 | _ => Err(LispError::EvalError), | 82 | _ => Err(LispError::EvalError), |
62 | } | 83 | } |
@@ -78,40 +99,3 @@ pub fn unwrap_number(n: &LispExpr) -> &LispNumber { | |||
78 | _ => panic!("unwrap_number expected number"), | 99 | _ => panic!("unwrap_number expected number"), |
79 | } | 100 | } |
80 | } | 101 | } |
81 | |||
82 | #[cfg(test)] | ||
83 | mod tests { | ||
84 | use super::*; | ||
85 | |||
86 | #[test] | ||
87 | fn eval_primitive_call() { | ||
88 | let mut env = Environment::new(); | ||
89 | new_binding(&mut env, "age", LispExpr::Number(LispNumber::Float(1.4))); | ||
90 | new_binding( | ||
91 | &mut env, | ||
92 | "+", | ||
93 | LispExpr::PrimitiveFunc(|args, _| { | ||
94 | if args.iter().any(|arg| !matches!(arg, LispExpr::Number(_))) { | ||
95 | Err(LispError::EvalError) | ||
96 | } else { | ||
97 | let result = args | ||
98 | .iter() | ||
99 | .map(|n| unwrap_number(n)) | ||
100 | .fold(LispNumber::Integer(0), |acc, x| acc + *x); | ||
101 | Ok(LispExpr::Number(result)) | ||
102 | } | ||
103 | }), | ||
104 | ); | ||
105 | let mut numbers = (1..=3) | ||
106 | .map(LispNumber::Integer) | ||
107 | .map(LispExpr::Number) | ||
108 | .collect::<Vec<_>>(); | ||
109 | let mut expr = Vec::new(); | ||
110 | expr.push(LispExpr::Ident("+".into())); | ||
111 | expr.append(&mut numbers); | ||
112 | // assert!(matches!( | ||
113 | // eval(&LispExpr::List(expr), ).unwrap(), | ||
114 | // LispExpr::Number(LispNumber::Integer(6)) | ||
115 | // )); | ||
116 | } | ||
117 | } | ||
diff --git a/src/lisp/expr.rs b/src/lisp/expr.rs index 4676a3e..adacc1c 100644 --- a/src/lisp/expr.rs +++ b/src/lisp/expr.rs | |||
@@ -11,7 +11,7 @@ pub enum LispExpr { | |||
11 | StringLit(String), | 11 | StringLit(String), |
12 | BoolLit(bool), | 12 | BoolLit(bool), |
13 | Ident(String), | 13 | Ident(String), |
14 | PrimitiveFunc(fn(&[LispExpr], Option<&mut AppState>) -> Result<LispExpr, LispError>), | 14 | PrimitiveFunc(PrimitiveFunc), |
15 | Function(LispFunction), | 15 | Function(LispFunction), |
16 | 16 | ||
17 | // none of these depths should be zero | 17 | // none of these depths should be zero |
@@ -21,6 +21,23 @@ pub enum LispExpr { | |||
21 | Quote(Box<LispExpr>, u32), | 21 | Quote(Box<LispExpr>, u32), |
22 | } | 22 | } |
23 | 23 | ||
24 | #[derive(Clone)] | ||
25 | pub struct PrimitiveFunc { | ||
26 | pub arity: Option<usize>, | ||
27 | pub closure: fn(&[LispExpr], &mut AppState) -> Result<LispExpr, LispError>, | ||
28 | } | ||
29 | |||
30 | impl PrimitiveFunc { | ||
31 | pub fn call(&self, args: &[LispExpr], app: &mut AppState) -> Result<LispExpr, LispError> { | ||
32 | if let Some(arity) = self.arity { | ||
33 | if args.len() < arity { | ||
34 | return Err(LispError::EvalError); | ||
35 | } | ||
36 | } | ||
37 | (self.closure)(args, app) | ||
38 | } | ||
39 | } | ||
40 | |||
24 | impl LispExpr { | 41 | impl LispExpr { |
25 | pub fn comma(self, n: u32) -> LispExpr { | 42 | pub fn comma(self, n: u32) -> LispExpr { |
26 | match self { | 43 | match self { |
diff --git a/src/lisp/lex.rs b/src/lisp/lex.rs index 3b1389d..30f49fa 100644 --- a/src/lisp/lex.rs +++ b/src/lisp/lex.rs | |||
@@ -94,10 +94,7 @@ impl<'a> Lexer<'a> { | |||
94 | self.cur_pos += ch.len_utf8() as u32; | 94 | self.cur_pos += ch.len_utf8() as u32; |
95 | continue; | 95 | continue; |
96 | } | 96 | } |
97 | ch => { | 97 | _ => Err(LispError::ParseError), |
98 | eprintln!("some unexpected character: {}", ch); | ||
99 | Err(LispError::ParseError) | ||
100 | } | ||
101 | }; | 98 | }; |
102 | let (size, token) = match res { | 99 | let (size, token) = match res { |
103 | Ok(v) => v, | 100 | Ok(v) => v, |
diff --git a/src/lisp/mod.rs b/src/lisp/mod.rs index 5166a04..2a19314 100644 --- a/src/lisp/mod.rs +++ b/src/lisp/mod.rs | |||
@@ -4,6 +4,8 @@ pub mod expr; | |||
4 | pub mod lex; | 4 | pub mod lex; |
5 | pub mod number; | 5 | pub mod number; |
6 | pub mod parse; | 6 | pub mod parse; |
7 | #[macro_use] | ||
8 | mod primitives; | ||
7 | 9 | ||
8 | use std::collections::HashMap; | 10 | use std::collections::HashMap; |
9 | 11 | ||