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/lisp/eval.rs | 275 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 275 insertions(+) create mode 100644 src/lisp/eval.rs (limited to 'src/lisp/eval.rs') 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"), + } +} -- cgit v1.2.3