ksl 0.1.5

KSL core library and interpreter
Documentation
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, &current_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, &current_environment),
                        lambda @ Value::Lambda(_, _, _) => eval_apply(
                            &Value::Apply(args.clone(), Box::new(lambda.clone())),
                            &current_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, &current_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, &current_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, &current_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)),
    }
}