use crate::parser::interpreter::Interpreter;
use crate::parser::types::{
ComposeMode,
FunctionValue,
Value,
InkIteratorKind,
FunctionValue::{
Named,
InlineLambdaAST,
InlineLambdaASTPartial,
CurriedMap,
CurriedMapPlaceholder,
CurriedFilter,
CurriedFilterPlaceholder,
CurriedReducePartial,
CurriedReducePlaceholder,
Composition,
CompositionPartial,
RustClosure,
CurriedProp,
CurriedAssoc,
},
};
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 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> {
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 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 {
return Err(format!("InlineLambdaAST => got {} extra args beyond {} params", args.len() - arity, arity));
}
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, mut bound_args } => {
let arity = params.len();
let mut arg_idx = 0;
for arg in &args {
while arg_idx < bound_args.len() && !is_placeholder(&bound_args[arg_idx]) {
arg_idx += 1;
}
if arg_idx >= arity {
return Err(format!(
"InlineLambdaASTPartial => got {} extra args beyond {} params",
args.len() - (arity - arg_idx),
arity
));
}
if is_placeholder(arg) {
bound_args[arg_idx] = Value::Placeholder;
} else {
bound_args[arg_idx] = arg.clone();
}
arg_idx += 1;
}
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,
})))
}
}
CurriedMap(map_fn_box) => {
if args.is_empty() {
Ok(Value::Function(Box::new(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 } => {
let mut current_func = func;
let mut current_data = data;
for val in args {
if current_func.is_none() {
match val {
Value::Function(fb) => current_func = Some(fb),
Value::Placeholder => {},
other => {
return Err(format!(
"CurriedMapPlaceholder => first param must be function or '_', got {:?}",
other
));
}
}
} else if current_data.is_none() {
match val {
Value::Placeholder => {},
x => current_data = Some(x),
}
} else {
return Err("array:map => extra argument(s) after function & data".to_string());
}
}
if let (Some(mf), Some(dat)) = (current_func.as_ref(), current_data.as_ref()) {
do_map(interp, mf, dat.clone())
} else {
Ok(Value::Function(Box::new(CurriedMapPlaceholder {
func: current_func,
data: current_data,
})))
}
}
CurriedFilter(pred_box) => {
if args.is_empty() {
Ok(Value::Function(Box::new(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 } => {
let mut current_pred = pred;
let mut current_data = data;
for val in args {
if current_pred.is_none() {
match val {
Value::Function(fb) => current_pred = Some(fb),
Value::Placeholder => {},
other => {
return Err(format!(
"CurriedFilterPlaceholder => first param must be function or '_', got {:?}",
other
));
}
}
} else if current_data.is_none() {
match val {
Value::Placeholder => {},
v => current_data = Some(v),
}
} else {
return Err("array:filter => extra arg(s) after function & data".to_string());
}
}
if let (Some(pf), Some(dat)) = (current_pred.as_ref(), current_data.as_ref()) {
do_filter(interp, pf, dat.clone())
} else {
Ok(Value::Function(Box::new(CurriedFilterPlaceholder {
pred: current_pred,
data: current_data,
})))
}
}
CurriedReducePartial(f_box, init_val) => {
if args.is_empty() {
Ok(Value::Function(Box::new(CurriedReducePlaceholder {
func: Some(f_box),
init_val: Some(init_val),
data: None,
})))
} else if args.len() == 1 {
let _data_val = args.into_iter().next().unwrap();
Err("reduce => not fully implemented".to_string())
} else {
Err(format!("CurriedReducePartial => expected 0 or 1 arg, got {}", args.len()))
}
}
CurriedReducePlaceholder { func, init_val, data } => {
let mut cf = func;
let mut iv = init_val;
let mut dv = data;
for val in args {
if cf.is_none() {
match val {
Value::Function(fb) => cf = Some(fb),
Value::Placeholder => {},
other => {
return Err(format!(
"array:reduce => first param must be function or '_', got {:?}",
other
));
}
}
} else if iv.is_none() {
match val {
Value::Placeholder => {},
x => iv = Some(x),
}
} else if dv.is_none() {
match val {
Value::Placeholder => {},
x => dv = Some(x),
}
} else {
return Err("array:reduce => extra arg(s) after function, init, data".to_string());
}
}
if let (Some(_f), Some(_ini), Some(_dat)) = (cf.as_ref(), iv.as_ref(), dv.as_ref()) {
Err("reduce => final bridging not fully implemented".to_string())
} else {
Ok(Value::Function(Box::new(CurriedReducePlaceholder {
func: cf,
init_val: iv,
data: dv,
})))
}
}
Composition(f_vec, mode) => {
if args.len() != 1 {
return Err(format!("Composition => expected 1 argument, got {}", args.len()));
}
let mut current_val = args.into_iter().next().unwrap();
match mode {
ComposeMode::Compose => {
for fv_box in f_vec.iter().rev() {
current_val = apply_n_ary_function_value(interp, fv_box.clone(), vec![current_val])?;
}
}
ComposeMode::Pipe => {
for fv_box in f_vec.iter() {
current_val = apply_n_ary_function_value(interp, fv_box.clone(), vec![current_val])?;
}
}
}
Ok(current_val)
}
CompositionPartial { mode, mut slots } => {
for val in args {
let mut assigned = false;
for slot in slots.iter_mut() {
if slot.is_none() {
match val {
Value::Function(fb) => {
*slot = Some(fb);
assigned = true;
break;
}
Value::Placeholder => {
assigned = true;
break;
}
other => {
return Err(format!(
"CompositionPartial => slot expects function, got {:?}",
other
));
}
}
}
if assigned {
break;
}
}
if !assigned {
return Err("CompositionPartial => no open slot or unexpected argument".to_string());
}
}
if slots.iter().all(|mb| mb.is_some()) {
let final_fvec: Vec<Box<FunctionValue>> =
slots.into_iter().map(|op| op.unwrap()).collect();
Ok(Value::Function(Box::new(Composition(final_fvec, mode))))
} else {
Ok(Value::Function(Box::new(CompositionPartial { mode, slots })))
}
}
CurriedProp(k) => {
if args.is_empty() {
return Ok(Value::Function(Box::new(CurriedProp(k))));
}
if args.len() > 1 {
return Err(format!("CurriedProp => expected 0 or 1 arg, got {}", args.len()));
}
let obj_val = args.into_iter().next().unwrap();
match obj_val {
Value::Placeholder => Ok(Value::Function(Box::new(CurriedProp(k)))),
Value::KeyedArray(map) => {
if let Some(v) = map.get(&k) {
Ok(v.clone())
} else {
Err(format!("CurriedProp => no key '{}' in keyed array", k))
}
}
other => Err(format!(
"CurriedProp => final object must be keyed array, got {:?}",
other
)),
}
}
CurriedAssoc(k, v) => {
if args.is_empty() {
return Ok(Value::Function(Box::new(CurriedAssoc(k, v))));
}
if args.len() > 1 {
return Err(format!("CurriedAssoc => expected 1 argument, got {}", args.len()));
}
let obj_val = args.into_iter().next().unwrap();
match obj_val {
Value::KeyedArray(map) => {
let mut new_map = map.clone();
new_map.insert(k, v);
Ok(Value::KeyedArray(new_map))
}
other => Err(format!(
"CurriedAssoc => final object must be keyed array, got {:?}",
other
)),
}
}
RustClosure(desc, closure_arc, arity) => {
if arity != 0 && args.len() != arity {
return Err(format!(
"RustClosure({}): expected {} arg(s), got {}",
desc, arity, args.len()
));
}
let lock = closure_arc
.lock()
.map_err(|_| format!("Cannot lock RustClosure for '{}'", desc))?;
lock(interp, args)
}
}
}
pub fn do_map(
interp: &mut Interpreter,
map_fn: &FunctionValue,
data_val: Value,
) -> Result<Value, String> {
use crate::parser::interpreter::integration::apply_one_function_value;
match data_val {
Value::IntArray(xs) => {
let mut out = Vec::new();
for x in xs {
let ret = apply_one_function_value(interp, Box::new(map_fn.clone()), Value::Int(x))?;
match ret {
Value::Int(i2) => out.push(i2),
other => {
return Err(format!("array:map => user function returned non-int: {:?}", other));
}
}
}
Ok(Value::IntArray(out))
}
Value::StrArray(ss) => {
let mut results = Vec::new();
let mut out_kind: Option<&'static str> = None;
for s in ss {
let ret = apply_one_function_value(interp, Box::new(map_fn.clone()), Value::SingleString(s))?;
match &ret {
Value::Int(_) => {
if out_kind.is_none() {
out_kind = Some("int");
} else if out_kind != Some("int") {
return Err("array:map => inconsistent return types from user function".to_string());
}
}
Value::SingleString(_) => {
if out_kind.is_none() {
out_kind = Some("string");
} else if out_kind != Some("string") {
return Err("array:map => inconsistent return types".to_string());
}
}
_ => return Err("array:map => unsupported return type from user function".to_string()),
}
results.push(ret);
}
match out_kind {
None => Ok(Value::StrArray(vec![])),
Some("int") => {
let mut out = Vec::with_capacity(results.len());
for item in results {
if let Value::Int(n) = item {
out.push(n);
}
}
Ok(Value::IntArray(out))
}
Some("string") => {
let mut out = Vec::with_capacity(results.len());
for item in results {
if let Value::SingleString(st) = item {
out.push(st);
}
}
Ok(Value::StrArray(out))
}
_ => Err("array:map => unrecognized type or mixing? (string vs int?)".to_string()),
}
}
Value::FloatArray(ff) => {
let mut out = Vec::new();
for x in ff {
let ret = apply_one_function_value(interp, Box::new(map_fn.clone()), Value::Float(x))?;
match ret {
Value::Float(y) => out.push(y),
other => {
return Err(format!(
"array:map => user function returned non-float: {:?}",
other
));
}
}
}
Ok(Value::FloatArray(out))
}
Value::InkIterator(h) => {
match &h.kind {
InkIteratorKind::Core(state_arc) => {
let mut out = Vec::new();
let mut guard = state_arc.lock().map_err(|_| "InkIteratorState lock error".to_string())?;
while !guard.done && guard.current < guard.end {
let next_val = guard.current;
guard.current += 1;
if guard.current >= guard.end {
guard.done = true;
}
drop(guard);
let ret = apply_one_function_value(interp, Box::new(map_fn.clone()), Value::Int(next_val))?;
match ret {
Value::Int(i2) => out.push(i2),
other => {
return Err(format!("array:map => user function returned non-int: {:?}", other));
}
}
guard = state_arc.lock().map_err(|_| "InkIteratorState lock error".to_string())?;
}
Ok(Value::IntArray(out))
}
InkIteratorKind::Plugin(plugin_arc) => {
let mut out = Vec::new();
let mut plugin = plugin_arc.lock().map_err(|_| "Plugin InkIterator lock error".to_string())?;
loop {
match plugin.next_value() {
Ok(val) => {
let ret = apply_one_function_value(interp, Box::new(map_fn.clone()), val)?;
match ret {
Value::Int(i2) => out.push(i2),
_ => return Err("array:map => user function returned non-int for plugin iterator".to_string()),
}
}
Err(e) if e == "NO_MORE_DATA" => break,
Err(e) => return Err(e),
}
}
Ok(Value::IntArray(out))
}
}
}
other => Err(format!(
"array:map => only IntArray/StrArray/FloatArray + InkIterator supported, got {:?}",
other
)),
}
}
pub fn do_filter(
interp: &mut Interpreter,
pred_fn: &FunctionValue,
data_val: Value,
) -> Result<Value, String> {
use crate::parser::interpreter::integration::apply_one_function_value;
match data_val {
Value::IntArray(xs) => {
let mut out = Vec::new();
for x in xs {
let ret = apply_one_function_value(interp, Box::new(pred_fn.clone()), Value::Int(x))?;
match ret {
Value::Bool(flag) if flag => out.push(x),
Value::Bool(false) => {},
other => return Err(format!("array:filter => user function returned non-bool: {:?}", other)),
}
}
Ok(Value::IntArray(out))
}
Value::StrArray(ss) => {
let mut out = Vec::new();
for s in ss {
let ret = apply_one_function_value(interp, Box::new(pred_fn.clone()), Value::SingleString(s.clone()))?;
match ret {
Value::Bool(true) => out.push(s),
Value::Bool(false) => {},
other => return Err(format!("array:filter => user function returned non-bool: {:?}", other)),
}
}
Ok(Value::StrArray(out))
}
Value::InkIterator(h) => {
match &h.kind {
InkIteratorKind::Core(state_arc) => {
let mut out = Vec::new();
let mut guard = state_arc.lock().map_err(|_| "InkIteratorState lock error".to_string())?;
while !guard.done && guard.current < guard.end {
let next_val = guard.current;
guard.current += 1;
if guard.current >= guard.end {
guard.done = true;
}
drop(guard);
let ret = apply_one_function_value(interp, Box::new(pred_fn.clone()), Value::Int(next_val))?;
match ret {
Value::Bool(true) => out.push(Value::Int(next_val)),
Value::Bool(false) => {},
_ => return Err("array:filter => user function returned non-bool for core iterator".to_string()),
}
guard = state_arc.lock().map_err(|_| "InkIteratorState lock error".to_string())?;
}
Ok(Value::IntArray(out.into_iter().map(|v| match v {
Value::Int(i) => i,
_ => unreachable!(),
}).collect()))
}
InkIteratorKind::Plugin(plugin_arc) => {
let mut out = Vec::new();
let mut plugin = plugin_arc.lock().map_err(|_| "Plugin InkIterator lock error".to_string())?;
loop {
match plugin.next_value() {
Ok(val) => {
let ret = apply_one_function_value(interp, Box::new(pred_fn.clone()), val.clone())?;
match ret {
Value::Bool(true) => out.push(val),
Value::Bool(false) => {},
_ => return Err("array:filter => user function returned non-bool for plugin iterator".to_string()),
}
}
Err(e) if e == "NO_MORE_DATA" => break,
Err(e) => return Err(e),
}
}
Ok(Value::IntArray(out.into_iter().map(|v| match v {
Value::Int(i) => i,
_ => unreachable!(),
}).collect()))
}
}
}
other => Err(format!("array:filter => only IntArray/StrArray/InkIterator supported, got {:?}", other)),
}
}
fn call_inline_lambda_ast(
interp: &mut Interpreter,
params: &[String],
body_expr: &crate::parser::ast::Expr,
arg_vals: Vec<Value>,
) -> Result<Value, String> {
let mut old_vals = Vec::new();
for (i, p) in params.iter().enumerate() {
let old = interp.get_variables().get(p).cloned();
old_vals.push((p.clone(), old));
interp.set_variable(p, arg_vals[i].clone());
}
let out = interp.eval_expression(body_expr);
for (p, maybe_val) in old_vals {
if let Some(v) = maybe_val {
interp.set_variable(&p, v);
} else {
interp.get_variables_mut().remove(&p);
}
}
out
}