use tan::{
context::Context,
error::Error,
eval::{invoke, invoke_func},
expr::{expr_clone, format_value, Expr},
util::{
args::{unpack_arg, unpack_array_arg, unpack_array_mut_arg, unpack_int_arg},
module_util::require_module,
},
};
use super::cmp::rust_ordering_from_tan_ordering;
pub fn list_cons(args: &[Expr]) -> Result<Expr, Error> {
let [head, tail] = args else {
return Err(Error::invalid_arguments(
"requires `head` and `tail` arguments",
None,
));
};
let Some(tail) = tail.as_list() else {
return Err(Error::invalid_arguments(
"`tail` argument should be a List",
tail.range(),
));
};
let mut cons_items = vec![expr_clone(head.unpack())];
for expr in tail {
cons_items.push(expr_clone(expr));
}
Ok(Expr::List(cons_items))
}
pub fn list_count(args: &[Expr]) -> Result<Expr, Error> {
let [list, ..] = args else {
return Err(Error::invalid_arguments("requires `list` argument", None));
};
let Some(list) = list.as_list() else {
return Err(Error::invalid_arguments(
"`list` argument should be a List",
list.range(),
));
};
Ok(Expr::Int(list.len() as i64))
}
pub fn array_eq(args: &[Expr]) -> Result<Expr, Error> {
let a = unpack_array_arg(args, 0, "a")?;
let b = unpack_array_arg(args, 1, "b")?;
Ok(Expr::Bool(*a == *b))
}
pub fn array_push(args: &[Expr]) -> Result<Expr, Error> {
let [array, element] = args else {
return Err(Error::invalid_arguments(
"requires `this` and `element` argument",
None,
));
};
let Some(mut elements) = array.as_array_mut() else {
return Err(Error::invalid_arguments(
"`array` argument should be a Array",
array.range(),
));
};
elements.push(element.unpack().clone());
Ok(Expr::None)
}
pub fn array_put(args: &[Expr]) -> Result<Expr, Error> {
let mut array = unpack_array_mut_arg(args, 0, "array")?;
let index = unpack_int_arg(args, 1, "index")?;
let element = unpack_arg(args, 2, "element")?;
array[index as usize] = element.clone();
Ok(Expr::None)
}
pub fn array_concat_mut(args: &[Expr]) -> Result<Expr, Error> {
let [array1, array2] = args else {
return Err(Error::invalid_arguments("requires two arguments", None));
};
let Some(mut array1) = array1.as_array_mut() else {
return Err(Error::invalid_arguments(
"`array1` argument should be a Array",
array1.range(),
));
};
let Some(mut array2) = array2.as_array_mut() else {
return Err(Error::invalid_arguments(
"`array2` argument should be a Array",
array2.range(),
));
};
array1.append(&mut array2);
Ok(Expr::None)
}
pub fn array_concat(args: &[Expr]) -> Result<Expr, Error> {
let a = unpack_array_arg(args, 0, "a")?;
let b = unpack_array_arg(args, 1, "b")?;
let c: Vec<Expr> = a.iter().chain(b.iter()).cloned().collect();
Ok(Expr::array(c))
}
pub fn array_join(args: &[Expr]) -> Result<Expr, Error> {
let Some(array) = args.first() else {
return Err(Error::invalid_arguments("requires `array` argument", None));
};
let separator = args.get(1);
let separator = if separator.is_some() {
let Some(str) = separator.unwrap().as_stringable() else {
return Err(Error::invalid_arguments(
"the `separator` should be a Stringable",
None,
));
};
str
} else {
""
};
let Some(array) = array.as_array() else {
return Err(Error::invalid_arguments(
"`array` argument should be a Array",
array.range(),
));
};
let elements: Vec<String> = array.iter().map(format_value).collect();
Ok(Expr::String(elements.join(separator)))
}
pub fn array_skip(args: &[Expr]) -> Result<Expr, Error> {
let Some(array) = args.first() else {
return Err(Error::invalid_arguments("requires `array` argument", None));
};
let n = args.get(1);
let n = if n.is_some() {
let Some(n) = n.unwrap().as_int() else {
return Err(Error::invalid_arguments("`n` should be an Int", None));
};
n
} else {
1
};
let Some(array) = array.as_array() else {
return Err(Error::invalid_arguments(
"`array` argument should be a Array",
array.range(),
));
};
let elements: Vec<Expr> = array.iter().skip(n as usize).cloned().collect();
Ok(Expr::array(elements))
}
pub fn array_count(args: &[Expr]) -> Result<Expr, Error> {
let [array, ..] = args else {
return Err(Error::invalid_arguments("requires `array` argument", None));
};
let Some(array) = array.as_array() else {
return Err(Error::invalid_arguments(
"`array` argument should be a Array",
array.range(),
));
};
Ok(Expr::Int(array.len() as i64))
}
pub fn array_is_empty(args: &[Expr]) -> Result<Expr, Error> {
let [array, ..] = args else {
return Err(Error::invalid_arguments("requires `array` argument", None));
};
let Some(array) = array.as_array() else {
return Err(Error::invalid_arguments(
"`array` argument should be a Array",
array.range(),
));
};
Ok(Expr::Bool(array.len() == 0))
}
pub fn array_contains(args: &[Expr]) -> Result<Expr, Error> {
let [array, element] = args else {
return Err(Error::invalid_arguments(
"requires `this` and `element` argument",
None,
));
};
let Some(elements) = array.as_array_mut() else {
return Err(Error::invalid_arguments(
"`array` argument should be a Array",
array.range(),
));
};
Ok(Expr::Bool(elements.contains(element.unpack())))
}
pub fn array_map(args: &[Expr], context: &mut Context) -> Result<Expr, Error> {
let [seq, func] = args else {
return Err(Error::invalid_arguments(
"requires `array` and `func` arguments",
None,
));
};
let Some(input_values) = seq.as_array() else {
return Err(Error::invalid_arguments(
"`seq` must be an `Array`",
seq.range(),
));
};
let mut output_values: Vec<Expr> = Vec::new();
for x in input_values.iter() {
let args = vec![expr_clone(x)];
output_values.push(invoke(func, args, context)?);
}
Ok(Expr::array(output_values))
}
pub fn array_filter(_args: &[Expr]) -> Result<Expr, Error> {
todo!();
}
#[inline]
fn sort_array_items(array_items: &mut [Expr], func: &Expr, context: &mut Context) {
array_items.sort_by(|x, y| {
let args = vec![x.clone(), y.clone()];
let tan_ordering = invoke_func(func, args, context).unwrap();
rust_ordering_from_tan_ordering(&tan_ordering).unwrap()
});
}
pub fn array_sort(args: &[Expr], context: &mut Context) -> Result<Expr, Error> {
let xs = unpack_array_arg(args, 0, "xs")?;
let func = unpack_arg(args, 1, "func")?;
let mut xs_sorted = xs.clone();
sort_array_items(&mut xs_sorted, func, context);
Ok(Expr::array(xs_sorted))
}
pub fn array_sort_mut(args: &[Expr], context: &mut Context) -> Result<Expr, Error> {
let [array, func] = args else {
return Err(Error::invalid_arguments(
"requires `array` and `func` arguments",
None,
));
};
let Some(mut array_items) = array.as_array_mut() else {
return Err(Error::invalid_arguments(
"`array` argument should be a Array",
array.range(),
));
};
sort_array_items(&mut array_items, func, context);
Ok(array.clone())
}
pub fn array_slice(args: &[Expr]) -> Result<Expr, Error> {
let [this, start, ..] = args else {
return Err(Error::invalid_arguments(
"requires `this` and start arguments",
None,
));
};
let Some(elements) = this.as_array() else {
return Err(Error::invalid_arguments(
"`this` argument should be an Array",
this.range(),
));
};
let Some(start) = start.as_int() else {
return Err(Error::invalid_arguments(
"`start` argument should be an Int",
this.range(),
));
};
let end = if let Some(end) = args.get(2) {
let Some(end) = end.as_int() else {
return Err(Error::invalid_arguments(
"`end` argument should be an Int",
this.range(),
));
};
end
} else {
elements.len() as i64
};
let start = start as usize;
let end = if end < 0 {
(elements.len() as i64 + end) as usize
} else {
end as usize
};
let slice = &elements[start..end];
Ok(Expr::array(slice))
}
pub fn array_roll(args: &[Expr]) -> Result<Expr, Error> {
let window_size = unpack_int_arg(args, 0, "window-size")?;
let items = unpack_array_arg(args, 1, "items")?;
let windows = items.windows(window_size as usize);
let mut rolled_items = Vec::new();
for window in windows {
rolled_items.push(Expr::array(window));
}
Ok(Expr::array(rolled_items))
}
pub fn setup_lib_seq(context: &mut Context) {
let module = require_module("prelude", context);
module.insert_invocable("=$$Array$$Array", Expr::foreign_func(&array_eq));
module.insert_invocable("cons", Expr::foreign_func(&list_cons));
module.insert_invocable("count", Expr::foreign_func(&list_count));
module.insert_invocable("count$$List", Expr::foreign_func(&list_count));
module.insert_invocable("push", Expr::foreign_func(&array_push));
module.insert_invocable("put$$Array$$Int$$Int", Expr::foreign_func(&array_put));
module.insert_invocable("put$$Array$$Int$$Float", Expr::foreign_func(&array_put));
module.insert_invocable("concat!", Expr::foreign_func(&array_concat_mut));
module.insert_invocable("concat", Expr::foreign_func(&array_concat));
module.insert_invocable("++", Expr::foreign_func(&array_concat));
module.insert_invocable("map", Expr::foreign_func_mut_context(&array_map));
module.insert_invocable("join", Expr::foreign_func(&array_join));
module.insert_invocable("skip", Expr::foreign_func(&array_skip));
module.insert_invocable("count", Expr::foreign_func(&array_count));
module.insert_invocable("count$$Array", Expr::foreign_func(&array_count));
module.insert_invocable("contains?", Expr::foreign_func(&array_contains));
module.insert_invocable("contains?$$Array$$Int", Expr::foreign_func(&array_contains));
module.insert_invocable(
"contains?$$Array$$String",
Expr::foreign_func(&array_contains),
);
module.insert_invocable("is-empty?", Expr::foreign_func(&array_is_empty));
module.insert_invocable("sort", Expr::foreign_func_mut_context(&array_sort));
module.insert_invocable("sort!", Expr::foreign_func_mut_context(&array_sort_mut));
module.insert_invocable("slice$$Array$$Int", Expr::foreign_func(&array_slice));
module.insert_invocable("slice$$Array$$Int$$Int", Expr::foreign_func(&array_slice));
module.insert_invocable("roll", Expr::foreign_func(&array_roll));
}