use std::sync::{Arc, Mutex};
use mumu::parser::interpreter::{Interpreter, DynamicFnInfo};
use mumu::parser::interpreter::apply_n_ary_function_value;
use mumu::parser::types::{
Value,
FunctionValue,
InkIteratorHandle,
InkIteratorKind,
};
pub fn register_flow_trans(interp: &mut Interpreter) {
let bridging = Arc::new(Mutex::new(flow_trans_bridge_fn));
let info = DynamicFnInfo::new(bridging, true);
interp.register_dynamic_function_ex("flow:trans", info);
interp.set_variable(
"flow:trans",
Value::Function(Box::new(FunctionValue::Named("flow:trans".to_string())))
);
}
fn flow_trans_bridge_fn(_intp: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
match args.len() {
0 => Ok(make_partial(None, None)),
1 => {
if is_placeholder(&args[0]) {
Ok(make_partial(None, None))
} else if let Value::Function(fb) = &args[0] {
Ok(make_partial(Some(fb.clone()), None))
} else {
Err(format!("flow:trans => first arg must be Function or '_', got {:?}", args[0]))
}
}
2 => {
let first = &args[0];
let second = &args[1];
if !is_placeholder(first) && !is_placeholder(second) {
let func_box = match first {
Value::Function(fb) => fb.clone(),
other => return Err(format!("flow:trans => first param must be a Function, got {:?}", other)),
};
match second {
Value::InkIterator(h) => Ok(build_transform_from_iter(func_box, h.clone())),
Value::InkTransform(tfb) => Ok(build_transform_chain(func_box, tfb.clone())),
other => Err(format!("flow:trans => second param must be InkIterator, InkTransform, or '_', got {:?}", other)),
}
} else {
let maybe_func = if is_placeholder(first) {
None
} else if let Value::Function(fb) = first {
Some(fb.clone())
} else {
None
};
let maybe_source = if is_placeholder(second) {
None
} else {
Some(second.clone())
};
if maybe_func.is_some() && maybe_source.is_some() {
let f = maybe_func.unwrap();
let s = maybe_source.unwrap();
match s {
Value::InkIterator(h) => return Ok(build_transform_from_iter(f, h)),
Value::InkTransform(tf) => return Ok(build_transform_chain(f, tf)),
other => return Err(format!("flow:trans => second param must be InkIterator/InkTransform or '_', got {:?}", other)),
}
} else {
Ok(make_partial(maybe_func, maybe_source))
}
}
}
n => Err(format!("flow:trans => expected up to 2 arguments, got {}", n)),
}
}
fn make_partial(func_opt: Option<Box<FunctionValue>>, source_opt: Option<Value>) -> Value {
use mumu::parser::types::FunctionValue::RustClosure;
let st = TransPartialState { func: func_opt, source: source_opt };
Value::Function(Box::new(RustClosure(
"flow:trans-partial".to_string(),
Arc::new(Mutex::new(move |_intp: &mut Interpreter, new_args: Vec<Value>| {
let mut f_cur = st.func.clone();
let mut s_cur = st.source.clone();
for arg in &new_args {
if f_cur.is_none() {
if is_placeholder(arg) {
} else if let Value::Function(fb) = arg {
f_cur = Some(fb.clone());
} else {
return Err(format!("flow:trans => first param must be Function or '_', got {:?}", arg));
}
continue;
}
if s_cur.is_none() {
if is_placeholder(arg) {
} else {
s_cur = Some(arg.clone());
}
continue;
}
return Err("flow:trans => partial => too many arguments".to_string());
}
if let Some(fb) = f_cur.clone() {
if let Some(src) = s_cur.clone() {
match src {
Value::InkIterator(h) => return Ok(build_transform_from_iter(fb, h)),
Value::InkTransform(tf) => return Ok(build_transform_chain(fb, tf)),
other => return Err(format!("flow:trans => second param must be InkIterator or InkTransform, got {:?}", other)),
}
}
}
Ok(make_partial(f_cur, s_cur))
})),
0
)))
}
fn build_transform_from_iter(func: Box<FunctionValue>, iter: InkIteratorHandle) -> Value {
let env = Arc::new(TransEnvIter {
user_func: func,
source_iter: iter,
});
use mumu::parser::types::FunctionValue::RustClosure;
let clos = RustClosure(
"flow:InkTransform".to_string(),
Arc::new(Mutex::new(move |intp: &mut Interpreter, _args: Vec<Value>| {
match &env.source_iter.kind {
InkIteratorKind::Core(state_arc) => {
let mut guard = state_arc.lock()
.map_err(|_| "flow:trans => InkIterator lock error".to_string())?;
if guard.done || guard.current >= guard.end {
guard.done = true;
return Err("NO_MORE_DATA".to_string());
}
let item_val = Value::Int(guard.current);
guard.current += 1;
if guard.current >= guard.end {
guard.done = true;
}
drop(guard);
let result = apply_n_ary_function_value(intp, env.user_func.clone(), vec![item_val])?;
Ok(result)
}
InkIteratorKind::Plugin(_) => {
Err("flow:trans => plugin iterators not supported yet".to_string())
}
}
})),
0
);
Value::InkTransform(Box::new(clos))
}
fn build_transform_chain(func: Box<FunctionValue>, prev_tf: Box<FunctionValue>) -> Value {
let env = Arc::new(ChainEnv {
user_func: func,
prev_transform: prev_tf,
});
use mumu::parser::types::FunctionValue::RustClosure;
let clos = RustClosure(
"flow:InkTransformChain".to_string(),
Arc::new(Mutex::new(move |intp: &mut Interpreter, _args: Vec<Value>| {
let item = apply_n_ary_function_value(intp, env.prev_transform.clone(), vec![]);
match item {
Ok(val) => {
apply_n_ary_function_value(intp, env.user_func.clone(), vec![val])
}
Err(e) => Err(e),
}
})),
0
);
Value::InkTransform(Box::new(clos))
}
#[derive(Clone)]
struct TransPartialState {
func: Option<Box<FunctionValue>>,
source: Option<Value>,
}
#[derive(Clone)]
struct TransEnvIter {
user_func: Box<FunctionValue>,
source_iter: InkIteratorHandle,
}
#[derive(Clone)]
struct ChainEnv {
user_func: Box<FunctionValue>,
prev_transform: Box<FunctionValue>,
}
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,
}
}