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()
)))
}
}
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
)));
}
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()
};
let _ = scope.store.write().insert(sym, std::sync::Arc::new(value));
Ok(())
}