Skip to main content

ksl/eval/
apply.rs

1//! # ksl::eval::apply
2use crate::{Dict, Environment, Scope, builtin::eval_builtin, eval::plugin::apply_plugin, read_from_environment, value::Value};
3
4/// Applies a value to a value that can be applied in KSL.
5///
6/// # Example
7///
8/// ```rust
9/// use ksl::{Scope, value::Value};
10///
11/// let env = ksl::init_environment(None);
12///
13/// // ((x) => x + 1)(2)
14/// let func = Value::Apply(
15///     vec![Value::Number(2.0)],
16///     std::sync::Arc::new(Value::Lambda(
17///         vec![std::sync::Arc::from("x")],
18///         std::sync::Arc::new(Value::Apply(
19///             vec![Value::Symbol(std::sync::Arc::from("x")), Value::Number(1.0)],
20///             std::sync::Arc::new(Value::Symbol(std::sync::Arc::from("Add"))),
21///         )),
22///         env.clone(),
23///     )),
24/// );
25///
26/// match ksl::eval::apply::eval_apply(&func, env) {
27///     Ok(value) => assert_eq!(value, Value::Number(3.0)),
28///     Err(_) => unreachable!(),
29/// }
30/// ```
31pub fn eval_apply(val: &Value, env: Environment) -> Result<Value, std::sync::Arc<str>> {
32    match val {
33        // list
34        Value::List(contents) => Ok(Value::List(
35            contents
36                .iter()
37                .map(|content| eval_apply(content, env.clone()))
38                .collect::<Result<std::sync::Arc<[Value]>, std::sync::Arc<str>>>()?,
39        )),
40        // appliable
41        Value::Apply(args, func_expr) => {
42            let func_value = eval_apply(func_expr, env.clone())?;
43            match func_value {
44                Value::Builtin(name) => eval_builtin(name, args, env),
45                Value::Lambda(params, func_body, cap) => {
46                    if args.len() != params.len() {
47                        return Err(std::sync::Arc::from(format!(
48                            "Error[ksl::eval::apply::eval_apply]: Expected {} parameter(s), but {} were passed.",
49                            params.len(),
50                            args.len()
51                        )));
52                    }
53                    // arguments
54                    let mut eval_args = Vec::with_capacity(args.len());
55                    for arg in args.iter() {
56                        eval_args.push(std::sync::Arc::new(eval_apply(arg, env.clone())?));
57                    }
58                    // local scope
59                    let mut local_store = Dict::with_capacity_and_hasher(params.len(), rustc_hash::FxBuildHasher);
60                    for (param_name, arg_val) in params.iter().zip(eval_args) {
61                        local_store.insert(param_name.clone(), arg_val);
62                    }
63                    let exec_env = std::sync::Arc::new(Scope {
64                        store: parking_lot::RwLock::new(local_store),
65                        parent: Some(cap.clone()),
66                    });
67
68                    eval_apply(&func_body, exec_env)
69                }
70
71                Value::Plugin(lib, name) => apply_plugin(&lib, &name, args, env),
72                _ => Err(std::sync::Arc::from(format!(
73                    "Error[ksl::eval::apply::eval_apply]: Value `{}` is not callable.",
74                    func_value
75                ))),
76            }
77        }
78        // find symbols
79        Value::Symbol(sym) => match read_from_environment(&env, sym) {
80            Some(val) => Ok(val.as_ref().clone()),
81            None => Err(std::sync::Arc::from(format!(
82                "Error[ksl::eval::apply::eval_apply]: Unknown symbol `{}`.",
83                sym
84            ))),
85        },
86        _ => Ok(val.clone()),
87    }
88}