use crate::{Environment, builtin::eval_builtin, eval::apply::eval_apply, value::Value};
pub(crate) fn builtin(args: &[Value], env: &Environment) -> Option<(Value, Environment)> {
let mut current_environment = env.clone();
let mut local_environment = Environment::new();
if let [func, arguments @ ..] = &args[..] {
let mut eval_args = Vec::with_capacity(arguments.len());
for argument in arguments.iter() {
if let Some((new_arg, new_env)) = eval_apply(argument, ¤t_environment) {
local_environment.extend(new_env.clone());
current_environment.extend(new_env);
eval_args.push(new_arg);
} else {
eprintln!(
concat!(
"Error[ksl::builtin::apply]: ",
"Cannot evaluate expression {:?}."
),
argument
);
return None;
}
}
match eval_apply(func, ¤t_environment) {
Some((Value::Identity(id), _)) => {
if let Some(func_body) = current_environment.get(&id) {
eval_apply(
&Value::Apply(eval_args, Box::new(func_body.clone())),
¤t_environment,
)
} else {
eprintln!("Error[ksl::builtin::apply]: Symbol `{}` is not bound.", id);
None
}
}
Some((lambda @ Value::Lambda(_, _, _), _)) => eval_apply(
&Value::Apply(eval_args, Box::new(lambda)),
¤t_environment,
),
Some((Value::Builtin(name), _)) => eval_builtin(name, &eval_args, ¤t_environment),
Some((plugin @ Value::Plugin(_, _), _)) => eval_apply(
&Value::Apply(arguments.to_vec(), Box::new(plugin)),
¤t_environment,
),
Some((e, _)) => {
eprintln!(
concat!(
"Error[ksl::builtin::apply]: ",
"`{:?}` is not an applicable value.",
),
e
);
None
}
None => {
eprintln!(
concat!(
"Error[ksl::builtin::apply]: ",
"Cannot evaluate expression {:?}."
),
func
);
None
}
}
} else {
eprintln!(
concat!(
"Error[ksl::builtin::apply]: ",
"Only accepts an applicable value ",
"and a series of arbitrary values as arguments, ",
"but {:?} were passed."
),
args
);
None
}
}