add some functions, base fns return values, not results

This commit is contained in:
Scott Richmond 2024-12-04 20:19:41 -05:00
parent c3408a56c1
commit af14eaee72
4 changed files with 161 additions and 142 deletions

View File

@ -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::value::*;
use crate::vm::*; use crate::vm::*;
use std::rc::Rc; use std::rc::Rc;
// use imbl::*; // use imbl::*;
// use std::fmt; // use std::fmt;
type LResult<'src> = Result<Value<'src>, LudusError>;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum Base<'src> { pub enum Base<'src> {
Unary(&'src str, fn(&Value<'src>) -> LResult<'src>), Unary(fn(&Value<'src>) -> Value<'src>),
Binary(&'src str, fn(&Value<'src>, &Value<'src>) -> LResult<'src>), Binary(fn(&Value<'src>, &Value<'src>) -> Value<'src>),
// Ternary( Ternary(fn(&Value<'src>, &Value<'src>, &Value<'src>) -> Value<'src>),
// &'src str,
// fn(&Value<'src>, &Value<'src>, &Value<'src>) -> LResult<'src>,
// ),
} }
// impl<'src> Base<'src> { pub fn eq<'src>(x: &Value<'src>, y: &Value<'src>) -> Value<'src> {
// pub fn name(&self) -> &'src str { Value::Boolean(x == y)
// 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 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) { match (x, y) {
(Value::Number(x), Value::Number(y)) => Ok(Value::Number(x + y)), (Value::Number(x), Value::Number(y)) => Value::Number(x + y),
_ => Err(LudusError { _ => unreachable!("internal Ludus error"),
msg: "add takes two numbers".to_string(),
}),
} }
} }
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) { match (x, y) {
(Value::Number(x), Value::Number(y)) => Ok(Value::Number(x - y)), (Value::Number(x), Value::Number(y)) => Value::Number(x - y),
_ => Err(LudusError { _ => unreachable!("internal Ludus error"),
msg: "sub takes two numbers".to_string(),
}),
} }
} }
pub fn unbox<'src>(x: &Value<'src>) -> LResult<'src> { pub fn unbox<'src>(x: &Value<'src>) -> Value<'src> {
match x { match x {
Value::Box(_, cell) => Ok(cell.borrow().clone()), Value::Box(_, cell) => cell.borrow().clone(),
_ => Err(LudusError { _ => unreachable!("internal Ludus error"),
msg: "only boxes may be unboxed".to_string(),
}),
} }
} }
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 { if let Value::Box(_, cell) = b {
cell.replace(val.clone()); cell.replace(val.clone());
Ok(val.clone()) val.clone()
} else { } else {
Err(LudusError { unreachable!("internal Ludus error")
msg: "only boxes may store values".to_string(),
})
} }
} }
// TODO: do better than returning just the docstr // TODO: do better than returning just the docstr
// name, patterns, AND docstring // name, patterns, AND docstring
pub fn doc<'src>(f: &Value<'src>) -> LResult<'src> { pub fn doc<'src>(f: &Value<'src>) -> Value<'src> {
match f { match f {
Value::Fn(f) => { Value::Fn(f) => {
let Fn { name, doc, .. } = **f; let Fn { name, doc, .. } = **f;
if let Some(docstr) = doc { if let Some(docstr) = doc {
Ok(Value::AllocatedString(Rc::new(format!("{name}: {docstr}")))) Value::AllocatedString(Rc::new(format!("{name}: {docstr}")))
} else { } else {
Ok(Value::InternedString( Value::InternedString(doc.unwrap_or("no documentation found"))
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>)> { pub fn base<'src>() -> Vec<(&'src str, Value<'src>)> {
let mut base = vec![]; vec![
("eq?", Value::Base(Base::Binary(eq))),
let eq = Base::Binary("eq", eq); ("add", Value::Base(Base::Binary(add))),
base.push(("eq", Value::Base(eq))); ("sub", Value::Base(Base::Binary(sub))),
("unbox", Value::Base(Base::Unary(unbox))),
let add = Base::Binary("add", add); ("store!", Value::Base(Base::Binary(store))),
base.push(("add", Value::Base(add))); ("doc!", Value::Base(Base::Unary(doc))),
("and", Value::Base(Base::Binary(and))),
base.push(("sub", Value::Base(Base::Binary("sub", sub)))); ("assoc", Value::Base(Base::Ternary(assoc))),
("bool", Value::Base(Base::Unary(r#bool))),
base.push(("unbox", Value::Base(Base::Unary("unbox", unbox)))); ("chars", Value::Base(Base::Unary(chars))),
]
base.push(("store", Value::Base(Base::Binary("store", store))));
base.push(("doc", Value::Base(Base::Unary("doc", doc))));
base
} }
// DONE add (x, y) -> number // * [x] and (x, y) -> value
// and (x, y) -> value // * [x] assoc (x, y) -> dict
// assoc (x, y) -> dict // * [ ] atan_2 (x) -> number
// atan_2 (x) -> number // * [x] bool (x) -> bool
// bool (x) -> bool // * [ ] ceil (x) -> number
// ceil (x) -> number // * [ ] chars (x) -> list
// chars (x) -> list // * [ ] concat (x, y) -> value
// concat (x, y) -> value // * [ ] conj (x, y) -> list
// conj (x, y) -> list // * [ ] cos (x) -> number
// cos (x) -> number // * [ ] dec (x) -> number
// dec (x) -> number // * [ ] disj (x) -> set
// disj (x) -> set // * [ ] dissoc (x, y) -> dict
// dissoc (x, y) -> dict // * [ ] div (x, y) -> number
// div (x, y) -> number // * [ ] doc (x) -> string
// doc (x) -> string // * [ ] downcase (x) -> string
// downcase (x) -> string // * [ ] first (x) -> value
// DONE eq (x, y) -> bool // * [ ] floor (x) -> number
// first (x) -> value // * [ ] get (x, y) -> value
// floor (x) -> number // * [ ] gt (x, y) -> bool
// get (x, y) -> value // * [ ] gte! (x, y) -> bool
// gt (x, y) -> bool // * [ ] inc (x) -> number
// gte! (x, y) -> bool // * [ ] last (x) -> value
// inc (x) -> number // * [ ] lt (x) -> bool
// last (x) -> value // * [ ] lte (x) -> bool
// lt (x) -> bool // * [ ] mod (x, y) -> number
// lte (x) -> bool // * [ ] or (x, y) -> value
// mod (x, y) -> number // * [ ] pi
// or (x, y) -> value // * [ ] print! (x) -> :ok
// pi // * [ ] prn (x) -> value
// print! (x) -> :ok // * [ ] push (x) -> list
// prn (x) -> value // * [ ] random () -> number
// push (x) -> list // * [ ] range () -> list
// random () -> number // * [ ] rest (x) -> coll
// range () -> list // * [ ] round (x) -> number
// rest (x) -> coll // * [ ] show (x) -> string
// round (x) -> number // * [ ] sin (x) -> number
// show (x) -> string // * [ ] slice (x, y, z) -> list
// sin (x) -> number // * [ ] split (x, y) -> list(string)
// slice (x, y, z) -> list // * [ ] sqrt (x) -> number
// split (x, y) -> list(string) // * [ ] str_slice (x, y, z) -> string
// sqrt (x) -> number // * [ ] stringify (x) -> string
// DONE store! (x, y) -> value // * [ ] sub (x, y) -> number
// str_slice (x, y, z) -> string // * [ ] tan (x) -> number
// stringify (x) -> string // * [ ] to_list (x) -> list
// sub (x, y) -> number // * [ ] to_number (x) -> number
// tan (x) -> number // * [ ] trim (x) -> string
// to_list (x) -> list // * [ ] triml (x) -> string
// to_number (x) -> number // * [ ] trimr (x) -> string
// trim (x) -> string // * [ ] type (x) -> keyword
// triml (x) -> string // * [ ] upcase (x) -> string
// trimr (x) -> string // * [x] add (x, y) -> number
// type (x) -> keyword // * [x] eq (x, y) -> bool
// DONE unbox (x) -> value // * [x] store! (x, y) -> value
// upcase (x) -> string // * [x] unbox (x) -> value

View File

@ -29,7 +29,7 @@
// * [x] splats in list and dict literals // * [x] splats in list and dict literals
// * [x] `loop` and `recur` // * [x] `loop` and `recur`
// * [ ] string patterns // * [ ] string patterns
// * [ ] string interpolation // * [x] string interpolation
// * [x] docstrings // * [x] docstrings
// * [~] write `base` in Rust // * [~] write `base` in Rust
// * [ ] turn this into a library function // * [ ] turn this into a library function
@ -58,8 +58,7 @@ use crate::base::*;
pub fn main() { pub fn main() {
let src = " let src = "
let foo = \"foo\" chars (\"féo\")
eq (\"foo\", \"{foo}\")
"; ";
let (tokens, lex_errs) = lexer().parse(src).into_output_errors(); let (tokens, lex_errs) = lexer().parse(src).into_output_errors();
if !lex_errs.is_empty() { if !lex_errs.is_empty() {

View File

@ -113,14 +113,7 @@ impl fmt::Display for Value<'_> {
) )
} }
Value::Placeholder => write!(f, "_"), Value::Placeholder => write!(f, "_"),
Value::Base(base) => { Value::Base(..) => unreachable!(),
let name = match base {
Base::Unary(name, _) => name,
Base::Binary(name, _) => name,
// Base::Ternary(name, _) => name,
};
write!(f, "base fn {}", name)
}
Value::Recur(..) => unreachable!(), Value::Recur(..) => unreachable!(),
} }
} }

View File

@ -241,32 +241,33 @@ pub fn apply<'src>(
msg: "you may only call a function".to_string(), msg: "you may only call a function".to_string(),
}), }),
(Value::Base(f), Value::Tuple(args)) => match f { (Value::Base(f), Value::Tuple(args)) => match f {
Base::Unary(_name, f) => { Base::Unary(f) => {
if args.len() != 1 { if args.len() != 1 {
Err(LudusError { Err(LudusError {
msg: "wrong arity: expected 1 argument".to_string(), msg: "wrong arity: expected 1 argument".to_string(),
}) })
} else { } else {
f(&args[0]) Ok(f(&args[0]))
} }
} }
Base::Binary(_name, r#fn) => { Base::Binary(r#fn) => {
if args.len() != 2 { if args.len() != 2 {
Err(LudusError { Err(LudusError {
msg: "wrong arity: expected 2 arguments".to_string(), msg: "wrong arity: expected 2 arguments".to_string(),
}) })
} else { } else {
r#fn(&args[0], &args[1]) Ok(r#fn(&args[0], &args[1]))
}
}
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]))
}
} }
} // 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!(), _ => unreachable!(),
} }