use crate::{
eval::{EvalFn, EvalResult, eval},
parser::ExprValue,
stdlib::{func::func, quote::call},
};
pub(crate) fn map(args: Vec<EvalResult>) -> EvalResult {
if let Some(EvalResult::Array(arr)) = args.get(0)
&& let Some(EvalResult::Quoted(block, ctx)) = args.get(1)
{
EvalResult::Array(
arr.iter()
.enumerate()
.map(|(idx, it)| {
eval(
block.as_ref().clone(),
ctx.with_var("$it", it.clone().into())
.with_var("$i", ExprValue::Number(idx as f64)),
)
})
.collect::<Result<Vec<_>, _>>()
.unwrap_or(vec![]),
)
} else {
EvalResult::None
}
}
pub(crate) fn reduce(args: Vec<EvalResult>) -> EvalResult {
if let Some(EvalResult::Array(arr)) = args.get(0)
&& let Some(quoted) = args.get(1)
{
arr.iter()
.map(|v| v.clone())
.reduce(|a, b| call(vec![quoted.clone(), EvalResult::Array(vec![a, b])]))
.unwrap_or(EvalResult::None)
} else {
EvalResult::None
}
}
pub(crate) static append: EvalFn = func!(
r#"
array:merge($0, [$1])
"#
);
pub(crate) fn merge(args: Vec<EvalResult>) -> EvalResult {
if let Some(EvalResult::Array(arr)) = args.get(0)
&& let Some(EvalResult::Array(arr2)) = args.get(1)
{
let mut new_arr = arr.clone();
let mut new_arr2 = arr2.clone();
new_arr.append(&mut new_arr2);
EvalResult::Array(new_arr)
} else {
EvalResult::None
}
}
pub(crate) fn at(args: Vec<EvalResult>) -> EvalResult {
if let Some(EvalResult::Array(arr)) = args.get(0)
&& let Some(EvalResult::Number(idx)) = args.get(1)
{
arr.get(*idx as usize).unwrap_or(&EvalResult::None).clone()
} else {
EvalResult::None
}
}
pub(crate) fn length(args: Vec<EvalResult>) -> EvalResult {
if let Some(EvalResult::Array(arr)) = args.get(0) {
EvalResult::Number(arr.len() as f64)
} else {
EvalResult::None
}
}
pub(crate) fn slice(args: Vec<EvalResult>) -> EvalResult {
if let Some(EvalResult::Array(arr)) = args.get(0)
&& let Some(EvalResult::Number(start)) = args.get(1)
&& let Some(EvalResult::Number(end)) = args.get(2)
{
EvalResult::Array(
arr[(*start as usize)..(*end as usize)]
.iter()
.cloned()
.collect(),
)
} else {
EvalResult::None
}
}
pub(crate) static head: EvalFn = func!(
r#"
array:at($0, 0)
"#
);
pub(crate) static tail: EvalFn = func!(
r#"
use(["array"], 'slice($0, 1, length($0)))
"#
);
pub(crate) static last: EvalFn = func!(
r#"
use(["array", "num"], 'at($0, sub(length($0), 1)))
"#
);
pub(crate) static filter: EvalFn = func!(
r#"
let({"arr": $0, "predicate": $1}, 'use(["array", "bool"],
'reduce(arr,
'if(predicate($1),
'append($0, $1),
'$0)))
"#
);
pub(crate) static find: EvalFn = func!(
r#"
use(["array"], 'head(filter($0, $1)))
"#
);
#[cfg(test)]
mod tests {
use crate::{eval::EvalResult, eval_str, stdlib::STDLIB};
#[test]
fn test_map() {
assert_eq!(
eval_str(
r#"use(["array", "string", "num"], 'concat(...map([2, 4, 6], 'mul($it, 2))))"#,
STDLIB.clone()
)
.unwrap(),
EvalResult::String("4812".to_string())
);
}
#[test]
fn test_at() {
assert_eq!(
eval_str(r#"use(["array"], 'at([2, 4, 6], 1))"#, STDLIB.clone()).unwrap(),
EvalResult::Number(4 as f64)
);
}
#[test]
fn test_length() {
assert_eq!(
eval_str(r#"use(["array"], 'length([2, 4, 6]))"#, STDLIB.clone()).unwrap(),
EvalResult::Number(3 as f64)
);
}
#[test]
fn test_head() {
assert_eq!(
eval_str(r#"use(["array"], 'head([2, 4, 6]))"#, STDLIB.clone()).unwrap(),
EvalResult::Number(2 as f64)
);
}
#[test]
fn test_slice() {
assert_eq!(
eval_str(r#"use(["array"], 'slice([2, 4, 6], 1, 3))"#, STDLIB.clone()).unwrap(),
EvalResult::Array(vec![
EvalResult::Number(4 as f64),
EvalResult::Number(6 as f64)
])
);
}
#[test]
fn test_tail() {
assert_eq!(
eval_str(r#"use(["array"], 'tail([2, 4, 6]))"#, STDLIB.clone()).unwrap(),
EvalResult::Array(vec![
EvalResult::Number(4 as f64),
EvalResult::Number(6 as f64)
])
);
}
#[test]
fn test_last() {
assert_eq!(
eval_str(r#"use(["array"], 'last([2, 4, 6]))"#, STDLIB.clone()).unwrap(),
EvalResult::Number(6 as f64)
);
}
}