use crate::eval::{EvalContext, EvalFn, EvalResult, eval};
pub(crate) fn call<'a>(args: Vec<EvalResult<'a>>) -> EvalResult<'a> {
if let Some(EvalResult::Quoted(q, ctx)) = args.first() {
let args_list = if let Some(EvalResult::Object(args_obj)) = args.get(1) {
args_obj.clone()
} else if let Some(EvalResult::Array(args_arr)) = args.get(1) {
args_arr
.iter()
.cloned()
.enumerate()
.map(|(k, v)| (format!("${k}"), v))
.collect::<Vec<_>>()
} else {
return EvalResult::None;
};
let mut new_ctx: EvalContext<'a> = EvalContext {
fns: ctx.fns.clone(),
vars: ctx.vars.clone(),
};
if !new_ctx.vars.contains_key("$arguments") {
new_ctx
.vars
.insert("$arguments".into(), args.get(1).unwrap().clone().into());
} else {
new_ctx.vars.insert(
"$local:arguments".into(),
args.get(1).unwrap().clone().into(),
);
}
for arg in args_list {
new_ctx.vars.insert(arg.0.clone(), arg.1.clone().into());
}
eval(q.as_ref().clone(), new_ctx)
.unwrap_or(EvalResult::None)
.to_owned()
} else {
EvalResult::None
}
}
pub(crate) const let_: EvalFn = func!("call($1, $0)");
#[cfg(test)]
mod tests {
use crate::{eval::EvalResult, eval_str, stdlib::STDLIB};
#[test]
fn test_let_call() {
assert_eq!(
eval_str(r#"let({"a":2,"b":4}, 'num:add(a, b))"#, STDLIB.clone()).unwrap(),
EvalResult::Number(6_f64)
);
assert_eq!(
eval_str(r#"call('num:add(a, b), {"a": 2, "b": 4})"#, STDLIB.clone()).unwrap(),
EvalResult::Number(6_f64)
);
assert_eq!(
eval_str(
r#"
let({"fn": 'num:add(a, b)},
'call(fn, {"a": 2, "b": 4}))
"#,
STDLIB.clone()
)
.unwrap(),
EvalResult::Number(6_f64)
);
assert_eq!(
eval_str(
r#"
call('num:add($0, $1), [2, 2])
"#,
STDLIB.clone()
)
.unwrap(),
EvalResult::Number(4_f64)
)
}
}