From 305bf638f823a41f391936712eef302bc6733d00 Mon Sep 17 00:00:00 2001 From: Akshay Date: Tue, 23 Mar 2021 18:56:25 +0530 Subject: expose functions to lisp interface, add primitives with macros --- src/app.rs | 51 +++++++++++++++----------------- src/lisp/env.rs | 88 +++++++++++++++++++++++--------------------------------- src/lisp/expr.rs | 19 +++++++++++- src/lisp/lex.rs | 5 +--- src/lisp/mod.rs | 2 ++ 5 files changed, 80 insertions(+), 85 deletions(-) diff --git a/src/app.rs b/src/app.rs index 9730cf2..ef299cd 100644 --- a/src/app.rs +++ b/src/app.rs @@ -13,6 +13,7 @@ use crate::{ use std::{convert::From, fs::File, io::prelude::*}; +use log::{info, warn}; use obi::Image; use sdl2::{ event::Event, @@ -74,11 +75,11 @@ impl<'ctx> AppState<'ctx> { self.start += direction.into(); } - fn width(&self) -> u32 { + pub fn width(&self) -> u32 { self.pixmap.width } - fn height(&self) -> u32 { + pub fn height(&self) -> u32 { self.pixmap.height } @@ -93,11 +94,11 @@ impl<'ctx> AppState<'ctx> { ); } - fn change_active_color(&mut self) { + pub fn change_active_color(&mut self) { self.active_color = !self.active_color; } - fn idx_at_coord>(&self, p: P) -> Option<(u32, u32)> { + pub fn idx_at_coord>(&self, p: P) -> Option<(u32, u32)> { let p: Point = p.into(); if self.within_canvas(p) { // convert p relative to start of drawing area @@ -110,17 +111,17 @@ impl<'ctx> AppState<'ctx> { } } - fn within_canvas>(&self, p: P) -> bool { + pub fn within_canvas>(&self, p: P) -> bool { let p: Point = p.into(); let (mini, maxi) = self.bounds(); p.x() < maxi.x() && p.y() < maxi.y() && p.x() >= mini.x() && p.y() >= mini.y() } - fn toggle_grid(&mut self) { + pub fn toggle_grid(&mut self) { self.grid.enabled = !self.grid.enabled; } - fn cycle_symmetry(&mut self) { + pub fn cycle_symmetry(&mut self) { let Symmetry { x, y } = self.symmetry; self.symmetry = match (x, y) { (None, None) => Symmetry { @@ -139,7 +140,7 @@ impl<'ctx> AppState<'ctx> { } } - fn paint_point>( + pub fn paint_point>( &mut self, center: P, val: bool, @@ -164,7 +165,7 @@ impl<'ctx> AppState<'ctx> { Ok(modify_record) } - fn paint_line>( + pub fn paint_line>( &mut self, start: MapPoint, end: P, @@ -191,7 +192,7 @@ impl<'ctx> AppState<'ctx> { Ok(line_modify_record) } - fn apply_operation(&mut self, op: Operation, op_kind: OpKind) { + pub fn apply_operation(&mut self, op: Operation, op_kind: OpKind) { for ModifyRecord { point, old_val, @@ -207,7 +208,7 @@ impl<'ctx> AppState<'ctx> { } } - fn commit_operation(&mut self) { + pub fn commit_operation(&mut self) { if !self.current_operation.is_empty() { let op = self .current_operation @@ -218,7 +219,7 @@ impl<'ctx> AppState<'ctx> { } } - fn zoom_in(&mut self, p: (i32, i32)) { + pub fn zoom_in(&mut self, p: (i32, i32)) { // attempt to center around cursor if let Some(p) = self.idx_at_coord(p) { let (x1, y1) = (p.0 * (self.zoom as u32), p.1 * (self.zoom as u32)); @@ -230,7 +231,7 @@ impl<'ctx> AppState<'ctx> { self.zoom += 1; } - fn zoom_out(&mut self, p: (i32, i32)) { + pub fn zoom_out(&mut self, p: (i32, i32)) { if self.zoom > 1 { // attempt to center around cursor if let Some(p) = self.idx_at_coord(p) { @@ -244,7 +245,7 @@ impl<'ctx> AppState<'ctx> { } } - fn center_grid(&mut self) { + pub fn center_grid(&mut self) { let (winsize_x, winsize_y) = self.canvas.window().size(); let grid_width = self.width() * self.zoom as u32; let grid_height = self.height() * self.zoom as u32; @@ -254,29 +255,29 @@ impl<'ctx> AppState<'ctx> { ); } - fn increase_brush_size(&mut self) { + pub fn increase_brush_size(&mut self) { self.brush_size += 1; } - fn decrease_brush_size(&mut self) { + pub fn decrease_brush_size(&mut self) { if self.brush_size > 0 { self.brush_size -= 1; } } - fn reduce_intensity(&mut self) { + pub fn reduce_intensity(&mut self) { if self.dither_level > 0 { self.dither_level -= 1; } } - fn increase_intensity(&mut self) { + pub fn increase_intensity(&mut self) { if self.dither_level < 16 { self.dither_level += 1; } } - fn eval_command(&mut self) { + pub fn eval_command(&mut self) { let lisp_expr = &self.command_box.text; let mut parser = Parser::new(Lexer::new(lisp_expr, 0)); let res = parser.parse_single_expr(); @@ -293,11 +294,8 @@ impl<'ctx> AppState<'ctx> { let image = self.export(); let encoded = image.encode().unwrap(); let mut buffer = File::create(path).unwrap(); - eprintln!("writing to file"); buffer.write_all(&encoded[..]).unwrap(); self.command_box.hist_append(); - } else { - eprintln!("cmd: {}", self.command_box.text); } self.command_box.clear(); self.mode = Mode::Draw; @@ -332,7 +330,7 @@ impl<'ctx> AppState<'ctx> { self.canvas .fill_rect(rect!( 0, - winsize_y - status_height, + winsize_y - status_height - 20, status_width, status_height )) @@ -355,7 +353,7 @@ impl<'ctx> AppState<'ctx> { self.ttf_context, status_text, BLACK, - (0, winsize_y - status_height), + (0, winsize_y - status_height - 20), ); } @@ -462,11 +460,8 @@ impl<'ctx> AppState<'ctx> { .draw_line((line_coord, 0), (line_coord, winsize_y as i32)) .unwrap(); } - // if self.mode == Mode::Draw { - // self.draw_statusline(); - // } else { + self.draw_statusline(); self.draw_command_box(); - // } self.draw_mouse(); } 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 @@ use crate::{ app::AppState, lisp::{error::LispError, expr::LispExpr, number::LispNumber, Environment}, + primitive, }; +use log::warn; + pub fn is_bound>(env: &mut Environment, name: S) -> bool { env.get(name.as_ref()).is_some() } @@ -13,21 +16,39 @@ pub fn new_binding>(env: &mut Environment, name: S, value: LispExp pub fn with_prelude() -> Environment { let mut env = Environment::new(); - new_binding( - &mut env, - "+", - LispExpr::PrimitiveFunc(|args, _| { - if args.iter().any(|arg| !matches!(arg, LispExpr::Number(_))) { - Err(LispError::EvalError) - } else { - let result = args - .iter() - .map(|n| unwrap_number(n)) - .fold(LispNumber::Integer(0), |acc, x| acc + *x); - Ok(LispExpr::Number(result)) + primitive!(env, Some(2), "+", |args, _| { + if args.iter().any(|arg| !matches!(arg, LispExpr::Number(_))) { + Err(LispError::EvalError) + } else { + Ok(LispExpr::Number( + args.iter() + .map(|arg| unwrap_number(arg)) + .fold(LispNumber::Integer(0), |acc, x| acc + *x), + )) + } + }); + primitive!(env, Some(2), "sub", |args, _| { + if args.iter().any(|arg| !matches!(arg, LispExpr::Number(_))) { + Err(LispError::EvalError) + } else { + let mut acc = unwrap_number(&args[0]).clone(); + for arg in args.into_iter().skip(1) { + acc = acc - *unwrap_number(&arg); } - }), - ); + Ok(LispExpr::Number(acc)) + } + }); + primitive!(env, Some(2), "*", |args, _| { + if args.iter().any(|arg| !matches!(arg, LispExpr::Number(_))) { + Err(LispError::EvalError) + } else { + Ok(LispExpr::Number( + args.iter() + .map(|arg| unwrap_number(arg)) + .fold(LispNumber::Integer(1), |acc, x| acc * *x), + )) + } + }); env } @@ -56,7 +77,7 @@ pub fn eval(expr: &LispExpr, app: &mut AppState) -> Result for item in li[1..].iter() { args.push(eval(item, app)?); } - (f)(&args, None) + f.call(&args, app) } _ => Err(LispError::EvalError), } @@ -78,40 +99,3 @@ pub fn unwrap_number(n: &LispExpr) -> &LispNumber { _ => panic!("unwrap_number expected number"), } } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn eval_primitive_call() { - let mut env = Environment::new(); - new_binding(&mut env, "age", LispExpr::Number(LispNumber::Float(1.4))); - new_binding( - &mut env, - "+", - LispExpr::PrimitiveFunc(|args, _| { - if args.iter().any(|arg| !matches!(arg, LispExpr::Number(_))) { - Err(LispError::EvalError) - } else { - let result = args - .iter() - .map(|n| unwrap_number(n)) - .fold(LispNumber::Integer(0), |acc, x| acc + *x); - Ok(LispExpr::Number(result)) - } - }), - ); - let mut numbers = (1..=3) - .map(LispNumber::Integer) - .map(LispExpr::Number) - .collect::>(); - let mut expr = Vec::new(); - expr.push(LispExpr::Ident("+".into())); - expr.append(&mut numbers); - // assert!(matches!( - // eval(&LispExpr::List(expr), ).unwrap(), - // LispExpr::Number(LispNumber::Integer(6)) - // )); - } -} 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 { StringLit(String), BoolLit(bool), Ident(String), - PrimitiveFunc(fn(&[LispExpr], Option<&mut AppState>) -> Result), + PrimitiveFunc(PrimitiveFunc), Function(LispFunction), // none of these depths should be zero @@ -21,6 +21,23 @@ pub enum LispExpr { Quote(Box, u32), } +#[derive(Clone)] +pub struct PrimitiveFunc { + pub arity: Option, + pub closure: fn(&[LispExpr], &mut AppState) -> Result, +} + +impl PrimitiveFunc { + pub fn call(&self, args: &[LispExpr], app: &mut AppState) -> Result { + if let Some(arity) = self.arity { + if args.len() < arity { + return Err(LispError::EvalError); + } + } + (self.closure)(args, app) + } +} + impl LispExpr { pub fn comma(self, n: u32) -> LispExpr { 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> { self.cur_pos += ch.len_utf8() as u32; continue; } - ch => { - eprintln!("some unexpected character: {}", ch); - Err(LispError::ParseError) - } + _ => Err(LispError::ParseError), }; let (size, token) = match res { 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; pub mod lex; pub mod number; pub mod parse; +#[macro_use] +mod primitives; use std::collections::HashMap; -- cgit v1.2.3