ksl 0.1.30

KSL core library and interpreter
Documentation
//! # ksl::eval::apply
use crate::{Dict, Environment, Scope, builtin::eval_builtin, eval::plugin::apply_plugin, read_from_environment, value::Value};

/// Applies a value to a value that can be applied in KSL.
///
/// # Example
///
/// ```rust
/// use ksl::{Scope, value::Value};
///
/// let env = ksl::init_environment(None);
///
/// // ((x) => x + 1)(2)
/// let func = Value::Apply(
///     vec![Value::Number(2.0)],
///     std::sync::Arc::new(Value::Lambda(
///         vec![std::sync::Arc::from("x")],
///         std::sync::Arc::new(Value::Apply(
///             vec![Value::Symbol(std::sync::Arc::from("x")), Value::Number(1.0)],
///             std::sync::Arc::new(Value::Symbol(std::sync::Arc::from("Add"))),
///         )),
///         env.clone(),
///     )),
/// );
///
/// match ksl::eval::apply::eval_apply(&func, env) {
///     Ok(value) => assert_eq!(value, Value::Number(3.0)),
///     Err(_) => unreachable!(),
/// }
/// ```
pub fn eval_apply(val: &Value, env: Environment) -> Result<Value, std::sync::Arc<str>> {
    match val {
        // list
        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>>>()?,
        )),
        // appliable
        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()
                        )));
                    }
                    // arguments
                    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())?));
                    }
                    // local scope
                    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
                ))),
            }
        }
        // find symbols
        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()),
    }
}