use crate::{Dict, Environment, Scope, builtin::eval_builtin, eval::plugin::apply_plugin, read_from_environment, value::Value};
pub fn eval_apply(val: &Value, env: Environment) -> Result<Value, std::sync::Arc<str>> {
match val {
Value::List(contents) => Ok(Value::List(
contents
.iter()
.map(|content| eval_apply(content, env.clone()))
.collect::<Result<std::sync::Arc<[Value]>, std::sync::Arc<str>>>()?,
)),
Value::Apply(args, func_expr) => {
let func_value = eval_apply(func_expr, env.clone())?;
match func_value {
Value::Builtin(name) => eval_builtin(name, args, env),
Value::Lambda(params, func_body, cap) => {
if args.len() != params.len() {
return Err(std::sync::Arc::from(format!(
"Error[ksl::eval::apply::eval_apply]: Expected {} parameter(s), but {} were passed.",
params.len(),
args.len()
)));
}
let mut eval_args = Vec::with_capacity(args.len());
for arg in args.iter() {
eval_args.push(std::sync::Arc::new(eval_apply(arg, env.clone())?));
}
let mut local_store = Dict::with_capacity_and_hasher(params.len(), rustc_hash::FxBuildHasher);
for (param_name, arg_val) in params.iter().zip(eval_args) {
local_store.insert(param_name.clone(), arg_val);
}
let exec_env = std::sync::Arc::new(Scope {
store: parking_lot::RwLock::new(local_store),
parent: Some(cap),
});
eval_apply(&func_body, exec_env)
}
Value::Plugin(lib, name) => apply_plugin(&lib, &name, args, env),
_ => Err(std::sync::Arc::from(format!(
"Error[ksl::eval::apply::eval_apply]: Value `{}` is not callable.",
func_value
))),
}
}
Value::Symbol(sym) => match read_from_environment(&env, sym) {
Some(val) => Ok(val.as_ref().clone()),
None => Err(std::sync::Arc::from(format!(
"Error[ksl::eval::apply::eval_apply]: Unknown symbol `{}`.",
sym
))),
},
_ => Ok(val.clone()),
}
}