use crate::parser::types::{FunctionValue, Value, LambdaParam};
use crate::parser::interpreter::Interpreter;
use super::partial::apply_partial_application;
use super::array_helpers::{do_map, do_filter};
use super::curry_utils::{
handle_composition, handle_composition_partial, handle_curried_prop, handle_curried_assoc,
handle_rust_closure,
};
use super::lambda::call_inline_lambda_ast;
pub fn is_placeholder(val: &Value) -> bool {
match val {
Value::Placeholder => true,
Value::SingleString(s) if s == "_" => true,
Value::StrArray(ss) if ss.len() == 1 && ss[0] == "_" => true,
_ => false,
}
}
pub fn contains_placeholder(val: &Value) -> bool {
match val {
Value::Placeholder => true,
Value::SingleString(s) if s == "_" => true,
Value::StrArray(ss) if ss.len() == 1 && ss[0] == "_" => true,
Value::MixedArray(items) => items.iter().any(contains_placeholder),
_ => false,
}
}
pub fn apply_one_function_value(
interp: &mut Interpreter,
func_val: Box<FunctionValue>,
data: Value,
) -> Result<Value, String> {
apply_n_ary_function_value(interp, func_val, vec![data])
}
pub fn apply_n_ary_function_value(
interp: &mut Interpreter,
func_val: Box<FunctionValue>,
args: Vec<Value>,
) -> Result<Value, String> {
use FunctionValue::*;
match *func_val {
Named(ref name) => {
if let Some(bridge) = interp.get_dynamic_function(name) {
let closure = bridge.lock()
.map_err(|_| format!("Dynamic function lock error for '{}'", name))?;
closure(interp, args)
} else {
Err(format!("No built-in bridging for Named('{}')", name))
}
}
InlineLambdaAST { params, body_expr } => {
let mut bound_args = Vec::new();
let arity = params.len();
let variadic_idx = params.iter().position(|p| matches!(p, LambdaParam::Variadic));
if let Some(vidx) = variadic_idx {
if arity == 1 {
let arg_vec = args;
let any_ph = arg_vec.iter().any(contains_placeholder);
let ba = vec![Value::MixedArray(arg_vec)];
if !any_ph {
call_inline_lambda_ast(interp, ¶ms, &body_expr, ba)
} else {
Ok(Value::Function(Box::new(InlineLambdaASTPartial {
params,
body_expr,
bound_args: ba,
})))
}
} else if arity >= 2 {
let before_count = vidx;
let after_count = arity - vidx - 1;
let total_args = args.len();
for i in 0..before_count {
if i < total_args {
bound_args.push(args[i].clone());
} else {
bound_args.push(Value::Placeholder);
}
}
let start_variadic = before_count;
let end_variadic = total_args.saturating_sub(after_count);
let variadic_slice = if end_variadic > start_variadic && total_args >= before_count + after_count {
args[start_variadic..end_variadic].to_vec()
} else {
Vec::new()
};
bound_args.push(Value::MixedArray(variadic_slice.clone()));
for i in 0..after_count {
let idx = total_args.checked_sub(after_count).unwrap_or(0) + i;
if idx < total_args {
bound_args.push(args[idx].clone());
} else {
bound_args.push(Value::Placeholder);
}
}
let any_ph = bound_args.iter().any(contains_placeholder);
if !any_ph {
call_inline_lambda_ast(interp, ¶ms, &body_expr, bound_args)
} else {
Ok(Value::Function(Box::new(InlineLambdaASTPartial {
params,
body_expr,
bound_args,
})))
}
} else {
Err("Invalid lambda parameter pattern".to_string())
}
}
else {
let arity = params.len();
let mut bound_args: Vec<Value> = Vec::with_capacity(arity);
let mut n_provided = 0;
for arg in &args {
if n_provided >= arity {
break; }
if is_placeholder(arg) {
bound_args.push(Value::Placeholder);
} else {
bound_args.push(arg.clone());
}
n_provided += 1;
}
while bound_args.len() < arity {
bound_args.push(Value::Placeholder);
}
if bound_args.iter().all(|v| !is_placeholder(v)) {
call_inline_lambda_ast(interp, ¶ms, &body_expr, bound_args)
} else {
Ok(Value::Function(Box::new(InlineLambdaASTPartial {
params,
body_expr,
bound_args,
})))
}
}
}
InlineLambdaASTPartial { params, body_expr, bound_args } => {
apply_partial_application(interp, params, body_expr, bound_args, args)
}
CurriedMap(map_fn_box) => {
if args.is_empty() {
Ok(Value::Function(Box::new(FunctionValue::CurriedMapPlaceholder {
func: Some(map_fn_box),
data: None,
})))
} else if args.len() == 1 {
let data_val = args.into_iter().next().unwrap();
do_map(interp, &*map_fn_box, data_val)
} else {
Err(format!("array:map => expected 0 or 1 argument, got {}", args.len()))
}
}
CurriedMapPlaceholder { func, data } => {
super::array_helpers::apply_curried_map_placeholder(interp, func, data, args)
}
CurriedFilter(pred_box) => {
if args.is_empty() {
Ok(Value::Function(Box::new(FunctionValue::CurriedFilterPlaceholder {
pred: Some(pred_box),
data: None,
})))
} else if args.len() == 1 {
let data_val = args.into_iter().next().unwrap();
do_filter(interp, &*pred_box, data_val)
} else {
Err(format!("array:filter => expected 0 or 1 arg, got {}", args.len()))
}
}
CurriedFilterPlaceholder { pred, data } => {
super::array_helpers::apply_curried_filter_placeholder(interp, pred, data, args)
}
CurriedReducePartial(f_box, init_val) => {
super::array_helpers::apply_curried_reduce_partial(interp, f_box, init_val, args)
}
CurriedReducePlaceholder { func, init_val, data } => {
super::array_helpers::apply_curried_reduce_placeholder(interp, func, init_val, data, args)
}
Composition(f_vec, mode) => handle_composition(interp, f_vec, mode, args),
CompositionPartial { mode, slots } => handle_composition_partial(interp, mode, slots, args),
CurriedProp(k) => handle_curried_prop(interp, k, args),
CurriedAssoc(k, v) => handle_curried_assoc(interp, k, v, args),
RustClosure(desc, closure_arc, arity) => handle_rust_closure(interp, desc, closure_arc, arity, args),
}
}