From af14eaee7256f7aab06cc2b4d6732de5351746b4 Mon Sep 17 00:00:00 2001 From: Scott Richmond Date: Wed, 4 Dec 2024 20:19:41 -0500 Subject: [PATCH] add some functions, base fns return values, not results --- src/base.rs | 262 ++++++++++++++++++++++++++++----------------------- src/main.rs | 5 +- src/value.rs | 9 +- src/vm.rs | 27 +++--- 4 files changed, 161 insertions(+), 142 deletions(-) diff --git a/src/base.rs b/src/base.rs index a594f15..44dcdd3 100644 --- a/src/base.rs +++ b/src/base.rs @@ -1,163 +1,189 @@ -// Solution to the #[derive(Clone)] problem: https://stackoverflow.com/questions/27883509/can-you-clone-a-closure use crate::value::*; use crate::vm::*; use std::rc::Rc; // use imbl::*; // use std::fmt; -type LResult<'src> = Result, LudusError>; - #[derive(Clone, Debug)] pub enum Base<'src> { - Unary(&'src str, fn(&Value<'src>) -> LResult<'src>), - Binary(&'src str, fn(&Value<'src>, &Value<'src>) -> LResult<'src>), - // Ternary( - // &'src str, - // fn(&Value<'src>, &Value<'src>, &Value<'src>) -> LResult<'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>), } -// impl<'src> Base<'src> { -// pub fn name(&self) -> &'src str { -// match self { -// Base::Unary(name, _) => name, -// Base::Binary(name, _) => name, -// Base::Ternary(name, _) => name, -// } -// } -// } - -pub fn eq<'src>(x: &Value<'src>, y: &Value<'src>) -> LResult<'src> { - Ok(Value::Boolean(x == y)) +pub fn eq<'src>(x: &Value<'src>, y: &Value<'src>) -> Value<'src> { + Value::Boolean(x == y) } -pub fn add<'src>(x: &Value<'src>, y: &Value<'src>) -> LResult<'src> { +pub fn add<'src>(x: &Value<'src>, y: &Value<'src>) -> Value<'src> { match (x, y) { - (Value::Number(x), Value::Number(y)) => Ok(Value::Number(x + y)), - _ => Err(LudusError { - msg: "add takes two numbers".to_string(), - }), + (Value::Number(x), Value::Number(y)) => Value::Number(x + y), + _ => unreachable!("internal Ludus error"), } } -pub fn sub<'src>(x: &Value<'src>, y: &Value<'src>) -> LResult<'src> { +pub fn sub<'src>(x: &Value<'src>, y: &Value<'src>) -> Value<'src> { match (x, y) { - (Value::Number(x), Value::Number(y)) => Ok(Value::Number(x - y)), - _ => Err(LudusError { - msg: "sub takes two numbers".to_string(), - }), + (Value::Number(x), Value::Number(y)) => Value::Number(x - y), + _ => unreachable!("internal Ludus error"), } } -pub fn unbox<'src>(x: &Value<'src>) -> LResult<'src> { +pub fn unbox<'src>(x: &Value<'src>) -> Value<'src> { match x { - Value::Box(_, cell) => Ok(cell.borrow().clone()), - _ => Err(LudusError { - msg: "only boxes may be unboxed".to_string(), - }), + Value::Box(_, cell) => cell.borrow().clone(), + _ => unreachable!("internal Ludus error"), } } -pub fn store<'src>(b: &Value<'src>, val: &Value<'src>) -> LResult<'src> { +pub fn store<'src>(b: &Value<'src>, val: &Value<'src>) -> Value<'src> { if let Value::Box(_, cell) = b { cell.replace(val.clone()); - Ok(val.clone()) + val.clone() } else { - Err(LudusError { - msg: "only boxes may store values".to_string(), - }) + unreachable!("internal Ludus error") } } // TODO: do better than returning just the docstr // name, patterns, AND docstring -pub fn doc<'src>(f: &Value<'src>) -> LResult<'src> { +pub fn doc<'src>(f: &Value<'src>) -> Value<'src> { match f { Value::Fn(f) => { let Fn { name, doc, .. } = **f; if let Some(docstr) = doc { - Ok(Value::AllocatedString(Rc::new(format!("{name}: {docstr}")))) + Value::AllocatedString(Rc::new(format!("{name}: {docstr}"))) } else { - Ok(Value::InternedString( - doc.unwrap_or("no documentation found"), - )) + Value::InternedString(doc.unwrap_or("no documentation found")) } } - _ => Ok(Value::InternedString("no documentation found")), + _ => Value::InternedString("no documentation found"), + } +} + +pub fn and<'src>(x: &Value<'src>, y: &Value<'src>) -> Value<'src> { + Value::Boolean(x.bool() && y.bool()) +} + +pub fn assoc<'src>(dict: &Value<'src>, key: &Value<'src>, value: &Value<'src>) -> Value<'src> { + match (dict, key) { + (Value::Dict(d), Value::Keyword(k)) => Value::Dict(d.update(k, value.clone())), + _ => unreachable!("internal Ludus error"), + } +} + +pub fn r#bool<'src>(x: &Value<'src>) -> Value<'src> { + Value::Boolean(x.bool()) +} + +pub fn chars<'src>(x: &Value<'src>) -> Value<'src> { + match x { + Value::InternedString(s) => { + let chars = s.chars(); + + let mut charlist = imbl::vector![]; + for char in chars { + if char.is_ascii() { + charlist.push_back(Value::AllocatedString(Rc::new(char.to_string()))) + } else { + return Value::Tuple(Rc::new(vec![ + Value::Keyword("err"), + Value::AllocatedString(Rc::new(format!( + "{char} is not an ascii character" + ))), + ])); + } + } + Value::Tuple(Rc::new(vec![Value::Keyword("ok"), Value::List(charlist)])) + } + Value::AllocatedString(s) => { + let chars = s.chars(); + + let mut charlist = imbl::vector![]; + for char in chars { + if char.is_ascii() { + charlist.push_back(Value::AllocatedString(Rc::new(char.to_string()))) + } else { + return Value::Tuple(Rc::new(vec![ + Value::Keyword("err"), + Value::AllocatedString(Rc::new(format!( + "{char} is not an ascii character" + ))), + ])); + } + } + Value::Tuple(Rc::new(vec![Value::Keyword("ok"), Value::List(charlist)])) + } + _ => unreachable!("internal Ludus error"), } } pub fn base<'src>() -> Vec<(&'src str, Value<'src>)> { - let mut base = vec![]; - - let eq = Base::Binary("eq", eq); - base.push(("eq", Value::Base(eq))); - - 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.push(("doc", Value::Base(Base::Unary("doc", doc)))); - - base + 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))), + ("assoc", Value::Base(Base::Ternary(assoc))), + ("bool", Value::Base(Base::Unary(r#bool))), + ("chars", Value::Base(Base::Unary(chars))), + ] } -// DONE add (x, y) -> number -// and (x, y) -> value -// assoc (x, y) -> dict -// atan_2 (x) -> number -// bool (x) -> bool -// ceil (x) -> number -// chars (x) -> list -// concat (x, y) -> value -// conj (x, y) -> list -// cos (x) -> number -// dec (x) -> number -// disj (x) -> set -// dissoc (x, y) -> dict -// 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 -// gt (x, y) -> bool -// gte! (x, y) -> bool -// inc (x) -> number -// last (x) -> value -// lt (x) -> bool -// lte (x) -> bool -// mod (x, y) -> number -// or (x, y) -> value -// pi -// print! (x) -> :ok -// prn (x) -> value -// push (x) -> list -// random () -> number -// range () -> list -// rest (x) -> coll -// round (x) -> number -// show (x) -> string -// sin (x) -> number -// slice (x, y, z) -> list -// split (x, y) -> list(string) -// sqrt (x) -> number -// DONE store! (x, y) -> value -// str_slice (x, y, z) -> string -// stringify (x) -> string -// sub (x, y) -> number -// tan (x) -> number -// to_list (x) -> list -// to_number (x) -> number -// trim (x) -> string -// triml (x) -> string -// trimr (x) -> string -// type (x) -> keyword -// DONE unbox (x) -> value -// upcase (x) -> string +// * [x] and (x, y) -> value +// * [x] assoc (x, y) -> dict +// * [ ] atan_2 (x) -> number +// * [x] bool (x) -> bool +// * [ ] ceil (x) -> number +// * [ ] chars (x) -> list +// * [ ] concat (x, y) -> value +// * [ ] conj (x, y) -> list +// * [ ] cos (x) -> number +// * [ ] dec (x) -> number +// * [ ] disj (x) -> set +// * [ ] dissoc (x, y) -> dict +// * [ ] div (x, y) -> number +// * [ ] doc (x) -> string +// * [ ] downcase (x) -> string +// * [ ] first (x) -> value +// * [ ] floor (x) -> number +// * [ ] get (x, y) -> value +// * [ ] gt (x, y) -> bool +// * [ ] gte! (x, y) -> bool +// * [ ] inc (x) -> number +// * [ ] last (x) -> value +// * [ ] lt (x) -> bool +// * [ ] lte (x) -> bool +// * [ ] mod (x, y) -> number +// * [ ] or (x, y) -> value +// * [ ] pi +// * [ ] print! (x) -> :ok +// * [ ] prn (x) -> value +// * [ ] push (x) -> list +// * [ ] random () -> number +// * [ ] range () -> list +// * [ ] rest (x) -> coll +// * [ ] round (x) -> number +// * [ ] show (x) -> string +// * [ ] sin (x) -> number +// * [ ] slice (x, y, z) -> list +// * [ ] split (x, y) -> list(string) +// * [ ] sqrt (x) -> number +// * [ ] str_slice (x, y, z) -> string +// * [ ] stringify (x) -> string +// * [ ] sub (x, y) -> number +// * [ ] tan (x) -> number +// * [ ] to_list (x) -> list +// * [ ] to_number (x) -> number +// * [ ] trim (x) -> string +// * [ ] triml (x) -> string +// * [ ] trimr (x) -> string +// * [ ] type (x) -> keyword +// * [ ] upcase (x) -> string +// * [x] add (x, y) -> number +// * [x] eq (x, y) -> bool +// * [x] store! (x, y) -> value +// * [x] unbox (x) -> value diff --git a/src/main.rs b/src/main.rs index e482046..527ce17 100644 --- a/src/main.rs +++ b/src/main.rs @@ -29,7 +29,7 @@ // * [x] splats in list and dict literals // * [x] `loop` and `recur` // * [ ] string patterns -// * [ ] string interpolation +// * [x] string interpolation // * [x] docstrings // * [~] write `base` in Rust // * [ ] turn this into a library function @@ -58,8 +58,7 @@ use crate::base::*; pub fn main() { let src = " -let foo = \"foo\" -eq (\"foo\", \"{foo}\") +chars (\"féo\") "; let (tokens, lex_errs) = lexer().parse(src).into_output_errors(); if !lex_errs.is_empty() { diff --git a/src/value.rs b/src/value.rs index f434639..124d60c 100644 --- a/src/value.rs +++ b/src/value.rs @@ -113,14 +113,7 @@ impl fmt::Display for Value<'_> { ) } Value::Placeholder => write!(f, "_"), - Value::Base(base) => { - let name = match base { - Base::Unary(name, _) => name, - Base::Binary(name, _) => name, - // Base::Ternary(name, _) => name, - }; - write!(f, "base fn {}", name) - } + Value::Base(..) => unreachable!(), Value::Recur(..) => unreachable!(), } } diff --git a/src/vm.rs b/src/vm.rs index 5cff0ce..181b900 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -241,32 +241,33 @@ pub fn apply<'src>( msg: "you may only call a function".to_string(), }), (Value::Base(f), Value::Tuple(args)) => match f { - Base::Unary(_name, f) => { + Base::Unary(f) => { if args.len() != 1 { Err(LudusError { msg: "wrong arity: expected 1 argument".to_string(), }) } else { - f(&args[0]) + Ok(f(&args[0])) } } - Base::Binary(_name, r#fn) => { + Base::Binary(r#fn) => { if args.len() != 2 { Err(LudusError { msg: "wrong arity: expected 2 arguments".to_string(), }) } else { - r#fn(&args[0], &args[1]) + Ok(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]) - // } - // } + } + Base::Ternary(f) => { + if args.len() != 3 { + Err(LudusError { + msg: "wrong arity: expected 3 arguments".to_string(), + }) + } else { + Ok(f(&args[0], &args[1], &args[2])) + } + } }, _ => unreachable!(), }