diff --git a/src/base.rs b/src/base.rs index ad6d66c..1c90f91 100644 --- a/src/base.rs +++ b/src/base.rs @@ -1,28 +1,18 @@ +// Solution to the #[derive(Clone)] problem: https://stackoverflow.com/questions/27883509/can-you-clone-a-closure use crate::value::*; use crate::vm::*; use imbl::*; -use std::cell::RefCell; use std::fmt; -use std::rc::Rc; +type LResult<'src> = Result, LudusError>; + +#[derive(Clone, Debug)] pub enum Base<'src> { - Unary( - &'src str, - Box) -> Result, LudusError>>, - ), - Binary( - &'src str, - Box, Value<'src>) -> Result, LudusError>>, - ), + Unary(&'src str, fn(&Value<'src>) -> LResult<'src>), + Binary(&'src str, fn(&Value<'src>, &Value<'src>) -> LResult<'src>), Ternary( &'src str, - Box< - dyn std::ops::Fn( - Value<'src>, - Value<'src>, - Value<'src>, - ) -> Result, LudusError>, - >, + fn(&Value<'src>, &Value<'src>, &Value<'src>) -> LResult<'src>, ), } @@ -36,33 +26,67 @@ impl<'src> Base<'src> { } } -impl<'src> fmt::Debug for Base<'src> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("Base").field(&self.name()).finish() +pub fn eq<'src>(x: &Value<'src>, y: &Value<'src>) -> LResult<'src> { + Ok(Value::Boolean(x == y)) +} + +pub fn add<'src>(x: &Value<'src>, y: &Value<'src>) -> LResult<'src> { + match (x, y) { + (Value::Number(x), Value::Number(y)) => Ok(Value::Number(x + y)), + _ => Err(LudusError { + msg: "add takes two numbers".to_string(), + }), + } +} + +pub fn sub<'src>(x: &Value<'src>, y: &Value<'src>) -> LResult<'src> { + match (x, y) { + (Value::Number(x), Value::Number(y)) => Ok(Value::Number(x - y)), + _ => Err(LudusError { + msg: "sub takes two numbers".to_string(), + }), + } +} + +pub fn unbox<'src>(x: &Value<'src>) -> LResult<'src> { + match x { + Value::Box(_, cell) => Ok(cell.borrow().clone()), + _ => Err(LudusError { + msg: "only boxes may be unboxed".to_string(), + }), + } +} + +pub fn store<'src>(b: &Value<'src>, val: &Value<'src>) -> LResult<'src> { + if let Value::Box(_, cell) = b { + cell.replace(val.clone()); + Ok(val.clone()) + } else { + Err(LudusError { + msg: "only boxes may store values".to_string(), + }) } } pub fn base<'src>() -> Vec<(&'src str, Value<'src>)> { let mut base = vec![]; - let eq = Base::Binary("eq", Box::new(|x, y| Ok(Value::Boolean(x == y)))); - base.push(("eq", Value::Base(&eq))); + let eq = Base::Binary("eq", eq); + base.push(("eq", Value::Base(eq))); - let add = Base::Binary( - "add", - Box::new(|x, y| match (x, y) { - (Value::Number(x), Value::Number(y)) => Ok(Value::Number(x + y)), - _ => Err(LudusError { - msg: "I can only add numbers".to_string(), - }), - }), - ); - base.push(("add", Value::Base(&add))); + let add = Base::Binary("add", add); + base.push(("add", Value::Base(add))); + + base.push(("sub", Value::Base(Base::Binary("sub", sub)))); + + base.push(("unbox", Value::Base(Base::Unary("unbox", unbox)))); + + base.push(("store", Value::Base(Base::Binary("store", store)))); base } -// add (x, y) -> number +// DONE add (x, y) -> number // and (x, y) -> value // assoc (x, y) -> dict // atan_2 (x) -> number @@ -78,6 +102,7 @@ pub fn base<'src>() -> Vec<(&'src str, Value<'src>)> { // div (x, y) -> number // doc (x) -> string // downcase (x) -> string +// DONE eq (x, y) -> bool // first (x) -> value // floor (x) -> number // get (x, y) -> value @@ -102,7 +127,7 @@ pub fn base<'src>() -> Vec<(&'src str, Value<'src>)> { // slice (x, y, z) -> list // split (x, y) -> list(string) // sqrt (x) -> number -// store! (x, y) -> value +// DONE store! (x, y) -> value // str_slice (x, y, z) -> string // stringify (x) -> string // sub (x, y) -> number @@ -113,5 +138,5 @@ pub fn base<'src>() -> Vec<(&'src str, Value<'src>)> { // triml (x) -> string // trimr (x) -> string // type (x) -> keyword -// unbox (x) -> value +// DONE unbox (x) -> value // upcase (x) -> string diff --git a/src/main.rs b/src/main.rs index 0475319..92c1e92 100644 --- a/src/main.rs +++ b/src/main.rs @@ -26,7 +26,7 @@ // * [ ] splat patterns in tuples, lists, dicts // * [ ] splats in list and dict literals // * [ ] `loop` and `recur` -// * [ ] write `base` in Rust +// * [~] write `base` in Rust // * [ ] turn this into a library function // * [ ] compile this into WASM // * [ ] perf testing @@ -53,7 +53,7 @@ use crate::base::*; pub fn main() { let src = " -eq + "; let (tokens, lex_errs) = lexer().parse(src).into_output_errors(); if lex_errs.len() > 0 { diff --git a/src/value.rs b/src/value.rs index 24981c4..c53fc9a 100644 --- a/src/value.rs +++ b/src/value.rs @@ -28,7 +28,7 @@ pub enum Value<'src> { Dict(HashMap<&'src str, Self>), Box(&'src str, Rc>), Fn(Rc>), - Base(&Base<'src>), + Base(Base<'src>), // Set(HashSet), // Sets are hard // Sets require Eq diff --git a/src/vm.rs b/src/vm.rs index 328730d..1bf1967 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -174,6 +174,35 @@ pub fn apply<'src, 'a>( (_, Value::Args(_)) => Err(LudusError { msg: "you may only call a function".to_string(), }), + (Value::Base(f), Value::Tuple(args)) => match f { + Base::Unary(_name, f) => { + if args.len() != 1 { + Err(LudusError { + msg: "wrong arity: expected 1 argument".to_string(), + }) + } else { + f(&args[0]) + } + } + Base::Binary(_name, r#fn) => { + if args.len() != 2 { + Err(LudusError { + msg: "wrong arity: expected 2 arguments".to_string(), + }) + } else { + r#fn(&args[0], &args[1]) + } + } + Base::Ternary(_name, f) => { + if args.len() != 3 { + Err(LudusError { + msg: "wrong arity: expected 3 arguments".to_string(), + }) + } else { + f(&args[0], &args[1], &args[2]) + } + } + }, _ => unreachable!(), } }