use crate::{
Environment,
builtin::{DEFAULT_KSL_PATH, KSL_PATH_ENV, eval_builtin},
expand_tilde,
value::Value,
};
pub fn eval_apply(val: &Value, env: &Environment) -> Option<(Value, Environment)> {
let mut current_environment = env.clone();
let mut local_environment = Environment::new();
match val {
Value::List(contents) => {
let mut elements = Vec::with_capacity(contents.len());
for content in contents.iter() {
if let Some((element, new_env)) = eval_apply(content, ¤t_environment) {
local_environment.extend(new_env.clone());
current_environment.extend(new_env);
elements.push(element);
} else {
eprintln!(
concat!(
"Error[ksl::eval::eval_apply]: ",
"Cannot evaluate expression {:?}."
),
content
);
return None;
}
}
Some((Value::List(elements), local_environment))
}
Value::Apply(args, func) => match &**func {
Value::Identity(id) => {
if let Some(func_body) = current_environment.get(id) {
match func_body {
Value::Builtin(name) => eval_builtin(name, &args, ¤t_environment),
lambda @ Value::Lambda(_, _, _) => eval_apply(
&Value::Apply(args.clone(), Box::new(lambda.clone())),
¤t_environment,
),
e => {
eprintln!(
concat!(
"Error[ksl::eval::eval_apply]: ",
"Expected a lambda or a built-in function, but got `{:?}`."
),
e
);
None
}
}
} else {
eprintln!(
concat!("Error[ksl::eval::eval_apply]: ", "Unknown symbol `{}`."),
id
);
None
}
}
Value::Lambda(params, func_body, inner_env) => {
let mut eval_args = Vec::with_capacity(args.len());
for arg in args.iter() {
if let Some((new_arg, new_env)) = eval_apply(arg, ¤t_environment) {
local_environment.extend(new_env.clone());
current_environment.extend(new_env);
eval_args.push(new_arg);
} else {
eprintln!(
concat!(
"Error[ksl::eval::eval_apply]: ",
"Cannot evaluate expression {:?}."
),
arg
);
return None;
}
}
if params.len() == eval_args.len() {
let mut capture = current_environment.clone();
capture.extend(inner_env.clone());
capture.extend(
params
.iter()
.zip(eval_args.iter())
.map(|(name, val)| (name.clone(), val.clone())),
);
eval_apply(func_body, &capture)
} else {
eprintln!(
concat!(
"Error[ksl::eval::eval_apply]: ",
"Expected `{}` parameters, but received `{}`."
),
params.len(),
eval_args.len()
);
None
}
}
Value::Builtin(name) => eval_builtin(name, args, ¤t_environment),
Value::Plugin(lib, name) => {
let path_with_ext = format!(
"lib{}.{}",
lib,
if cfg!(target_os = "windows") {
"dll"
} else if cfg!(target_os = "macos") {
"dylib"
} else {
"so"
}
);
let real_path = if let Ok(true) = std::fs::exists(&path_with_ext) {
path_with_ext
} else {
let path_prepend_lib = format!("lib/{}", path_with_ext);
if let Ok(true) = std::fs::exists(&path_prepend_lib) {
path_prepend_lib
} else {
let ksl_path = match std::env::var(KSL_PATH_ENV) {
Ok(p) => p,
Err(_) => String::from(DEFAULT_KSL_PATH),
};
if let Some(ksl_path) = expand_tilde(ksl_path) {
format!("{}/lib/{}", ksl_path, path_with_ext)
} else {
return None;
}
}
};
match unsafe { libloading::Library::new(&real_path) } {
Ok(loaded_lib) => {
match unsafe {
loaded_lib.get::<fn(&[Value], &Environment) -> Option<(Value, Environment)>>(name.as_bytes())
} {
Ok(plugin_func) => plugin_func(args, ¤t_environment),
Err(err) => {
println!(
concat!(
"Error[ksl::eval::eval_apply]: ",
"Failed to load function `{}` with `{}`."
),
name, err
);
return None;
}
}
}
Err(err) => {
println!(
concat!(
"Error[ksl::eval::eval_apply]: ",
"Failed to load library `{}` with `{}`."
),
real_path, err
);
return None;
}
}
}
e => {
eprintln!(
concat!(
"Error[ksl::eval::eval_apply]: ",
"`{:?}` is not an applicable value.",
),
e
);
None
}
},
Value::Identity(id) => {
if let Some(content) = env.get(id) {
Some((content.clone(), local_environment))
} else {
eprintln!(
concat!("Error[ksl::eval::eval_apply]: ", "Unknown symbol `{}`."),
id
);
None
}
}
_ => Some((val.clone(), local_environment)),
}
}