From 5824a40847ad9ae1f66dad3d9caa069d03f2a832 Mon Sep 17 00:00:00 2001 From: Akshay Date: Sat, 27 Mar 2021 13:16:14 +0530 Subject: better arity spec --- src/lisp/prelude.rs | 132 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 95 insertions(+), 37 deletions(-) diff --git a/src/lisp/prelude.rs b/src/lisp/prelude.rs index ad94cd2..6153e4f 100644 --- a/src/lisp/prelude.rs +++ b/src/lisp/prelude.rs @@ -1,14 +1,16 @@ use crate::{ lisp::{ error::{EvalError, LispError}, - expr::LispExpr, + expr::{Arity, LispExpr}, number::LispNumber, Environment, }, primitive, }; -use std::convert::TryInto; +use std::{convert::TryInto, fs::File, io::Write, path::PathBuf}; + +use log::info; #[macro_export] macro_rules! primitive { @@ -24,7 +26,7 @@ macro_rules! primitive { pub fn new_env() -> Environment { let mut env = Environment::new(); - primitive!(env, Some(2), "+", |args, _| { + primitive!(env, Arity::Atleast(2), "+", |args, _| { let nums = args .into_iter() .map(|arg| arg.try_into()) @@ -34,7 +36,7 @@ pub fn new_env() -> Environment { )); }); - primitive!(env, Some(2), "-", |args, _| { + primitive!(env, Arity::Atleast(2), "-", |args, _| { let nums = args .into_iter() .map(|arg| arg.try_into()) @@ -46,7 +48,7 @@ pub fn new_env() -> Environment { Ok(LispExpr::Number(acc)) }); - primitive!(env, Some(2), "*", |args, _| { + primitive!(env, Arity::Atleast(2), "*", |args, _| { let nums = args .into_iter() .map(|arg| arg.try_into()) @@ -56,7 +58,7 @@ pub fn new_env() -> Environment { )); }); - primitive!(env, Some(2), "/", |args, _| { + primitive!(env, Arity::Atleast(2), "/", |args, _| { let nums = args .into_iter() .map(|arg| arg.try_into()) @@ -68,27 +70,12 @@ pub fn new_env() -> Environment { Ok(LispExpr::Number(acc)) }); - primitive!(env, Some(0), "toggle-grid", |_, app| { + primitive!(env, Arity::Exact(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, _| { + primitive!(env, Arity::Atleast(2), "and", |args, _| { if args .iter() .any(|arg| matches!(arg, LispExpr::BoolLit(false))) @@ -99,7 +86,7 @@ pub fn new_env() -> Environment { } }); - primitive!(env, Some(2), "or", |args, _| { + primitive!(env, Arity::Atleast(2), "or", |args, _| { if args .iter() .any(|arg| matches!(arg, LispExpr::BoolLit(true))) @@ -110,30 +97,101 @@ pub fn new_env() -> Environment { } }); - 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, Arity::Exact(1), "not", |args, _| { + if matches!(&args[0], LispExpr::BoolLit(false)) { + Ok(LispExpr::BoolLit(true)) + } else { + Ok(LispExpr::BoolLit(false)) } }); - primitive!(env, None, "begin", |args, _| { + primitive!(env, Arity::Atleast(1), "begin", |args, _| { if args.is_empty() { - Err(EvalError::ArgumentCount(None).into()) + Err(EvalError::ArgumentCount(Arity::Atleast(1)).into()) } else { Ok(args.into_iter().last().unwrap().clone()) } }); - primitive!(env, Some(0), "quit", |_, app| { + primitive!(env, Arity::Exact(0), "quit", |_, app| { app.quit(); Ok(LispExpr::Unit) }); + + primitive!(env, Arity::Exact(2), "eq?", |args, app| { + let s = &args[0]; + let o = &args[1]; + info!("comparing {} {}", s, o); + let result = s.compare(o, &app.lisp_env); + result.map(LispExpr::BoolLit) + }); + + primitive!(env, Arity::Exact(2), ">", |args, _| { + let nums = args + .into_iter() + .map(|arg| arg.try_into()) + .collect::, LispError>>()?; + let acc = nums[0].clone(); + Ok(LispExpr::BoolLit( + nums.into_iter().skip(1).all(|&arg| acc > arg), + )) + }); + + primitive!(env, Arity::Exact(2), ">=", |args, _| { + let nums = args + .into_iter() + .map(|arg| arg.try_into()) + .collect::, LispError>>()?; + let acc = nums[0].clone(); + Ok(LispExpr::BoolLit( + nums.into_iter().skip(1).all(|&arg| acc >= arg), + )) + }); + + primitive!(env, Arity::Exact(2), "<", |args, _| { + let nums = args + .into_iter() + .map(|arg| arg.try_into()) + .collect::, LispError>>()?; + let acc = nums[0].clone(); + Ok(LispExpr::BoolLit( + nums.into_iter().skip(1).all(|&arg| acc < arg), + )) + }); + + primitive!(env, Arity::Exact(2), "<=", |args, _| { + let nums = args + .into_iter() + .map(|arg| arg.try_into()) + .collect::, LispError>>()?; + let acc = nums[0].clone(); + Ok(LispExpr::BoolLit( + nums.into_iter().skip(1).all(|&arg| acc <= arg), + )) + }); + + primitive!(env, Arity::Exact(1), "string-len", |args, _| { + match &args[0] { + LispExpr::StringLit(s) => Ok(LispExpr::Number(LispNumber::Integer(s.len() as i64))), + _ => Err(EvalError::TypeMismatch.into()), + } + }); + + primitive!(env, Arity::Atmost(1), "save", |args, app| { + let image = app.export().encode().unwrap(); + if args.len() == 1 && matches!(&args[0], LispExpr::StringLit(_)) { + let mut buffer = File::create(&args[0].as_ref()).unwrap(); + buffer.write_all(&image[..]).unwrap(); + } else if let Some(p) = app.file_name { + let file_name = p; + let mut buffer = File::create(&file_name).unwrap(); + buffer.write_all(&image[..]).unwrap(); + } else { + return Err(EvalError::NoFileName.into()); + } + + return Ok(LispExpr::Unit); + }); + env } -- cgit v1.2.3