use std::rc::Rc;
use super::super::{Error, Func, Num, Proc};
use super::Primitive::{self, Number};
use super::SExp::{self, Atom};
pub fn make_unary_numeric<T>(f: impl Fn(Num) -> T + 'static, name: Option<&str>) -> SExp
where
T: Into<SExp>,
{
SExp::from(Proc::new(
Func::Pure(Rc::new(move |e| {
let n = e.car()?;
if let SExp::Atom(Primitive::Number(n)) = n {
Ok((f(n)).into())
} else {
Err(Error::Type {
expected: "number",
given: n.type_of().to_string(),
})
}
})),
1,
name,
))
}
pub fn make_binary_numeric<T>(f: impl Fn(Num, Num) -> T + 'static, name: Option<&str>) -> SExp
where
T: Into<SExp>,
{
SExp::from(Proc::new(
Func::Pure(Rc::new(move |expr| {
let (arg0, tail) = expr.split_car()?;
let arg1 = tail.car()?;
match (arg0, arg1) {
(Atom(Number(n0)), Atom(Number(n1))) => Ok((f(n0, n1)).into()),
(Atom(Number(_)), e) | (e, _) => Err(Error::Type {
expected: "number",
given: e.type_of().to_string(),
}),
}
})),
2,
name,
))
}
pub fn make_fold_numeric<F, T>(init: T, f: F, name: Option<&str>) -> SExp
where
F: Fn(T, Num) -> T + 'static,
T: Into<SExp> + Clone + 'static,
{
SExp::from(Proc::new(
Func::Pure(Rc::new(move |exp: SExp| {
match exp.into_iter().fold(Ok(init.to_owned()), |a, e| {
if let Ok(val) = a {
if let SExp::Atom(Primitive::Number(n)) = e {
Ok(f(val, n))
} else {
Err(Error::Type {
expected: "number",
given: e.type_of().to_string(),
})
}
} else {
a
}
}) {
Ok(v) => Ok(v.into()),
Err(err) => Err(err),
}
})),
(0,),
name,
))
}
pub fn make_fold_from0_numeric<F>(f: F, name: Option<&str>) -> SExp
where
F: Fn(Num, Num) -> Num + 'static,
{
SExp::from(Proc::new(
Func::Pure(Rc::new(move |exp: SExp| {
let mut i = exp.into_iter();
match i.next() {
Some(SExp::Atom(Primitive::Number(first))) => {
match i.fold(Ok(first), |a, e| {
if let Ok(val) = a {
if let SExp::Atom(Primitive::Number(n)) = e {
Ok(f(val, n))
} else {
Err(Error::Type {
expected: "number",
given: e.type_of().to_string(),
})
}
} else {
a
}
}) {
Ok(v) => Ok(v.into()),
Err(err) => Err(err),
}
}
Some(other) => Err(Error::Type {
expected: "number",
given: other.type_of().to_string(),
}),
None => Err(Error::ArityMin {
expected: 1,
given: 0,
}),
}
})),
(1,),
name,
))
}
pub fn make_unary_expr<F>(f: F, name: Option<&str>) -> SExp
where
F: Fn(SExp) -> crate::Result + 'static,
{
SExp::from(Proc::new(
Func::Pure(Rc::new(move |exp| f(exp.car()?))),
1,
name,
))
}
pub fn make_binary_expr<F>(f: F, name: Option<&str>) -> SExp
where
F: Fn(SExp, SExp) -> crate::Result + 'static,
{
SExp::from(Proc::new(
Func::Pure(Rc::new(move |exp| {
let (arg0, tail) = exp.split_car()?;
f(arg0, tail.car()?)
})),
2,
name,
))
}
pub fn make_ternary_expr<F>(f: F, name: Option<&str>) -> SExp
where
F: Fn(SExp, SExp, SExp) -> crate::Result + 'static,
{
SExp::from(Proc::new(
Func::Pure(Rc::new(move |exp| {
let (arg0, tail) = exp.split_car()?;
let (arg1, tail) = tail.split_car()?;
f(arg0, arg1, tail.car()?)
})),
3,
name,
))
}