diff --git a/Cargo.toml b/Cargo.toml index 23231ca..e700f56 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,3 +11,4 @@ chumsky = { git = "https://github.com/zesterer/chumsky", features = ["label"] } imbl = "3.0.0" struct_scalpel = "0.1.1" tailcall = "1.0.1" +ran = "2.0.1" diff --git a/src/base.rs b/src/base.rs index 077beab..09d2fce 100644 --- a/src/base.rs +++ b/src/base.rs @@ -1,9 +1,11 @@ use crate::value::*; use imbl::*; +use ran::ran_f64; use std::rc::Rc; #[derive(Clone, Debug)] pub enum Base<'src> { + Nullary(fn() -> Value<'src>), Unary(fn(&Value<'src>) -> Value<'src>), Binary(fn(&Value<'src>, &Value<'src>) -> Value<'src>), Ternary(fn(&Value<'src>, &Value<'src>, &Value<'src>) -> Value<'src>), @@ -384,72 +386,262 @@ pub fn r#type<'src>(x: &Value<'src>) -> Value<'src> { } } +pub fn split<'src>(source: &Value<'src>, splitter: &Value) -> Value<'src> { + match (source, splitter) { + (Value::AllocatedString(source), Value::AllocatedString(splitter)) => { + let parts = source.split_terminator(splitter.as_str()); + let mut list = vector![]; + for part in parts { + list.push_back(Value::AllocatedString(Rc::new(part.to_string()))); + } + Value::List(list) + } + (Value::AllocatedString(source), Value::InternedString(splitter)) => { + let parts = source.split_terminator(splitter); + let mut list = vector![]; + for part in parts { + list.push_back(Value::AllocatedString(Rc::new(part.to_string()))); + } + Value::List(list) + } + (Value::InternedString(source), Value::AllocatedString(splitter)) => { + let parts = source.split_terminator(splitter.as_str()); + let mut list = vector![]; + for part in parts { + list.push_back(Value::AllocatedString(Rc::new(part.to_string()))); + } + Value::List(list) + } + (Value::InternedString(source), Value::InternedString(splitter)) => { + let parts = source.split_terminator(splitter); + let mut list = vector![]; + for part in parts { + list.push_back(Value::AllocatedString(Rc::new(part.to_string()))); + } + Value::List(list) + } + _ => unreachable!("internal Ludus error"), + } +} + +pub fn upcase<'src>(string: &Value<'src>) -> Value<'src> { + match string { + Value::AllocatedString(string) => Value::AllocatedString(Rc::new(string.to_uppercase())), + Value::InternedString(string) => Value::AllocatedString(Rc::new(string.to_uppercase())), + _ => unreachable!("internal Ludus error"), + } +} + +pub fn downcase<'src>(string: &Value<'src>) -> Value<'src> { + match string { + Value::AllocatedString(string) => Value::AllocatedString(Rc::new(string.to_lowercase())), + Value::InternedString(string) => Value::AllocatedString(Rc::new(string.to_lowercase())), + _ => unreachable!("internal Ludus error"), + } +} + +pub fn trim<'src>(string: &Value<'src>) -> Value<'src> { + match string { + Value::AllocatedString(string) => { + Value::AllocatedString(Rc::new(string.trim().to_string())) + } + Value::InternedString(string) => Value::AllocatedString(Rc::new(string.trim().to_string())), + _ => unreachable!("internal Ludus error"), + } +} + +pub fn triml<'src>(string: &Value<'src>) -> Value<'src> { + match string { + Value::AllocatedString(string) => { + Value::AllocatedString(Rc::new(string.trim_start().to_string())) + } + Value::InternedString(string) => { + Value::AllocatedString(Rc::new(string.trim_start().to_string())) + } + _ => unreachable!("internal Ludus error"), + } +} + +pub fn trimr<'src>(string: &Value<'src>) -> Value<'src> { + match string { + Value::AllocatedString(string) => { + Value::AllocatedString(Rc::new(string.trim_end().to_string())) + } + Value::InternedString(string) => { + Value::AllocatedString(Rc::new(string.trim_end().to_string())) + } + _ => unreachable!("internal Ludus error"), + } +} + +pub fn atan_2<'src>(x: &Value, y: &Value) -> Value<'src> { + match (x, y) { + (Value::Number(x), Value::Number(y)) => Value::Number(x.atan2(*y)), + _ => unreachable!("internal Ludus error"), + } +} + +pub fn ceil<'src>(x: &Value) -> Value<'src> { + match x { + Value::Number(x) => Value::Number(x.ceil()), + _ => unreachable!("internal Ludus error"), + } +} + +pub fn cos<'src>(x: &Value) -> Value<'src> { + match x { + Value::Number(x) => Value::Number(x.cos()), + _ => unreachable!("internal Ludus error"), + } +} + +pub fn floor<'src>(x: &Value) -> Value<'src> { + match x { + Value::Number(x) => Value::Number(x.floor()), + _ => unreachable!("internal Ludus error"), + } +} + +pub fn random<'src>() -> Value<'src> { + Value::Number(ran_f64()) +} + +pub fn round<'src>(x: &Value) -> Value<'src> { + match x { + Value::Number(x) => Value::Number(x.round()), + _ => unreachable!("internal Ludus error"), + } +} + +pub fn sin<'src>(x: &Value) -> Value<'src> { + match x { + Value::Number(x) => Value::Number(x.sin()), + _ => unreachable!("internal Ludus error"), + } +} + +pub fn sqrt<'src>(x: &Value) -> Value<'src> { + match x { + Value::Number(x) => Value::Number(x.sqrt()), + _ => unreachable!("internal Ludus error"), + } +} + +pub fn tan<'src>(x: &Value) -> Value<'src> { + match x { + Value::Number(x) => Value::Number(x.tan()), + _ => unreachable!("internal Ludus error"), + } +} + +pub fn gt<'src>(x: &Value, y: &Value) -> Value<'src> { + match (x, y) { + (Value::Number(x), Value::Number(y)) => Value::Boolean(x > y), + _ => unreachable!("internal Ludus error"), + } +} + +pub fn gte<'src>(x: &Value, y: &Value) -> Value<'src> { + match (x, y) { + (Value::Number(x), Value::Number(y)) => Value::Boolean(x >= y), + _ => unreachable!("internal Ludus error"), + } +} + +pub fn lt<'src>(x: &Value, y: &Value) -> Value<'src> { + match (x, y) { + (Value::Number(x), Value::Number(y)) => Value::Boolean(x < y), + _ => unreachable!("internal Ludus error"), + } +} + +pub fn lte<'src>(x: &Value, y: &Value) -> Value<'src> { + match (x, y) { + (Value::Number(x), Value::Number(y)) => Value::Boolean(x <= y), + _ => unreachable!("internal Ludus error"), + } +} + +pub fn r#mod<'src>(x: &Value, y: &Value) -> Value<'src> { + match (x, y) { + (Value::Number(x), Value::Number(y)) => Value::Number(x % y), + _ => unreachable!("internal Ludus error"), + } +} + pub fn base<'src>() -> Vec<(&'src str, Value<'src>)> { vec![ - ("eq?", Value::Base(Base::Binary(eq))), ("add", Value::Base(Base::Binary(add))), - ("sub", Value::Base(Base::Binary(sub))), - ("unbox", Value::Base(Base::Unary(unbox))), - ("store!", Value::Base(Base::Binary(store))), - ("doc!", Value::Base(Base::Unary(doc))), ("and", Value::Base(Base::Binary(and))), + ("append", Value::Base(Base::Binary(append))), ("assoc", Value::Base(Base::Ternary(assoc))), + ("at", Value::Base(Base::Binary(at))), + ("atan_2", Value::Base(Base::Binary(atan_2))), ("bool", Value::Base(Base::Unary(r#bool))), + ("ceil", Value::Base(Base::Unary(ceil))), ("chars", Value::Base(Base::Unary(chars))), ("concat", Value::Base(Base::Binary(concat))), - ("append", Value::Base(Base::Binary(append))), - ("dec", Value::Base(Base::Unary(dec))), - ("inc", Value::Base(Base::Unary(inc))), - ("div", Value::Base(Base::Binary(div))), - ("mult", Value::Base(Base::Binary(mult))), - ("dissoc", Value::Base(Base::Binary(dissoc))), - ("first", Value::Base(Base::Unary(first))), - ("at", Value::Base(Base::Binary(at))), - ("get", Value::Base(Base::Binary(get))), - ("last", Value::Base(Base::Unary(last))), - ("or", Value::Base(Base::Binary(or))), - ("print!", Value::Base(Base::Unary(print))), - ("show", Value::Base(Base::Unary(show))), - ("rest", Value::Base(Base::Unary(rest))), + ("cos", Value::Base(Base::Unary(cos))), ("count", Value::Base(Base::Unary(count))), - ("range", Value::Base(Base::Binary(range))), - ("slice", Value::Base(Base::Ternary(slice))), + ("dec", Value::Base(Base::Unary(dec))), + ("dissoc", Value::Base(Base::Binary(dissoc))), + ("div", Value::Base(Base::Binary(div))), + ("doc!", Value::Base(Base::Unary(doc))), + ("downcase", Value::Base(Base::Unary(downcase))), + ("eq?", Value::Base(Base::Binary(eq))), + ("first", Value::Base(Base::Unary(first))), + ("floor", Value::Base(Base::Unary(floor))), + ("get", Value::Base(Base::Binary(get))), + ("gt?", Value::Base(Base::Binary(gt))), + ("gte?", Value::Base(Base::Binary(gte))), + ("inc", Value::Base(Base::Unary(inc))), + ("last", Value::Base(Base::Unary(last))), ("list", Value::Base(Base::Unary(list))), + ("lt?", Value::Base(Base::Binary(lt))), + ("lte?", Value::Base(Base::Binary(lte))), + ("mod", Value::Base(Base::Binary(r#mod))), + ("mult", Value::Base(Base::Binary(mult))), ("number", Value::Base(Base::Unary(number))), + ("or", Value::Base(Base::Binary(or))), + ("pi", Value::Number(std::f64::consts::PI)), + ("print!", Value::Base(Base::Unary(print))), + ("random", Value::Base(Base::Nullary(random))), + ("range", Value::Base(Base::Binary(range))), + ("rest", Value::Base(Base::Unary(rest))), + ("round", Value::Base(Base::Unary(round))), + ("show", Value::Base(Base::Unary(show))), + ("sin", Value::Base(Base::Unary(sin))), + ("slice", Value::Base(Base::Ternary(slice))), + ("split", Value::Base(Base::Binary(split))), + ("sqrt", Value::Base(Base::Unary(sqrt))), + ("sqrt_2", Value::Number(std::f64::consts::SQRT_2)), + ("store!", Value::Base(Base::Binary(store))), + ("sub", Value::Base(Base::Binary(sub))), + ("tan", Value::Base(Base::Unary(tan))), + ("trim", Value::Base(Base::Unary(trim))), + ("triml", Value::Base(Base::Unary(triml))), + ("trimr", Value::Base(Base::Unary(trimr))), ("type", Value::Base(Base::Unary(r#type))), + ("unbox", Value::Base(Base::Unary(unbox))), + ("upcase", Value::Base(Base::Unary(upcase))), ] } -// * [ ] atan_2 (x) -> number -// * [ ] ceil (x) -> number -// * [ ] cos (x) -> number -// * [x] count (x) -> number -// * [~] disj (x) -> set -// * [ ] downcase (x) -> string -// * [ ] floor (x) -> number -// * [ ] gt (x, y) -> bool -// * [ ] gte! (x, y) -> bool -// * [ ] lt (x) -> bool -// * [ ] lte (x) -> bool -// * [ ] mod (x, y) -> number -// * [ ] pi -// * [ ] random () -> number -// * [x] range () -> list -// * [ ] round (x) -> number -// * [ ] sin (x) -> number -// * [x] slice (x, y, z) -> list -// * [ ] split (x, y) -> list(string) -// * [ ] sqrt (x) -> number -// * [x] ~str_slice (x, y, z) -> string~ -// * [x] ~stringify (x) -> string~ -// * [ ] tan (x) -> number -// * [x] to_list (x) -> list -// * [ ] to_number (x) -> number -// * [ ] trim (x) -> string -// * [ ] triml (x) -> string -// * [ ] trimr (x) -> string -// * [ ] type (x) -> keyword -// * [ ] upcase (x) -> string +// * [x] atan_2 (x) -> number +// * [x] ceil (x) -> number +// * [x] cos (x) -> number +// * [x] floor (x) -> number +// * [x] gt (x, y) -> bool +// * [x] gte! (x, y) -> bool +// * [x] lt (x) -> bool +// * [x] lte (x) -> bool +// * [x] mod (x, y) -> number +// * [x] pi +// * [x] random () -> number +// * [x] round (x) -> number +// * [x] sin (x) -> number +// * [x] sqrt (x) -> number +// * [x] tan (x) -> number // * [x] add (x, y) -> number // * [x] and (x, y) -> value // * [x] append (x, y) -> list @@ -457,10 +649,12 @@ pub fn base<'src>() -> Vec<(&'src str, Value<'src>)> { // * [x] bool (x) -> bool // * [x] chars (x) -> list // * [x] concat (x, y) -> value +// * [x] count (x) -> number // * [x] dec (x) -> number // * [x] dissoc (x, y) -> dict // * [x] div (x, y) -> number // * [x] doc (x) -> string +// * [x] downcase (x) -> string // * [x] eq (x, y) -> bool // * [x] first (x) -> value // * [x] get (x, y) -> value @@ -468,10 +662,23 @@ pub fn base<'src>() -> Vec<(&'src str, Value<'src>)> { // * [x] last (x) -> value // * [x] or (x, y) -> value // * [x] print! (x) -> :ok +// * [x] range () -> list // * [x] rest (x) -> coll // * [x] show (x) -> string +// * [x] slice (x, y, z) -> list +// * [x] split (x, y) -> list(string) // * [x] store! (x, y) -> value // * [x] sub (x, y) -> number +// * [x] to_list (x) -> list +// * [x] to_number (x) -> number +// * [x] trim (x) -> string +// * [x] triml (x) -> string +// * [x] trimr (x) -> string +// * [x] type (x) -> keyword // * [x] unbox (x) -> value +// * [x] upcase (x) -> string // * [x] ~prn (x) -> value~ // * [x] ~push (x) -> list~ +// * [x] ~str_slice (x, y, z) -> string~ +// * [x] ~stringify (x) -> string~ +// * [~] disj (x) -> set diff --git a/src/main.rs b/src/main.rs index f801a75..ab7f805 100644 --- a/src/main.rs +++ b/src/main.rs @@ -58,8 +58,8 @@ use crate::base::*; pub fn main() { let src = " -let foo = 42 -type (\"{foo}\") +let foo = \" fOobArbaz \" +trimr (foo) "; let (tokens, lex_errs) = lexer().parse(src).into_output_errors(); if !lex_errs.is_empty() { diff --git a/src/vm.rs b/src/vm.rs index 48f92aa..e53fc13 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -242,6 +242,15 @@ pub fn apply<'src>( msg: "you may only call a function".to_string(), }), (Value::Base(f), Value::Tuple(args)) => match f { + Base::Nullary(f) => { + if args.len() != 0 { + Err(LudusError { + msg: "wrong arity: expected 0 arguments".to_string(), + }) + } else { + Ok(f()) + } + } Base::Unary(f) => { if args.len() != 1 { Err(LudusError {