From 240aca36313016df68f03954c54c2bc21910344e Mon Sep 17 00:00:00 2001 From: Akshay Date: Wed, 24 Mar 2021 18:16:52 +0530 Subject: rename env to eval, add div operator --- src/app.rs | 7 +- src/lisp/env.rs | 266 --------------------------------------------------- src/lisp/error.rs | 2 - src/lisp/eval.rs | 275 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lisp/expr.rs | 2 +- src/lisp/mod.rs | 2 +- src/lisp/number.rs | 24 ++++- 7 files changed, 301 insertions(+), 277 deletions(-) delete mode 100644 src/lisp/env.rs create mode 100644 src/lisp/eval.rs diff --git a/src/app.rs b/src/app.rs index 7417dae..008b273 100644 --- a/src/app.rs +++ b/src/app.rs @@ -3,7 +3,7 @@ use crate::{ command::CommandBox, consts::{colors::*, FONT_PATH}, dither, - lisp::{env, lex::Lexer, parse::Parser, Environment}, + lisp::{eval, lex::Lexer, parse::Parser, Environment}, message::Message, rect, symmetry::Symmetry, @@ -13,7 +13,6 @@ use crate::{ use std::{convert::From, fs::File, io::prelude::*}; -use log::{info, warn}; use obi::Image; use sdl2::{ event::Event, @@ -281,7 +280,7 @@ impl<'ctx> AppState<'ctx> { let lisp_expr = &self.command_box.text; let mut parser = Parser::new(Lexer::new(lisp_expr, 0)); let res = parser.parse_single_expr(); - match env::eval(&res.unwrap(), self, None) { + match eval::eval(&res.unwrap(), self, None) { Ok(val) => { self.message.text = format!("{}", val); } @@ -519,7 +518,7 @@ impl<'ctx> AppState<'ctx> { symmetry: Default::default(), ttf_context, undo_stack: UndoStack::new(), - lisp_env: env::with_prelude(), + lisp_env: eval::with_prelude(), zoom: 5, } } diff --git a/src/lisp/env.rs b/src/lisp/env.rs deleted file mode 100644 index c5ff6d3..0000000 --- a/src/lisp/env.rs +++ /dev/null @@ -1,266 +0,0 @@ -use crate::{ - app::AppState, - lisp::{ - error::LispError, - expr::{LispExpr, LispFunction}, - number::LispNumber, - Environment, - }, - primitive, -}; - -use std::collections::HashMap; - -use log::{error, info}; - -pub fn with_prelude() -> Environment { - let mut env = Environment::new(); - 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), "-", |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), - )) - } - }); - primitive!(env, Some(0), "toggle-grid", |_, app| { - app.toggle_grid(); - Ok(LispExpr::Unit) - }); - primitive!(env, Some(3), "if", |args, _| { - match args { - [predicate, then, else_] => { - if matches!(predicate, LispExpr::BoolLit(false)) { - Ok(else_.clone()) - } else { - Ok(then.clone()) - } - } - _ => { - error!("invalid args for `if` primitive"); - Err(LispError::EvalError) - } - } - }); - primitive!(env, Some(2), "and", |args, _| { - if args - .iter() - .any(|arg| matches!(arg, LispExpr::BoolLit(false))) - { - Ok(LispExpr::BoolLit(false)) - } else { - Ok(LispExpr::BoolLit(true)) - } - }); - primitive!(env, Some(2), "or", |args, _| { - if args - .iter() - .any(|arg| matches!(arg, LispExpr::BoolLit(true))) - { - Ok(LispExpr::BoolLit(true)) - } else { - Ok(LispExpr::BoolLit(false)) - } - }); - primitive!(env, Some(1), "not", |args, _| { - match args { - [val] => { - if matches!(val, LispExpr::BoolLit(false)) { - Ok(LispExpr::BoolLit(true)) - } else { - Ok(LispExpr::BoolLit(false)) - } - } - _ => Err(LispError::EvalError), - } - }); - primitive!(env, None, "begin", |args, _| { - if args.is_empty() { - Err(LispError::EvalError) - } else { - Ok(args.into_iter().last().unwrap().clone()) - } - }); - env -} - -pub fn eval( - expr: &LispExpr, - app: &mut AppState, - extra_env: Option<&Environment>, -) -> Result { - match expr { - LispExpr::Unit => Ok(expr.clone()), - LispExpr::StringLit(_) => Ok(expr.clone()), - LispExpr::Number(_) => Ok(expr.clone()), - LispExpr::BoolLit(_) => Ok(expr.clone()), - LispExpr::Ident(ref id) => { - lookup_extended(extra_env.unwrap_or(&Environment::new()), &app.lisp_env, id) - } - LispExpr::List(li) => { - let func_expr = &li[0]; - match func_expr { - LispExpr::Ident(s) => match s.as_ref() { - "define" => define_var(&li[1..], app), - "set!" => set_var(&li[1..], app), - "lambda" => create_lambda(&li[1..]), - _ => { - let func_expr = eval(&func_expr, app, extra_env)?; - match func_expr { - LispExpr::PrimitiveFunc(f) => { - let mut args = Vec::new(); - for item in li[1..].iter() { - args.push(eval(item, app, extra_env)?); - } - f.call(&args, app) - } - LispExpr::Function(f) => { - info!("eval custom func"); - let mut args = Vec::new(); - for item in li[1..].iter() { - args.push(eval(item, app, extra_env)?); - } - if f.params.len() != args.len() { - info!("too many or too little number of args"); - Err(LispError::EvalError) // too many or too little number of args - } else { - let mut local_env: Environment = - f.params.into_iter().zip(args).collect(); - local_env.extend(app.lisp_env.clone()); - if let Some(env) = extra_env { - local_env.extend(env.clone()); - } - if f.body.is_empty() { - return Ok(LispExpr::Unit); - } else { - eval(&LispExpr::List(f.body), app, Some(&local_env)) - } - } - } - _ => Err(LispError::EvalError), - } - } - }, - _ => Err(LispError::EvalError), - } - } - _ => Err(LispError::ParseError), - } -} - -pub fn define_var(args: &[LispExpr], app: &mut AppState) -> Result { - if args.len() != 2 { - error!("Invalid arity for `define`"); - return Err(LispError::EvalError); - } - match args { - [LispExpr::Ident(id), expr] => { - let value = eval(&expr, app, None)?; - app.lisp_env.insert(id.into(), value); - return Ok(LispExpr::Unit); - } - _ => { - error!("Invalid usage of `define`"); - return Err(LispError::EvalError); - } - } -} - -pub fn set_var(args: &[LispExpr], app: &mut AppState) -> Result { - if args.len() != 2 { - error!("Invalid arity for `define`"); - return Err(LispError::EvalError); - } - match args { - [LispExpr::Ident(id), expr] => { - let value = eval(&expr, app, None)?; - return app - .lisp_env - .insert(id.into(), value) - .ok_or(LispError::EvalError); - } - _ => { - error!("Invalid usage of `define`"); - return Err(LispError::EvalError); - } - } -} - -pub fn create_lambda(cdr: &[LispExpr]) -> Result { - if cdr.len() != 2 { - // needs params and body - error!("needs params and body"); - return Err(LispError::EvalError); - } - info!("creating lambda"); - match cdr { - [LispExpr::List(params), LispExpr::List(body)] - if params.iter().all(|p| matches!(p, LispExpr::Ident(_))) => - { - return Ok(LispExpr::Function(LispFunction { - params: params - .into_iter() - .map(|p| unwrap_ident(p.clone())) - .collect::>(), - body: body.clone(), - })); - } - _ => { - error!("Invalid usage of `define`"); - return Err(LispError::EvalError); - } - } -} - -pub fn lookup_extended( - local: &Environment, - super_: &Environment, - key: &str, -) -> Result { - if let Some(e) = local.get(key) { - Ok(e.clone()) - } else if let Some(e) = super_.get(key) { - Ok(e.clone()) - } else { - Err(LispError::EvalError) - } -} - -pub fn unwrap_number(n: &LispExpr) -> &LispNumber { - match n { - LispExpr::Number(i) => i, - _ => panic!("unwrap_number expected number"), - } -} - -pub fn unwrap_ident(i: LispExpr) -> String { - match i { - LispExpr::Ident(i) => i, - _ => panic!("unwrap_ident expected string"), - } -} diff --git a/src/lisp/error.rs b/src/lisp/error.rs index 7214753..99dec3b 100644 --- a/src/lisp/error.rs +++ b/src/lisp/error.rs @@ -1,5 +1,3 @@ -use crate::lisp::lex::Span; - #[derive(Debug, PartialEq, Copy, Clone)] pub enum LispError { ParseError, diff --git a/src/lisp/eval.rs b/src/lisp/eval.rs new file mode 100644 index 0000000..63c963c --- /dev/null +++ b/src/lisp/eval.rs @@ -0,0 +1,275 @@ +use crate::{ + app::AppState, + lisp::{ + error::LispError, + expr::{LispExpr, LispFunction}, + number::LispNumber, + Environment, + }, + primitive, +}; + +use log::{error, info}; + +pub fn with_prelude() -> Environment { + let mut env = Environment::new(); + 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), "-", |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), + )) + } + }); + primitive!(env, Some(0), "toggle-grid", |_, app| { + app.toggle_grid(); + Ok(LispExpr::Unit) + }); + primitive!(env, Some(3), "if", |args, _| { + match args { + [predicate, then, else_] => { + if matches!(predicate, LispExpr::BoolLit(false)) { + Ok(else_.clone()) + } else { + Ok(then.clone()) + } + } + _ => { + error!("invalid args for `if` primitive"); + Err(LispError::EvalError) + } + } + }); + primitive!(env, Some(2), "and", |args, _| { + if args + .iter() + .any(|arg| matches!(arg, LispExpr::BoolLit(false))) + { + Ok(LispExpr::BoolLit(false)) + } else { + Ok(LispExpr::BoolLit(true)) + } + }); + primitive!(env, Some(2), "or", |args, _| { + if args + .iter() + .any(|arg| matches!(arg, LispExpr::BoolLit(true))) + { + Ok(LispExpr::BoolLit(true)) + } else { + Ok(LispExpr::BoolLit(false)) + } + }); + primitive!(env, Some(1), "not", |args, _| { + match args { + [val] => { + if matches!(val, LispExpr::BoolLit(false)) { + Ok(LispExpr::BoolLit(true)) + } else { + Ok(LispExpr::BoolLit(false)) + } + } + _ => Err(LispError::EvalError), + } + }); + primitive!(env, None, "begin", |args, _| { + if args.is_empty() { + Err(LispError::EvalError) + } else { + Ok(args.into_iter().last().unwrap().clone()) + } + }); + primitive!(env, Some(2), "/", |args, _| { + if args.is_empty() || 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.div(*unwrap_number(&arg))?; + } + Ok(LispExpr::Number(acc)) + } + }); + env +} + +pub fn eval( + expr: &LispExpr, + app: &mut AppState, + extra_env: Option<&Environment>, +) -> Result { + match expr { + LispExpr::Unit => Ok(expr.clone()), + LispExpr::StringLit(_) => Ok(expr.clone()), + LispExpr::Number(_) => Ok(expr.clone()), + LispExpr::BoolLit(_) => Ok(expr.clone()), + LispExpr::Ident(ref id) => { + lookup_extended(extra_env.unwrap_or(&Environment::new()), &app.lisp_env, id) + } + LispExpr::List(li) => { + let func_expr = &li[0]; + match func_expr { + LispExpr::Ident(s) => match s.as_ref() { + "define" => define_var(&li[1..], app), + "set!" => set_var(&li[1..], app), + "lambda" => create_lambda(&li[1..]), + _ => { + let func_expr = eval(&func_expr, app, extra_env)?; + match func_expr { + LispExpr::PrimitiveFunc(f) => { + let mut args = Vec::new(); + for item in li[1..].iter() { + args.push(eval(item, app, extra_env)?); + } + f.call(&args, app) + } + LispExpr::Function(f) => { + info!("eval custom func"); + let mut args = Vec::new(); + for item in li[1..].iter() { + args.push(eval(item, app, extra_env)?); + } + if f.params.len() != args.len() { + info!("too many or too little number of args"); + Err(LispError::EvalError) // too many or too little number of args + } else { + let mut local_env: Environment = + f.params.into_iter().zip(args).collect(); + local_env.extend(app.lisp_env.clone()); + if let Some(env) = extra_env { + local_env.extend(env.clone()); + } + if f.body.is_empty() { + return Ok(LispExpr::Unit); + } else { + eval(&LispExpr::List(f.body), app, Some(&local_env)) + } + } + } + _ => Err(LispError::EvalError), + } + } + }, + _ => Err(LispError::EvalError), + } + } + _ => Err(LispError::ParseError), + } +} + +pub fn define_var(args: &[LispExpr], app: &mut AppState) -> Result { + if args.len() != 2 { + error!("Invalid arity for `define`"); + return Err(LispError::EvalError); + } + match args { + [LispExpr::Ident(id), expr] => { + let value = eval(&expr, app, None)?; + app.lisp_env.insert(id.into(), value); + return Ok(LispExpr::Unit); + } + _ => { + error!("Invalid usage of `define`"); + return Err(LispError::EvalError); + } + } +} + +pub fn set_var(args: &[LispExpr], app: &mut AppState) -> Result { + if args.len() != 2 { + error!("Invalid arity for `define`"); + return Err(LispError::EvalError); + } + match args { + [LispExpr::Ident(id), expr] => { + let value = eval(&expr, app, None)?; + return app + .lisp_env + .insert(id.into(), value) + .ok_or(LispError::EvalError); + } + _ => { + error!("Invalid usage of `define`"); + return Err(LispError::EvalError); + } + } +} + +pub fn create_lambda(cdr: &[LispExpr]) -> Result { + if cdr.len() != 2 { + // needs params and body + error!("needs params and body"); + return Err(LispError::EvalError); + } + info!("creating lambda"); + match cdr { + [LispExpr::List(params), LispExpr::List(body)] + if params.iter().all(|p| matches!(p, LispExpr::Ident(_))) => + { + return Ok(LispExpr::Function(LispFunction { + params: params + .into_iter() + .map(|p| unwrap_ident(p.clone())) + .collect::>(), + body: body.clone(), + })); + } + _ => { + error!("Invalid usage of `define`"); + return Err(LispError::EvalError); + } + } +} + +pub fn lookup_extended( + local: &Environment, + super_: &Environment, + key: &str, +) -> Result { + if let Some(e) = local.get(key) { + Ok(e.clone()) + } else if let Some(e) = super_.get(key) { + Ok(e.clone()) + } else { + Err(LispError::EvalError) + } +} + +pub fn unwrap_number(n: &LispExpr) -> &LispNumber { + match n { + LispExpr::Number(i) => i, + _ => panic!("unwrap_number expected number"), + } +} + +pub fn unwrap_ident(i: LispExpr) -> String { + match i { + LispExpr::Ident(i) => i, + _ => panic!("unwrap_ident expected string"), + } +} diff --git a/src/lisp/expr.rs b/src/lisp/expr.rs index 651e41f..40b39eb 100644 --- a/src/lisp/expr.rs +++ b/src/lisp/expr.rs @@ -1,7 +1,7 @@ use std::fmt; use crate::app::AppState; -use crate::lisp::{error::LispError, number::LispNumber, Environment}; +use crate::lisp::{error::LispError, number::LispNumber}; #[derive(Clone)] pub struct PrimitiveFunc { diff --git a/src/lisp/mod.rs b/src/lisp/mod.rs index dc08835..a52baa0 100644 --- a/src/lisp/mod.rs +++ b/src/lisp/mod.rs @@ -1,5 +1,5 @@ -pub mod env; pub mod error; +pub mod eval; pub mod expr; pub mod lex; pub mod number; diff --git a/src/lisp/number.rs b/src/lisp/number.rs index ddadbe6..18a41f7 100644 --- a/src/lisp/number.rs +++ b/src/lisp/number.rs @@ -1,14 +1,32 @@ use std::{ - fmt::*, + fmt, ops::{Add, Div, Mul, Sub}, }; +use crate::lisp::error::LispError; + #[derive(Debug, Copy, Clone)] pub enum LispNumber { Integer(i64), Float(f64), } +impl LispNumber { + pub fn div(self, rhs: Self) -> Result { + use LispNumber::*; + if rhs == Integer(0) || rhs == Float(0.) { + return Err(LispError::EvalError); + } else { + return Ok(match (self, rhs) { + (Integer(a), Integer(b)) => Float(a as f64 / b as f64), + (Float(a), Integer(b)) => Float(a / b as f64), + (Integer(a), Float(b)) => Float(a as f64 / b), + (Float(a), Float(b)) => Float(a / b), + }); + } + } +} + impl Add for LispNumber { type Output = Self; fn add(self, rhs: Self) -> Self::Output { @@ -60,8 +78,8 @@ impl PartialEq for LispNumber { } } -impl Display for LispNumber { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { +impl fmt::Display for LispNumber { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { LispNumber::Integer(v) => write!(f, "{}", v), LispNumber::Float(v) => write!(f, "{}", v), -- cgit v1.2.3