ksl 0.1.30

KSL core library and interpreter
Documentation
//! # ksl::builtin::let
//!
//! Built-in function `Let` and `Update`.

use crate::{Environment, eval::apply::eval_apply, find_define_environment, is_invalid_symbol, value::Value};

pub(crate) fn builtin(args: &[Value], is_update: bool, env: Environment) -> Result<Value, std::sync::Arc<str>> {
    if let [sym, val] = args {
        let evaluated_val = eval_apply(val, env.clone())?;

        match sym {
            Value::Symbol(var) => {
                update_environment(var.clone(), evaluated_val, is_update, &env)?;
                Ok(Value::Unit)
            }
            Value::List(syms) => match evaluated_val {
                Value::List(elements) => {
                    if elements.len() < syms.len() {
                        return Err(std::sync::Arc::from(format!(
                            concat!(
                                "Error[ksl::builtin::{}]: ",
                                "Expected at least {} values, but {} were passed."
                            ),
                            if is_update { "Update" } else { "Let" },
                            syms.len(),
                            elements.len()
                        )));
                    }

                    for (symbol, element) in syms.iter().zip(elements.iter()) {
                        match symbol {
                            Value::Symbol(name) => {
                                update_environment(name.clone(), element.clone(), is_update, &env)?;
                            }
                            e => {
                                return Err(std::sync::Arc::from(format!(
                                    "Error[ksl::builtin::{}]: Unexpected value in symbol list: `{}`.",
                                    if is_update { "Update" } else { "Let" },
                                    e
                                )));
                            }
                        }
                    }
                    Ok(Value::Unit)
                }
                e => Err(std::sync::Arc::from(format!(
                    "Error[ksl::builtin::{}]: Expected a List value for destructuring, but got `{}`.",
                    if is_update { "Update" } else { "Let" },
                    e
                ))),
            },
            e => Err(std::sync::Arc::from(format!(
                "Error[ksl::builtin::{}]: Unexpected symbol value: `{}`.",
                if is_update { "Update" } else { "Let" },
                e
            ))),
        }
    } else {
        Err(std::sync::Arc::from(format!(
            concat!(
                "Error[ksl::builtin::{}]: ",
                "Expected 2 parameters, but {} were passed."
            ),
            if is_update { "Update" } else { "Let" },
            args.len()
        )))
    }
}

/// register variables into envrionment
fn update_environment(
    sym: std::sync::Arc<str>,
    value: Value,
    is_update: bool,
    env: &Environment,
) -> Result<(), std::sync::Arc<str>> {
    if is_invalid_symbol(&sym) {
        return Err(std::sync::Arc::from(format!(
            concat!(
                "Error[ksl::builtin::{}]: ",
                "Rebinding of symbol `{}` is not permitted."
            ),
            if is_update { "Update" } else { "Let" },
            sym
        )));
    }

    // find defined scope
    let scope = if is_update {
        if let Some(target) = find_define_environment(env, sym.as_ref()) {
            target
        } else {
            return Err(std::sync::Arc::from(format!(
                concat!(
                    "Error[ksl::builtin::{}]: ",
                    "Rebinding of symbol `{}` is not permitted."
                ),
                if is_update { "Update" } else { "Let" },
                sym
            )));
        }
    } else {
        env.clone()
    };

    // insert value
    let _ = scope.store.write().insert(sym, std::sync::Arc::new(value));
    Ok(())
}