use crate::{
Environment,
FALSE_SYMBOL,
TRUE_SYMBOL,
builtin::eval_builtin,
eval::apply::eval_apply,
is_number_eq,
read_from_environment,
value::Value,
};
pub(crate) fn is_member(args: &[Value], env: Environment) -> Result<Value, std::sync::Arc<str>> {
if let [lst, e] = args {
match (eval_apply(lst, env.clone())?, eval_apply(e, env)?) {
(Value::List(elements), element) => Ok(if elements.contains(&element) {
TRUE_SYMBOL.clone()
} else {
FALSE_SYMBOL.clone()
}),
(e, _) => Err(std::sync::Arc::from(format!(
concat!("Error[ksl::builtin::IsMember]: ", "Unexpected value: `{}`."),
e
))),
}
} else {
Err(std::sync::Arc::from(format!(
concat!(
"Error[ksl::builtin::IsMember]: ",
"Expected 2 parameters, but {} were passed."
),
args.len()
)))
}
}
pub(crate) fn slide_by(args: &[Value], env: Environment) -> Result<Value, std::sync::Arc<str>> {
if let [lst, n] = args {
match (eval_apply(lst, env.clone())?, eval_apply(n, env)?) {
(Value::List(elements), Value::Number(size)) => {
let size = size.trunc() as usize;
if elements.len() < size {
Ok(Value::List(std::sync::Arc::new([])))
} else {
Ok(Value::List(
(0..=(elements.len() - size))
.map(|idx| Value::List(std::sync::Arc::from(&elements[idx..(idx + size)])))
.collect::<std::sync::Arc<[Value]>>(),
))
}
}
(Value::List(_), e) | (e, _) => Err(std::sync::Arc::from(format!(
concat!("Error[ksl::builtin::SlideBy]: ", "Unexpected value: `{}`."),
e
))),
}
} else {
Err(std::sync::Arc::from(format!(
concat!(
"Error[ksl::builtin::SlideBy]: ",
"Expected 2 parameters, but {} were passed."
),
args.len()
)))
}
}
pub(crate) fn cond(args: &[Value], env: Environment) -> Result<Value, std::sync::Arc<str>> {
if let [cases, default] = args {
let cases_list = match cases {
Value::Symbol(cases_sym) => {
if let Some(value) = read_from_environment(&env, cases_sym) {
match value.as_ref() {
Value::List(cases_list) => Ok(cases_list.clone()),
e => Err(std::sync::Arc::from(format!(
concat!(
"Error[ksl::builtin::Cond]: ",
"Expected a list, but got: `{}`"
),
e
))),
}
} else {
Err(std::sync::Arc::from(format!(
"Error[ksl::builtin::Cond]: Unknown symbol: {cases_sym}"
)))
}
}
Value::List(cases_list) => Ok(cases_list.clone()),
e => Err(std::sync::Arc::from(format!(
concat!(
"Error[ksl::builtin::Cond]: ",
"Expected a list, but got: `{}`"
),
e
))),
}?;
for case in cases_list.as_ref() {
match case {
Value::List(case_pair) => match case_pair.as_ref() {
[pred, val] => match eval_apply(pred, env.clone())? {
p @ Value::Atom(_) if p == *TRUE_SYMBOL => return eval_apply(val, env.clone()),
p @ Value::Atom(_) if p == *FALSE_SYMBOL => continue,
e => {
return Err(std::sync::Arc::from(format!(
concat!(
"Error[ksl::builtin::Cond]: ",
"Expected a boolean, but got: `{}`"
),
e
)));
}
},
_ => {
return Err(std::sync::Arc::from(format!(
concat!(
"Error[ksl::builtin::Cond]: ",
"Expected a pair, but got: `{}`"
),
case
)));
}
},
e => {
return Err(std::sync::Arc::from(format!(
concat!(
"Error[ksl::builtin::Cond]: ",
"Expected a list, but got: `{}`"
),
e
)));
}
}
}
eval_apply(default, env)
} else {
Err(std::sync::Arc::from(format!(
concat!(
"Error[ksl::builtin::Cond]: ",
"Expected 2 parameters, but {} were passed."
),
args.len()
)))
}
}
pub(crate) fn zip(args: &[Value], env: Environment) -> Result<Value, std::sync::Arc<str>> {
if let [lst1, lst2] = args {
match (eval_apply(lst1, env.clone())?, eval_apply(lst2, env)?) {
(Value::List(fsts), Value::List(snds)) => Ok(Value::List(
fsts.iter()
.zip(snds.iter())
.map(|(fst, snd)| Value::List(std::sync::Arc::new([fst.clone(), snd.clone()])))
.collect::<std::sync::Arc<[Value]>>(),
)),
(Value::List(_), e) | (e, _) => Err(std::sync::Arc::from(format!(
concat!("Error[ksl::builtin::Zip]: ", "Unexpected value: `{}`."),
e
))),
}
} else {
Err(std::sync::Arc::from(format!(
concat!(
"Error[ksl::builtin::Zip]: ",
"Expected 2 parameters, but {} were passed."
),
args.len()
)))
}
}
pub(crate) fn take(args: &[Value], env: Environment) -> Result<Value, std::sync::Arc<str>> {
if let [lst, n] = args {
match (eval_apply(lst, env.clone())?, eval_apply(n, env)?) {
(Value::List(elements), Value::Number(size)) => {
let size = size.trunc() as usize;
if elements.len() < size {
Ok(Value::List(elements.clone()))
} else {
Ok(Value::List(std::sync::Arc::from(&elements[..size])))
}
}
(Value::List(_), e) | (e, _) => Err(std::sync::Arc::from(format!(
concat!("Error[ksl::builtin::Take]: ", "Unexpected value: `{}`."),
e
))),
}
} else {
Err(std::sync::Arc::from(format!(
concat!(
"Error[ksl::builtin::Take]: ",
"Expected 2 parameters, but {} were passed."
),
args.len()
)))
}
}
pub(crate) fn drop(args: &[Value], env: Environment) -> Result<Value, std::sync::Arc<str>> {
if let [lst, n] = args {
match (eval_apply(lst, env.clone())?, eval_apply(n, env)?) {
(Value::List(elements), Value::Number(size)) if size >= 0.0 => {
let size = size.trunc() as usize;
if elements.len() < size {
Ok(Value::List(std::sync::Arc::new([])))
} else {
Ok(Value::List(std::sync::Arc::from(&elements[size..])))
}
}
(Value::List(_), e) | (e, _) => Err(std::sync::Arc::from(format!(
concat!("Error[ksl::builtin::Drop]: ", "Unexpected value: `{}`."),
e
))),
}
} else {
Err(std::sync::Arc::from(format!(
concat!(
"Error[ksl::builtin::Drop]: ",
"Expected 2 parameters, but {} were passed."
),
args.len()
)))
}
}
pub(crate) fn zip_with(args: &[Value], env: Environment) -> Result<Value, std::sync::Arc<str>> {
if let [lst1, lst2, func] = args {
let func = std::sync::Arc::new(eval_apply(func, env.clone())?);
match (
eval_apply(lst1, env.clone())?,
eval_apply(lst2, env.clone())?,
) {
(Value::List(fsts), Value::List(snds)) => Ok(Value::List(
fsts.iter()
.zip(snds.iter())
.map(|(fst, snd)| match func.as_ref() {
Value::Builtin(name) => eval_builtin(name, &[fst.clone(), snd.clone()], env.clone()),
Value::Lambda(_, _, _) | Value::Plugin(_, _) => eval_apply(
&Value::Apply(vec![fst.clone(), snd.clone()], func.clone()),
env.clone(),
),
e => Err(std::sync::Arc::from(format!(
concat!("Error[ksl::builtin::ZipWith]: ", "Unexpected value: `{}`."),
e
))),
})
.collect::<Result<std::sync::Arc<[Value]>, std::sync::Arc<str>>>()?,
)),
(Value::List(_), e) | (e, _) => Err(std::sync::Arc::from(format!(
concat!("Error[ksl::builtin::ZipWith]: ", "Unexpected value: `{}`."),
e
))),
}
} else {
Err(std::sync::Arc::from(format!(
concat!(
"Error[ksl::builtin::ZipWith]: ",
"Expected 3 parameters, but {} were passed."
),
args.len()
)))
}
}
pub(crate) fn insert(args: &[Value], env: Environment) -> Result<Value, std::sync::Arc<str>> {
if let [lst, n, elem] = args {
match (eval_apply(lst, env.clone())?, eval_apply(n, env.clone())?) {
(Value::List(elements), Value::Number(idx)) => {
let mut new_vec = elements.to_vec();
let element = eval_apply(elem, env)?;
if is_number_eq(idx, -1.0) {
new_vec.push(element);
Ok(Value::List(std::sync::Arc::from(new_vec)))
} else {
let idx = idx.trunc();
if idx < 1.0 || idx as usize > elements.len() + 1 {
Err(std::sync::Arc::from(format!(
concat!(
"Error[ksl::builtin::Insert]: ",
"Index `{}` is out of boundary."
),
idx
)))
} else {
new_vec.insert(idx as usize - 1, element);
Ok(Value::List(std::sync::Arc::from(new_vec)))
}
}
}
(Value::List(_), e) | (e, _) => Err(std::sync::Arc::from(format!(
concat!("Error[ksl::builtin::Insert]: ", "Unexpected value: `{}`."),
e
))),
}
} else {
Err(std::sync::Arc::from(format!(
concat!(
"Error[ksl::builtin::Insert]: ",
"Expected 3 parameters, but {} were passed."
),
args.len()
)))
}
}
pub(crate) fn remove(args: &[Value], env: Environment) -> Result<Value, std::sync::Arc<str>> {
if let [lst, n] = args {
match (eval_apply(lst, env.clone())?, eval_apply(n, env)?) {
(Value::List(elements), Value::Number(idx)) => {
let mut new_vec = elements.to_vec();
if is_number_eq(idx, -1.0) {
let _ = new_vec.pop();
Ok(Value::List(std::sync::Arc::from(new_vec)))
} else {
let idx = idx.trunc();
if idx < 1.0 || idx as usize > elements.len() {
Err(std::sync::Arc::from(format!(
concat!(
"Error[ksl::builtin::Remove]: ",
"Index `{}` is out of boundary."
),
idx
)))
} else {
new_vec.remove(idx as usize - 1);
Ok(Value::List(std::sync::Arc::from(new_vec)))
}
}
}
(Value::List(_), e) | (e, _) => Err(std::sync::Arc::from(format!(
concat!("Error[ksl::builtin::Remove]: ", "Unexpected value: `{}`."),
e
))),
}
} else {
Err(std::sync::Arc::from(format!(
concat!(
"Error[ksl::builtin::Remove]: ",
"Expected 2 parameters, but {} were passed."
),
args.len()
)))
}
}
pub(crate) fn pad_left(args: &[Value], env: Environment) -> Result<Value, std::sync::Arc<str>> {
if let [lst, elem, cnt] = args {
match (
eval_apply(lst, env.clone())?,
eval_apply(elem, env.clone())?,
eval_apply(cnt, env)?,
) {
(Value::List(l), e, Value::Number(n)) => Ok(Value::List(if n > 0.0 {
std::sync::Arc::from({
let mut p = vec![e; n as usize];
p.extend_from_slice(&l);
p
})
} else {
l
})),
(Value::List(_), _, e) => Err(std::sync::Arc::from(format!(
concat!(
"Error[ksl::builtin::PadLeft]: ",
"Expected a number as count, but got: `{}`."
),
e
))),
(e, _, _) => Err(std::sync::Arc::from(format!(
concat!(
"Error[ksl::builtin::PadLeft]: ",
"Expected a list, but got: `{}`."
),
e
))),
}
} else {
Err(std::sync::Arc::from(format!(
concat!(
"Error[ksl::builtin::PadLeft]: ",
"Expected 3 parameters, but {} were passed."
),
args.len()
)))
}
}
pub(crate) fn pad_right(args: &[Value], env: Environment) -> Result<Value, std::sync::Arc<str>> {
if let [lst, elem, cnt] = args {
match (
eval_apply(lst, env.clone())?,
eval_apply(elem, env.clone())?,
eval_apply(cnt, env)?,
) {
(Value::List(l), e, Value::Number(n)) => Ok(Value::List(if n > 0.0 {
std::sync::Arc::from({
let mut p = l.to_vec();
p.resize(l.len() + n as usize, e);
p
})
} else {
l
})),
(Value::List(_), _, e) => Err(std::sync::Arc::from(format!(
concat!(
"Error[ksl::builtin::PadRight]: ",
"Expected a number as count, but got: `{}`."
),
e
))),
(e, _, _) => Err(std::sync::Arc::from(format!(
concat!(
"Error[ksl::builtin::PadRight]: ",
"Expected a list, but got: `{}`."
),
e
))),
}
} else {
Err(std::sync::Arc::from(format!(
concat!(
"Error[ksl::builtin::PadRight]: ",
"Expected 3 parameters, but {} were passed."
),
args.len()
)))
}
}