use crate::{Environment, eval::apply::eval_apply, is_number_eq, value::Value};
pub(crate) fn builtin(args: &[Value], env: Environment) -> Result<Value, std::sync::Arc<str>> {
let [v1, v2, v3] = if let [a, b, c] = args {
[a, b, c]
} else {
return Err(std::sync::Arc::from(format!(
"Error[ksl::builtin::Range]: Expected 3 parameters, but {} were passed.",
args.len()
)));
};
let start = eval_apply(v1, env.clone())?;
let step_val = eval_apply(v2, env.clone())?;
let bound_val = eval_apply(v3, env)?;
match (start, step_val, bound_val) {
(Value::Number(s), Value::Number(step), Value::Number(b)) => {
if is_number_eq(step, 0.0) {
return if is_number_eq(s, b) {
Ok(Value::List(std::sync::Arc::new([Value::Number(s)])))
} else {
Err(std::sync::Arc::from(
"Error[ksl::builtin::Range]: No terminating range.",
))
};
}
let diff = b - s;
if diff.signum() != step.signum() && !is_number_eq(diff, 0.0) {
return Err(std::sync::Arc::from(
"Error[ksl::builtin::Range]: No terminating range.",
));
}
let mut elements = Vec::with_capacity(((diff / step).floor() as usize) + 1);
let mut i = 0usize;
loop {
let current = s + (i as f64) * step;
let remaining = b - current;
if step.signum() != remaining.signum() && !is_number_eq(remaining, 0.0) {
break;
}
elements.push(Value::Number(current));
i += 1;
}
Ok(Value::List(std::sync::Arc::from(elements)))
}
(Value::Number(_), Value::Number(_), e) | (Value::Number(_), e, _) | (e, _, _) => Err(std::sync::Arc::from(format!(
"Error[ksl::builtin::Range]: Unexpected value: `{}`.",
e
))),
}
}
pub(crate) fn many(args: &[Value], env: Environment) -> Result<Value, std::sync::Arc<str>> {
if let [val, count] = args {
match eval_apply(count, env.clone())? {
Value::Number(n) if is_number_eq(n, n.trunc()) && n.is_sign_positive() => Ok(Value::List(
std::iter::repeat_n(eval_apply(val, env)?, n as usize).collect::<std::sync::Arc<[Value]>>(),
)),
e => Err(std::sync::Arc::from(format!(
concat!(
"Error[ksl::builtin::Many]: ",
"Expeced a positive integer, but got: `{}`."
),
e
))),
}
} else {
Err(std::sync::Arc::from(format!(
concat!(
"Error[ksl::builtin::Many]: ",
"Expected 2 parameters, but {} were passed."
),
args.len()
)))
}
}