add some functions, base fns return values, not results
This commit is contained in:
parent
c3408a56c1
commit
af14eaee72
262
src/base.rs
262
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::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
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
27
src/vm.rs
27
src/vm.rs
|
@ -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!(),
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user