From 5a232822a25604e669740a803eea8d9889772d74 Mon Sep 17 00:00:00 2001 From: Akshay Date: Thu, 25 Mar 2021 13:06:42 +0530 Subject: move primitives.rs to prelude.rs --- src/lisp/eval.rs | 249 +++++++++++++++++-------------------------------- src/lisp/prelude.rs | 139 +++++++++++++++++++++++++++ src/lisp/primitives.rs | 10 -- 3 files changed, 224 insertions(+), 174 deletions(-) create mode 100644 src/lisp/prelude.rs delete mode 100644 src/lisp/primitives.rs diff --git a/src/lisp/eval.rs b/src/lisp/eval.rs index 8bfa86b..5accec4 100644 --- a/src/lisp/eval.rs +++ b/src/lisp/eval.rs @@ -1,140 +1,24 @@ use crate::{ app::AppState, lisp::{ - error::LispError, - expr::{LispExpr, LispFunction}, + error::{EvalError, LispError}, + expr::{Ident, LispExpr, LispFunction}, number::LispNumber, - Environment, + EnvList, Environment, }, - primitive, }; -use log::{error, info}; +use std::convert::TryInto; -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)) - } - }); - primitive!(env, Some(0), "quit", |_, app| { - app.quit(); - Ok(LispExpr::Unit) - }); - env -} +use log::{error, info}; -pub fn eval( - expr: &LispExpr, - app: &mut AppState, - extra_env: Option<&Environment>, -) -> Result { +pub fn eval(expr: &LispExpr, app: &mut AppState) -> 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::Ident(ref id) => lookup_extended(&app.lisp_env, id), LispExpr::List(li) => { let func_expr = &li[0]; match func_expr { @@ -143,12 +27,12 @@ pub fn eval( "set!" => set_var(&li[1..], app), "lambda" => create_lambda(&li[1..]), _ => { - let func_expr = eval(&func_expr, app, extra_env)?; + let func_expr = eval(&func_expr, app)?; match func_expr { LispExpr::PrimitiveFunc(f) => { let mut args = Vec::new(); for item in li[1..].iter() { - args.push(eval(item, app, extra_env)?); + args.push(eval(item, app)?); } f.call(&args, app) } @@ -156,50 +40,83 @@ pub fn eval( info!("eval custom func"); let mut args = Vec::new(); for item in li[1..].iter() { - args.push(eval(item, app, extra_env)?); + args.push(eval(item, app)?); } 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 + Err(EvalError::ArgumentCount(Some(f.params.len() as u32)) + .into()) } else { - let mut local_env: Environment = + let mut nested_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); + app.lisp_env.push(nested_env); + let result = if f.body.is_empty() { + Ok(LispExpr::Unit) } else { - eval(&LispExpr::List(f.body), app, Some(&local_env)) - } + eval(&LispExpr::List(f.body), app) + }; + app.lisp_env.pop(); + return result; } } - _ => Err(LispError::EvalError), + _ => Err(EvalError::BadForm.into()), } } }, - _ => Err(LispError::EvalError), + _ => Err(EvalError::BadForm.into()), } } - _ => Err(LispError::ParseError), + _ => Err(EvalError::BadForm.into()), } } pub fn define_var(args: &[LispExpr], app: &mut AppState) -> Result { if args.len() != 2 { error!("Invalid arity for `define`"); - return Err(LispError::EvalError); + return Err(EvalError::ArgumentCount(Some(2)).into()); } match args { [LispExpr::Ident(id), expr] => { - let value = eval(&expr, app, None)?; - app.lisp_env.insert(id.into(), value); + let value = eval(&expr, app)?; + let local_env = app.lisp_env.last_mut(); + if let Some(env) = local_env { + env.insert(id.into(), value); + } else { + error!("Unable to create global definition"); + return Err(EvalError::BadForm.into()); + } + return Ok(LispExpr::Unit); + } + [LispExpr::List(shorthand), LispExpr::List(body)] => { + // (define (func arg) ) shorthand + + let id = unwrap_ident(shorthand[0].clone()); + let params = if shorthand.len() > 1 { + &shorthand[1..] + } else { + &[] + } + .to_vec() + .into_iter() + .map(|arg| arg.try_into()) + .collect::, LispError>>()?; + let value = LispExpr::Function(LispFunction { + params, + body: body.to_vec(), + }); + + let local_env = app.lisp_env.last_mut(); + if let Some(env) = local_env { + env.insert(id.into(), value); + } else { + error!("Unable to create global definition"); + return Err(EvalError::BadForm.into()); + } return Ok(LispExpr::Unit); } _ => { error!("Invalid usage of `define`"); - return Err(LispError::EvalError); + Err(EvalError::BadForm.into()) } } } @@ -207,19 +124,24 @@ pub fn define_var(args: &[LispExpr], app: &mut AppState) -> Result Result { if args.len() != 2 { error!("Invalid arity for `define`"); - return Err(LispError::EvalError); + return Err(EvalError::ArgumentCount(Some(2)).into()); } match args { [LispExpr::Ident(id), expr] => { - let value = eval(&expr, app, None)?; - return app - .lisp_env - .insert(id.into(), value) - .ok_or(LispError::EvalError); + let value = eval(&expr, app)?; + let local_env = app.lisp_env.last_mut(); + if let Some(env) = local_env { + return env + .insert(id.into(), value) + .ok_or(EvalError::UnboundVariable(id.into()).into()); + } else { + error!("Unable to set in global env!"); + return Err(EvalError::BadForm.into()); + } } _ => { - error!("Invalid usage of `define`"); - return Err(LispError::EvalError); + error!("Invalid usage of `set!`"); + return Err(EvalError::BadForm.into()); } } } @@ -228,7 +150,7 @@ pub fn create_lambda(cdr: &[LispExpr]) -> Result { if cdr.len() != 2 { // needs params and body error!("needs params and body"); - return Err(LispError::EvalError); + return Err(EvalError::ArgumentCount(Some(2)).into()); } info!("creating lambda"); match cdr { @@ -244,23 +166,22 @@ pub fn create_lambda(cdr: &[LispExpr]) -> Result { })); } _ => { - error!("Invalid usage of `define`"); - return Err(LispError::EvalError); + error!("Invalid usage of `lambda`"); + return Err(EvalError::BadForm.into()); } } } -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()) +pub fn lookup_extended(env_list: &[Environment], key: &str) -> Result { + if env_list.is_empty() { + return Err(EvalError::UnboundVariable(key.into()).into()); } else { - Err(LispError::EvalError) + let local_env = env_list.last().unwrap(); + if let Some(val) = local_env.get(key) { + return Ok(val.clone()); + } else { + return lookup_extended(&env_list[..env_list.len() - 1], key); + } } } diff --git a/src/lisp/prelude.rs b/src/lisp/prelude.rs new file mode 100644 index 0000000..ad94cd2 --- /dev/null +++ b/src/lisp/prelude.rs @@ -0,0 +1,139 @@ +use crate::{ + lisp::{ + error::{EvalError, LispError}, + expr::LispExpr, + number::LispNumber, + Environment, + }, + primitive, +}; + +use std::convert::TryInto; + +#[macro_export] +macro_rules! primitive { + ($env:expr, $arity:expr, $name:expr, $closure:expr) => { + let val = crate::lisp::expr::LispExpr::PrimitiveFunc(crate::lisp::expr::PrimitiveFunc { + arity: $arity, + closure: $closure, + }); + let _ = $env.insert($name.to_string(), val); + }; +} + +pub fn new_env() -> Environment { + let mut env = Environment::new(); + + primitive!(env, Some(2), "+", |args, _| { + let nums = args + .into_iter() + .map(|arg| arg.try_into()) + .collect::, LispError>>()?; + return Ok(LispExpr::Number( + nums.iter().fold(LispNumber::Integer(0), |acc, &x| acc + *x), + )); + }); + + primitive!(env, Some(2), "-", |args, _| { + let nums = args + .into_iter() + .map(|arg| arg.try_into()) + .collect::, LispError>>()?; + let mut acc = nums[0].clone(); + for arg in nums.into_iter().skip(1) { + acc = acc - *arg; + } + Ok(LispExpr::Number(acc)) + }); + + primitive!(env, Some(2), "*", |args, _| { + let nums = args + .into_iter() + .map(|arg| arg.try_into()) + .collect::, LispError>>()?; + return Ok(LispExpr::Number( + nums.iter().fold(LispNumber::Integer(1), |acc, &x| acc * *x), + )); + }); + + primitive!(env, Some(2), "/", |args, _| { + let nums = args + .into_iter() + .map(|arg| arg.try_into()) + .collect::, LispError>>()?; + let mut acc = nums[0].clone(); + for arg in nums.into_iter().skip(1) { + acc = acc.div(*arg)?; + } + Ok(LispExpr::Number(acc)) + }); + + 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()) + } + } + _ => { + panic!("panicked at `if` expression") + } + } + }); + + 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(EvalError::ArgumentCount(Some(1)).into()), + } + }); + + primitive!(env, None, "begin", |args, _| { + if args.is_empty() { + Err(EvalError::ArgumentCount(None).into()) + } else { + Ok(args.into_iter().last().unwrap().clone()) + } + }); + + primitive!(env, Some(0), "quit", |_, app| { + app.quit(); + Ok(LispExpr::Unit) + }); + env +} diff --git a/src/lisp/primitives.rs b/src/lisp/primitives.rs deleted file mode 100644 index 1c9fe8e..0000000 --- a/src/lisp/primitives.rs +++ /dev/null @@ -1,10 +0,0 @@ -#[macro_export] -macro_rules! primitive { - ($env:expr, $arity:expr, $name:expr, $closure:expr) => { - let val = crate::lisp::expr::LispExpr::PrimitiveFunc(crate::lisp::expr::PrimitiveFunc { - arity: $arity, - closure: $closure, - }); - let _ = $env.insert($name.to_string(), val); - }; -} -- cgit v1.2.3