use std::rc::Rc;
use crate::eval::State;
use crate::expression::BoxedIterator;
use crate::expression::eval2::execute_iteration;
use crate::libs::bin::{
math_lib,
top::{dig, flatten, len, rev},
};
use crate::libs::helper::{
check_args_len, check_exact_args_len, check_fn_arg, get_integer_arg, get_integer_ref,
get_string_ref,
};
use crate::libs::lazy_module::LazyModule;
use crate::{
Environment, Expression, Int, RuntimeError, RuntimeErrorKind, libs::BuiltinInfo, reg_info,
reg_lazy,
};
use rand::prelude::IndexedRandom;
use rand::seq::SliceRandom;
use std::cmp::Ordering;
use std::collections::{BTreeMap, BTreeSet, HashMap};
pub fn regist_lazy() -> LazyModule {
reg_lazy!({
max,min,sum,average,
dig,len,is_empty,first,last,get,take,skip,slice,
contains,find,rfind,position,rposition,
insert,rev,flatten,push,unique,split_at,split_first,sort,group,
remove_at,remove,set,swap,rotate,splice,
concat,from,fill,
map,items,filter,filter_map,any,all,
join,to_map,to_hmap,to_set,
transpose,chunks,fold,rfold,zip,unzip,windows,
shuffle,sample,
})
}
pub fn regist_info() -> BTreeMap<&'static str, BuiltinInfo> {
reg_info!({
max => "max value", "<num1> <num2>... | <array>"
min => "min value", "<num1> <num2>... | <array>"
sum => "sum of numbers", "<num1> <num2>... | <array>"
average => "average of numbers", "<num1> <num2>... | <array>"
dig => "get nested value by dot path. e.g. dig m 'a.b.0'", "<map|list|range> <path>"
len => "list length", "<list>"
is_empty => "is empty?", "<list>"
first => "first n elements", "<list> [n=1]"
last => "last n elements", "<list> [n=1]"
get => "nth element, negative index from end", "<list> <index>"
take => "first n elements", "<list> <count>"
skip => "skip first n elements", "<list> <count>"
slice => "sub-list [start,end), negative index ok", "<list> <start> <end>"
contains => "contains item?", "<list> <item>"
find => "first matched item", "<list> <item|fn> [skip_n=0]"
rfind => "last matched item", "<list> <item|fn> [skip_n=0]"
position => "first matched index", "<list> <item|fn> [skip_n=0]"
rposition => "last matched index", "<list> <item|fn> [skip_n=0]"
insert => "insert value at index", "<list> <index> <value>"
rev => "reverse", "<list>"
flatten => "flatten nested structure", "<collection>"
push => "append element", "<list> <element>"
unique => "dedupe, preserve order", "<list>"
split_at => "split at index, returns [left,right]", "<list> <index>"
split_first => "split head/tail, returns [head,rest]", "<list>"
sort => "sort, optional fn(a,b)->[-1/0/1]. e.g. sort list 'name'", "<list> [fn|±key...]"
group => "group by key fn or map field, e.g. fn(item)->string", "<list> <fn|key> [keep=false]"
remove_at => "remove n items from index", "<list> <index> [count=1]"
remove => "remove item, default first-only", "<list> <item> [all=false]"
set => "set value at existing index", "<list> <index> <value>"
swap => "swap two elements by index", "<list> <i> <j>"
rotate => "rotate, n>0 right, n<0 left", "<list> <n>"
splice => "delete & optionally insert at index, returns new list", "<list> <start> <delete_count> [items...]"
concat => "concat lists/items into one list", "<list1|item1> <list2|item2>..."
from => "list from range", "<range>"
fill => "repeat value n times", "<value> <n>"
map => "apply fn([index],item) per element", "<list> <fn>"
items => "index-value pairs", "<list>"
filter => "filter by fn([index],item)", "<list> <fn>"
filter_map => "filter+map, drop None results", "<list> <fn>"
any => "any element passes?", "<list> <fn>"
all => "all elements pass?", "<list> <fn>"
join => "join strings with separator", "<list> [sep=' ']"
to_map => "to btreeMap, pairs [k,v,k,v...]", "<list> [fn(k,v)]"
to_hmap => "to hashMap, pairs [k,v,k,v...]", "<list> [fn(k,v)]"
to_set => "to btreeSet", "<list>"
transpose => "transpose matrix (list of lists)", "<matrix>"
chunks => "split into chunks of size n", "<list> <size>"
fold => "fold left, fn(acc,item)", "<list> <fn> [init=0]"
rfold => "fold right, fn(acc,item)", "<list> <fn> [init=0]"
zip => "zip two lists into pairs", "<list1> <list2>"
unzip => "unzip pairs into two lists", "<list_of_pairs>"
windows => "overlapping sliding windows of size n", "<list> <size>"
shuffle => "shuffle order", "<list>"
sample => "pick n distinct random elements", "<list> <n>"
})
}
fn max(
args: Vec<Expression>,
env: &mut Environment,
ctx: &Expression,
) -> Result<Expression, RuntimeError> {
if args.len() == 1 {
let mut it = args.into_iter();
let arr = it.next().unwrap();
let list = get_list_ref(&arr, ctx)?;
math_lib::max(list.as_ref().clone(), env, ctx)
} else {
math_lib::max(args, env, ctx)
}
}
fn min(
args: Vec<Expression>,
env: &mut Environment,
ctx: &Expression,
) -> Result<Expression, RuntimeError> {
if args.len() == 1 {
let mut it = args.into_iter();
let arr = it.next().unwrap();
let list = get_list_ref(&arr, ctx)?;
math_lib::min(list.as_ref().clone(), env, ctx)
} else {
math_lib::min(args, env, ctx)
}
}
fn sum(
args: Vec<Expression>,
env: &mut Environment,
ctx: &Expression,
) -> Result<Expression, RuntimeError> {
if args.len() == 1 {
let mut it = args.into_iter();
let arr = it.next().unwrap();
let list = get_list_ref(&arr, ctx)?;
math_lib::sum(list.as_ref().clone(), env, ctx)
} else {
math_lib::sum(args, env, ctx)
}
}
fn average(
args: Vec<Expression>,
env: &mut Environment,
ctx: &Expression,
) -> Result<Expression, RuntimeError> {
if args.len() == 1 {
let mut it = args.into_iter();
let arr = it.next().unwrap();
let list = get_list_ref(&arr, ctx)?;
math_lib::average(list.as_ref().clone(), env, ctx)
} else {
math_lib::average(args, env, ctx)
}
}
fn insert(
args: Vec<Expression>,
_env: &mut Environment,
ctx: &Expression,
) -> Result<Expression, RuntimeError> {
check_exact_args_len("insert", &args, 3, ctx)?;
let mut it = args.into_iter();
let arr = it.next().unwrap();
let list = get_list_ref(&arr, ctx)?;
let idx = it.next().unwrap();
let i = get_integer_arg(idx, ctx)?;
let val = it.next().unwrap();
if i as usize <= list.as_ref().len() {
let mut result = list.as_ref().clone();
result.insert(i as usize, val);
Ok(Expression::from(result))
} else {
Err(RuntimeError::new(
RuntimeErrorKind::CustomError(
format!("index {} out of bounds for insertion", i).into(),
),
ctx.clone(),
0,
))
}
}
fn is_empty(
args: Vec<Expression>,
_env: &mut Environment,
ctx: &Expression,
) -> Result<Expression, RuntimeError> {
check_exact_args_len("is_empty", &args, 1, ctx)?;
let list = get_list_ref(&args[0], ctx)?;
Ok(Expression::Boolean(list.is_empty()))
}
fn first(
args: Vec<Expression>,
_env: &mut Environment,
ctx: &Expression,
) -> Result<Expression, RuntimeError> {
check_args_len("first", &args, 1..=2, ctx)?;
let mut it = args.iter();
let list = get_list_ref(&it.next().unwrap(), ctx)?;
match it.next() {
Some(Expression::Integer(i)) if *i > 1 => {
let r = list
.as_ref()
.iter()
.take(*i as usize)
.cloned()
.collect::<Vec<_>>();
Ok(Expression::from(r))
}
_ => list.as_ref().first().cloned().ok_or_else(|| {
RuntimeError::common("cannot get first of empty list".into(), ctx.clone(), 0)
}),
}
}
fn last(
args: Vec<Expression>,
_env: &mut Environment,
ctx: &Expression,
) -> Result<Expression, RuntimeError> {
check_args_len("last", &args, 1..=2, ctx)?;
let mut it = args.iter();
let list = get_list_ref(&it.next().unwrap(), ctx)?;
match it.next() {
Some(Expression::Integer(i)) if *i > 1 => {
let r = list
.as_ref()
.iter()
.rev()
.take(*i as usize)
.rev()
.cloned()
.collect::<Vec<_>>();
Ok(Expression::from(r))
}
_ => list.as_ref().first().cloned().ok_or_else(|| {
RuntimeError::common("cannot get first of empty list".into(), ctx.clone(), 0)
}),
}
}
pub fn clamp(n: Int, len: usize) -> usize {
if n < 0 {
len + n as usize
} else {
(n as usize).min(len)
}
}
fn get(
args: Vec<Expression>,
_env: &mut Environment,
ctx: &Expression,
) -> Result<Expression, RuntimeError> {
check_exact_args_len("get", &args, 2, ctx)?;
let list = get_list_ref(&args[0], ctx)?;
let n = get_integer_ref(&args[1], ctx)?;
let index = clamp(n, list.len());
list.get(index).cloned().ok_or(RuntimeError::new(
RuntimeErrorKind::IndexOutOfBounds {
index: n,
len: list.as_ref().len(),
},
ctx.clone(),
0,
))
}
fn take(
args: Vec<Expression>,
_env: &mut Environment,
ctx: &Expression,
) -> Result<Expression, RuntimeError> {
check_exact_args_len("take", &args, 2, ctx)?;
let list = get_list_ref(&args[0], ctx)?;
let n = get_integer_ref(&args[1], ctx)?;
let count = clamp(n, list.len());
Ok(Expression::List(Rc::new(
list.as_ref().iter().take(count).cloned().collect(),
)))
}
fn skip(
args: Vec<Expression>,
_env: &mut Environment,
ctx: &Expression,
) -> Result<Expression, RuntimeError> {
check_exact_args_len("skip", &args, 2, ctx)?;
let list = get_list_ref(&args[0], ctx)?;
let n = get_integer_ref(&args[1], ctx)?;
let count = clamp(n, list.len());
Ok(Expression::List(Rc::new(
list.as_ref().iter().skip(count).cloned().collect(),
)))
}
fn contains(
args: Vec<Expression>,
_env: &mut Environment,
ctx: &Expression,
) -> Result<Expression, RuntimeError> {
check_exact_args_len("contains", &args, 2, ctx)?;
let list = get_list_ref(&args[0], ctx)?;
Ok(Expression::Boolean(list.as_ref().contains(&args[1])))
}
fn find(
args: Vec<Expression>,
env: &mut Environment,
ctx: &Expression,
) -> Result<Expression, RuntimeError> {
check_args_len("find", &args, 2..=3, ctx)?;
let mut it = args.into_iter();
let list_exp = it.next().unwrap();
let target = it.next().unwrap();
let list = get_list_ref(&list_exp, ctx)?;
let start = if let Some(start_expr) = it.next() {
get_integer_ref(&start_expr, ctx)? as usize
} else {
0
};
match &target {
Expression::Function(..) | Expression::Lambda(..) => {
let state = &mut State::new();
for item in list.as_ref().iter().skip(start) {
let r = target.eval_apply(&target, std::slice::from_ref(item), state, env, 0)?;
if let Expression::Boolean(true) = r {
return Ok(item.clone());
}
}
Ok(Expression::None)
}
_ => Ok(list
.as_ref()
.iter()
.skip(start)
.find(|x| *x == &target)
.cloned()
.unwrap_or(Expression::None)),
}
}
fn position(
args: Vec<Expression>,
env: &mut Environment,
ctx: &Expression,
) -> Result<Expression, RuntimeError> {
check_args_len("position", &args, 2..=3, ctx)?;
let mut it = args.into_iter();
let list_exp = it.next().unwrap();
let target = it.next().unwrap();
let list = get_list_ref(&list_exp, ctx)?;
let start = if let Some(start_expr) = it.next() {
get_integer_ref(&start_expr, ctx)? as usize
} else {
0
};
match &target {
Expression::Function(..) | Expression::Lambda(..) => {
let state = &mut State::new();
for (i, item) in list.as_ref().iter().enumerate().skip(start) {
let r = target.eval_apply(&target, std::slice::from_ref(item), state, env, 0)?;
if let Expression::Boolean(true) = r {
return Ok(Expression::Integer(i as Int));
}
}
Ok(Expression::None)
}
_ => Ok(
match list.as_ref().iter().skip(start).position(|x| x == &target) {
Some(index) => Expression::Integer(index as Int),
None => Expression::None,
},
),
}
}
fn rfind(
args: Vec<Expression>,
env: &mut Environment,
ctx: &Expression,
) -> Result<Expression, RuntimeError> {
check_args_len("rfind", &args, 2..=3, ctx)?;
let mut it = args.into_iter();
let list_exp = it.next().unwrap();
let target = it.next().unwrap();
let list = get_list_ref(&list_exp, ctx)?;
let start = if let Some(start_expr) = it.next() {
get_integer_ref(&start_expr, ctx)? as usize
} else {
0
};
match target {
Expression::Function(..) | Expression::Lambda(..) => {
let state = &mut State::new();
let target_rc = target;
for item in list.as_ref().iter().rev().skip(start) {
let r =
target_rc.eval_apply(&target_rc, std::slice::from_ref(item), state, env, 0)?;
if let Expression::Boolean(true) = r {
return Ok(item.clone());
}
}
Ok(Expression::None)
}
_ => Ok(list
.as_ref()
.iter()
.rev()
.skip(start)
.find(|x| *x == &target)
.cloned()
.unwrap_or(Expression::None)),
}
}
fn rposition(
args: Vec<Expression>,
env: &mut Environment,
ctx: &Expression,
) -> Result<Expression, RuntimeError> {
check_args_len("rposition", &args, 2..=3, ctx)?;
let mut it = args.into_iter();
let list_exp = it.next().unwrap();
let target = it.next().unwrap();
let list = get_list_ref(&list_exp, ctx)?;
let start = if let Some(start_expr) = it.next() {
get_integer_ref(&start_expr, ctx)? as usize
} else {
0
};
match target {
Expression::Function(..) | Expression::Lambda(..) => {
let state = &mut State::new();
let target_rc = target;
for (i, item) in list.as_ref().iter().enumerate().rev().skip(start) {
let r =
target_rc.eval_apply(&target_rc, std::slice::from_ref(item), state, env, 0)?;
if let Expression::Boolean(true) = r {
return Ok(Expression::Integer(i as Int));
}
}
Ok(Expression::None)
}
_ => Ok(
match list
.as_ref()
.iter()
.rev()
.skip(start)
.position(|x| x == &target)
{
Some(index) => Expression::Integer((list.as_ref().len() - 1 - index) as Int),
None => Expression::None,
},
),
}
}
fn push(
args: Vec<Expression>,
_env: &mut Environment,
ctx: &Expression,
) -> Result<Expression, RuntimeError> {
check_exact_args_len("push", &args, 2, ctx)?;
let mut it = args.into_iter();
let list_exp = it.next().unwrap();
let item = it.next().unwrap();
let list = get_list_ref(&list_exp, ctx)?;
let mut new_list = list.as_ref().to_vec();
new_list.push(item);
Ok(Expression::List(Rc::new(new_list)))
}
fn unique(
args: Vec<Expression>,
_env: &mut Environment,
ctx: &Expression,
) -> Result<Expression, RuntimeError> {
check_exact_args_len("unique", &args, 1, ctx)?;
let list = get_list_ref(&args[0], ctx)?;
let mut seen = std::collections::HashSet::new();
let mut result = Vec::new();
for item in list.as_ref().iter() {
if seen.insert(item.to_string()) {
result.push(item.clone());
}
}
Ok(Expression::List(Rc::new(result)))
}
fn split_at(
args: Vec<Expression>,
_env: &mut Environment,
ctx: &Expression,
) -> Result<Expression, RuntimeError> {
check_exact_args_len("split_at", &args, 2, ctx)?;
let list = get_list_ref(&args[0], ctx)?;
let n = get_integer_ref(&args[1], ctx)?;
let index = if n < 0 {
(list.as_ref().len() as Int + n).max(0) as usize
} else {
(n as usize).min(list.as_ref().len())
};
let (first, second) = list.as_ref().split_at(index);
Ok(Expression::List(Rc::new(vec![
Expression::List(Rc::new(first.to_vec())),
Expression::List(Rc::new(second.to_vec())),
])))
}
fn split_first(
args: Vec<Expression>,
_env: &mut Environment,
ctx: &Expression,
) -> Result<Expression, RuntimeError> {
check_exact_args_len("shift", &args, 1, ctx)?;
let list = get_list_ref(&args[0], ctx)?;
match list.as_ref().split_first() {
Some((first, rest)) => Ok(Expression::List(Rc::new(vec![
first.clone(),
Expression::List(Rc::new(rest.to_vec())),
]))),
None => Ok(Expression::None),
}
}
pub enum SortKey {
Field(String, bool),
Index(usize, bool),
Whole(bool),
}
impl SortKey {
pub fn parse(expr: &Expression, ctx: &Expression) -> Result<Self, RuntimeError> {
match expr {
Expression::Integer(i) => Ok(SortKey::Index(i.unsigned_abs() as usize, i >= &0)),
Expression::Symbol(s) | Expression::String(s) => match s.as_str() {
"+" => Ok(SortKey::Whole(true)),
"-" => Ok(SortKey::Whole(false)),
_ => {
let mut chars = s.chars();
match chars.next() {
Some('+') => Ok(SortKey::Field(chars.as_str().to_string(), true)),
Some('-') => Ok(SortKey::Field(chars.as_str().to_string(), false)),
_ => Ok(SortKey::Field(s.clone(), true)),
}
}
},
e => Err(RuntimeError::new(
RuntimeErrorKind::TypeError {
expected: "String('+field'/'-field'/'+'/'-') or Integer as sort key".into(),
sym: e.to_string(),
found: e.type_name(),
},
ctx.clone(),
0,
)),
}
}
fn extract<'a>(&self, elem: &'a Expression) -> &'a Expression {
match self {
SortKey::Whole(_) => elem,
SortKey::Field(name, _) => match elem {
Expression::Map(m) => m.get(name).unwrap_or(&Expression::None),
Expression::HMap(m) => m.get(name).unwrap_or(&Expression::None),
_ => &Expression::None,
},
SortKey::Index(idx, _) => match elem {
Expression::List(l) => l.get(*idx).unwrap_or(&Expression::None),
Expression::BSet(l) => l.iter().nth(*idx).unwrap_or(&Expression::None),
_ => &Expression::None,
},
}
}
fn is_asc(&self) -> bool {
match self {
SortKey::Whole(a) | SortKey::Field(_, a) | SortKey::Index(_, a) => *a,
}
}
pub fn get_field(self) -> Option<String> {
match self {
SortKey::Field(f, _) => Some(f),
_ => None,
}
}
}
fn compare_by_keys(keys: &[SortKey], a: &Expression, b: &Expression) -> Ordering {
for key in keys {
let va = key.extract(a);
let vb = key.extract(b);
let mut ord = va.cmp(vb);
if !key.is_asc() {
ord = ord.reverse();
}
if ord != Ordering::Equal {
return ord;
}
}
Ordering::Equal
}
pub fn sort(
args: Vec<Expression>,
env: &mut Environment,
ctx: &Expression,
) -> Result<Expression, RuntimeError> {
check_args_len("sort", &args, 1.., ctx)?;
if let Some((list, ops)) = args.split_first() {
let target = get_list_ref(list, ctx)?.as_ref().clone();
let ops = ops.to_vec();
let r = sort_vec(target, ops, env, ctx)?;
return Ok(Expression::List(Rc::new(r)));
}
Ok(Expression::None)
}
pub fn sort_vec(
mut sorted: Vec<Expression>,
ops: Vec<Expression>,
env: &mut Environment,
ctx: &Expression,
) -> Result<Vec<Expression>, RuntimeError> {
let (func, keys) = match ops.len() {
0 => (None, None),
1 => {
let key_arg = ops.into_iter().next().unwrap();
match key_arg {
Expression::Lambda(..) | Expression::Function(..) => (Some(Rc::new(key_arg)), None),
Expression::List(items) => {
let keys = items
.as_ref()
.iter()
.map(|e| SortKey::parse(e, ctx))
.collect::<Result<Vec<_>, _>>()?;
(None, Some(keys))
}
other => (None, Some(vec![SortKey::parse(&other, ctx)?])),
}
}
_ => {
let keys = ops
.iter()
.map(|e| SortKey::parse(e, ctx))
.collect::<Result<Vec<_>, _>>()?;
(None, Some(keys))
}
};
if let Some(sort_func) = func {
sorted.sort_by(|a, b| {
let sort_result = Expression::Apply(
Rc::new((*sort_func).clone()),
Rc::new(vec![a.clone(), b.clone()]),
)
.eval(env);
match sort_result {
Ok(Expression::Integer(i)) => match i {
1.. => Ordering::Greater,
0 => Ordering::Equal,
..0 => Ordering::Less,
},
Ok(Expression::Boolean(b)) => match b {
true => Ordering::Greater,
false => Ordering::Less,
},
_ => Ordering::Equal,
}
});
} else if let Some(keys) = keys {
if matches!(
sorted.first(),
Some(Expression::String(_)) | Some(Expression::Symbol(_))
) && keys.iter().any(|k| !matches!(k, SortKey::Whole(_)))
{
return Err(RuntimeError::common(
"sorting a list of strings only supports '+'/'-' as sort key, not field name or index"
.to_string()
.into(),
ctx.clone(),
0,
));
}
sorted.sort_by(|a, b| compare_by_keys(&keys, a, b));
} else {
sorted.sort_by(|a, b| a.cmp(b));
}
Ok(sorted)
}
fn group(
args: Vec<Expression>,
env: &mut Environment,
ctx: &Expression,
) -> Result<Expression, RuntimeError> {
check_args_len("group", &args, 2..=3, ctx)?;
let mut it = args.into_iter();
let list_expr = it.next().unwrap();
let list = get_list_ref(&list_expr, ctx)?;
let key_func = it.next().unwrap();
let keep_group_field = it.next().is_some_and(|x| x.is_truthy());
let mut groups: BTreeMap<String, Vec<Expression>> = BTreeMap::new();
match key_func {
Expression::Lambda(..) | Expression::Function(..) => {
let state = &mut State::new();
for item in list.as_ref().iter() {
let r =
key_func.eval_apply(&key_func, std::slice::from_ref(item), state, env, 0)?;
let key = match r {
Expression::String(s) => s,
other => other.to_string(),
};
groups.entry(key).or_default().push(item.clone());
}
}
Expression::Symbol(k) | Expression::String(k) => {
for item in list.as_ref().iter() {
let kk: &str = &k;
let keyitem = match item {
Expression::Map(m) => m.get(kk),
Expression::HMap(m) => m.get(kk),
_ => {
return Err(RuntimeError::common(
"group by key can only apply to a map".to_string().into(),
ctx.clone(),
0,
));
}
};
if let Some(key) = keyitem {
let new_item = if keep_group_field {
item.clone()
} else {
match item {
Expression::Map(m) => {
let n = m
.iter()
.filter(|(k, _)| *k != &kk)
.map(|(k, v)| (k.clone(), v.clone()))
.collect::<BTreeMap<_, _>>();
Expression::from(n)
}
Expression::HMap(m) => {
let n = m
.iter()
.filter(|(k, _)| *k != &kk)
.map(|(k, v)| (k.clone(), v.clone()))
.collect::<BTreeMap<_, _>>();
Expression::from(n)
}
_ => {
return Err(RuntimeError::common(
"group by key can only apply to a map".to_string().into(),
ctx.clone(),
0,
));
}
}
};
groups.entry(key.to_string()).or_default().push(new_item);
} else {
return Err(RuntimeError::common(
format!("no such key found in map: `{k}`").into(),
ctx.clone(),
0,
));
}
}
}
_ => {
return Err(RuntimeError::common(
"group requires key-func or key".to_string().into(),
ctx.clone(),
0,
));
}
};
Ok(Expression::from(groups))
}
fn remove_at(
args: Vec<Expression>,
_env: &mut Environment,
ctx: &Expression,
) -> Result<Expression, RuntimeError> {
check_args_len("remove_at", &args, 2..=3, ctx)?;
let mut iter = args.into_iter();
let list_expr = iter.next().unwrap();
let index_expr = iter.next().unwrap();
let list = get_list_ref(&list_expr, ctx)?;
let index = get_integer_ref(&index_expr, ctx)?;
let count = if let Some(c) = iter.next() {
get_integer_ref(&c, ctx)?
} else {
1
};
if count <= 0 {
return Ok(list_expr);
}
let list_len = list.as_ref().len() as Int;
let start_idx = if index < 0 {
(list_len + index).max(0) as usize
} else {
(index as usize).min(list_len as usize)
};
let end_idx = (start_idx + count as usize).min(list_len as usize);
if start_idx >= list_len as usize {
return Ok(list_expr);
}
let mut new_list = Vec::new();
new_list.extend(list.as_ref().iter().take(start_idx).cloned());
new_list.extend(list.as_ref().iter().skip(end_idx).cloned());
Ok(Expression::List(Rc::new(new_list)))
}
fn remove(
args: Vec<Expression>,
_env: &mut Environment,
ctx: &Expression,
) -> Result<Expression, RuntimeError> {
check_args_len("remove", &args, 2..=3, ctx)?;
let mut it = args.into_iter();
let list_exp = it.next().unwrap();
let target = it.next().unwrap();
let list = get_list_ref(&list_exp, ctx)?;
let all = if let Some(Expression::Boolean(b)) = it.next() {
b
} else {
false
};
if all {
let new_list = list
.iter()
.filter(|x| *x != &target)
.cloned()
.collect::<Vec<_>>();
Ok(Expression::from(new_list))
} else if let Some(pos) = list.iter().position(|x| x == &target) {
let mut new_list = list.as_ref().clone();
new_list.remove(pos);
Ok(Expression::from(new_list))
} else {
Ok(Expression::List(list.clone()))
}
}
fn set(
args: Vec<Expression>,
_env: &mut Environment,
ctx: &Expression,
) -> Result<Expression, RuntimeError> {
check_exact_args_len("set", &args, 3, ctx)?;
let mut it = args.into_iter();
let list_exp = it.next().unwrap();
let pos = it.next().unwrap();
let target = it.next().unwrap();
let list = get_list_ref(&list_exp, ctx)?;
let n = get_integer_ref(&pos, ctx)?;
let index = n as usize;
if index < list.as_ref().len() {
let mut result = list.as_ref().clone();
result[index] = target;
Ok(Expression::from(result))
} else {
Err(RuntimeError::common(
format!(
"index {} out of bounds for list of length {}",
n,
list.as_ref().len()
)
.into(),
ctx.clone(),
0,
))
}
}
fn slice(
args: Vec<Expression>,
_env: &mut Environment,
ctx: &Expression,
) -> Result<Expression, RuntimeError> {
check_exact_args_len("slice", &args, 3, ctx)?;
let list = get_list_ref(&args[0], ctx)?;
let start_n = get_integer_ref(&args[1], ctx)?;
let end_n = get_integer_ref(&args[2], ctx)?;
let len = list.as_ref().len();
let start = clamp(start_n, len);
let end = clamp(end_n, len);
if start >= end {
return Ok(Expression::List(Rc::new(vec![])));
}
Ok(Expression::List(Rc::new(
list.as_ref()[start..end].to_vec(),
)))
}
fn concat(
args: Vec<Expression>,
_env: &mut Environment,
_ctx: &Expression,
) -> Result<Expression, RuntimeError> {
Ok(Expression::List(Rc::new(args)))
}
fn from(
args: Vec<Expression>,
_env: &mut Environment,
ctx: &Expression,
) -> Result<Expression, RuntimeError> {
match args.len() {
0 => Err(RuntimeError::common(
"requires a range (a..b) or some elements as arguments"
.to_string()
.into(),
ctx.clone(),
0,
)),
1 => match args.into_iter().next().unwrap() {
Expression::Range(r, step) => {
Ok(Expression::from(r.step_by(step).collect::<Vec<Int>>()))
}
_ => Err(RuntimeError::common(
"the only arg should be a range (a..b)".to_string().into(),
ctx.clone(),
0,
)),
},
2.. => Ok(Expression::from(args)),
}
}
fn map(
args: Vec<Expression>,
env: &mut Environment,
ctx: &Expression,
) -> Result<Expression, RuntimeError> {
check_exact_args_len("map", &args, 2, ctx)?;
let mut it = args.into_iter();
let list_exp = it.next().unwrap();
let func = it.next().unwrap();
let list = get_list_ref(&list_exp, ctx)?;
let (ind_name, var_name, body) = if check_fn_arg(&func, 2, ctx).is_ok() {
match func {
Expression::Function(_, mut p, _, body, _) => {
(Some(p.pop().unwrap().0), Some(p.pop().unwrap().0), body)
}
Expression::Lambda(mut p, body, _) => (p.pop(), p.pop(), body),
_ => unreachable!(),
}
} else if check_fn_arg(&func, 1, ctx).is_ok() {
match func {
Expression::Function(_, mut p, _, body, _) => (None, Some(p.pop().unwrap().0), body),
Expression::Lambda(mut p, body, _) => (None, p.pop(), body),
_ => unreachable!(),
}
} else {
return Err(RuntimeError::common(
("your func/lambda should receive 1..=2 param").into(),
ctx.clone(),
0,
));
};
let mut state = State::new();
state.set(State::IN_ASSIGN);
let count = list.iter().count();
let iterator = BoxedIterator::Vec(list.as_ref().clone().into_iter());
execute_iteration(
var_name.unwrap(),
ind_name,
iterator,
count,
body.as_ref(),
&mut state,
env,
0,
)
}
fn items(
args: Vec<Expression>,
_env: &mut Environment,
ctx: &Expression,
) -> Result<Expression, RuntimeError> {
check_exact_args_len("items", &args, 1, ctx)?;
let list = get_list_ref(&args[0], ctx)?;
let items = list
.as_ref()
.iter()
.enumerate()
.map(|(i, v)| Expression::from(vec![(i as Int).into(), v.clone()]))
.collect();
Ok(Expression::List(Rc::new(items)))
}
fn filter(
args: Vec<Expression>,
env: &mut Environment,
ctx: &Expression,
) -> Result<Expression, RuntimeError> {
check_exact_args_len("filter", &args, 2, ctx)?;
let mut it = args.into_iter();
let list_exp = it.next().unwrap();
let target = it.next().unwrap();
let mut result = Vec::new();
let fn_arg_count = match &target {
Expression::Lambda(params, ..) => params.len(),
Expression::Function(_, params, _, _, _) => params.len(),
_ => {
return Err(RuntimeError::common(
"expected a func/lambda as filter-function".into(),
ctx.clone(),
0,
));
}
};
let list = get_list_ref(&list_exp, ctx)?;
let state = &mut State::new();
match fn_arg_count {
1 => {
for item in list.as_ref() {
let r = target.eval_apply(&target, std::slice::from_ref(item), state, env, 0)?;
if let Expression::Boolean(true) = r {
result.push(item.clone());
}
}
}
2 => {
for (i, item) in list.as_ref().iter().enumerate() {
let r = target.eval_apply(
&target,
&[Expression::Integer(i as i64), item.clone()],
state,
env,
0,
)?;
if let Expression::Boolean(true) = r {
result.push(item.clone());
}
}
}
_ => {
return Err(RuntimeError::common(
"expected 1..=2 params for filter-function".into(),
ctx.clone(),
0,
));
}
}
Ok(Expression::List(Rc::new(result)))
}
fn filter_map(
args: Vec<Expression>,
env: &mut Environment,
ctx: &Expression,
) -> Result<Expression, RuntimeError> {
check_exact_args_len("filter_map", &args, 2, ctx)?;
let mut it = args.into_iter();
let list_exp = it.next().unwrap();
let func = it.next().unwrap();
let list = get_list_ref(&list_exp, ctx)?;
check_fn_arg(&func, 1, ctx)?;
let mut result = Vec::new();
let state = &mut State::new();
for item in list.as_ref().iter() {
let r = func.eval_apply(&func, std::slice::from_ref(item), state, env, 0)?;
match r {
Expression::None => continue,
val => result.push(val),
}
}
Ok(Expression::List(Rc::new(result)))
}
fn any(
args: Vec<Expression>,
env: &mut Environment,
ctx: &Expression,
) -> Result<Expression, RuntimeError> {
check_exact_args_len("any", &args, 2, ctx)?;
let mut it = args.into_iter();
let list_exp = it.next().unwrap();
let func = it.next().unwrap();
let list = get_list_ref(&list_exp, ctx)?;
check_fn_arg(&func, 1, ctx)?;
let state = &mut State::new();
for item in list.as_ref().iter() {
let r = func.eval_apply(&func, std::slice::from_ref(item), state, env, 0);
if let Ok(Expression::Boolean(true)) = &r {
return r;
}
}
Ok(Expression::Boolean(false))
}
fn all(
args: Vec<Expression>,
env: &mut Environment,
ctx: &Expression,
) -> Result<Expression, RuntimeError> {
check_exact_args_len("all", &args, 2, ctx)?;
let mut it = args.into_iter();
let list_exp = it.next().unwrap();
let func = it.next().unwrap();
let list = get_list_ref(&list_exp, ctx)?;
check_fn_arg(&func, 1, ctx)?;
let state = &mut State::new();
for item in list.as_ref().iter() {
let r = func.eval_apply(&func, std::slice::from_ref(item), state, env, 0);
if let Ok(Expression::Boolean(false)) = &r {
return r;
}
}
Ok(Expression::Boolean(true))
}
fn join(
args: Vec<Expression>,
_env: &mut Environment,
ctx: &Expression,
) -> Result<Expression, RuntimeError> {
check_args_len("join", &args, 1..=2, ctx)?;
let list = get_list_ref(&args[0], ctx)?;
let separator = if args.len() > 1 {
get_string_ref(&args[1], ctx)?
} else {
" "
};
let mut joined = String::new();
for (i, item) in list.as_ref().iter().enumerate() {
if i != 0 {
joined.push_str(separator);
}
joined.push_str(&item.to_string());
}
Ok(Expression::String(joined))
}
fn to_map(
args: Vec<Expression>,
env: &mut Environment,
ctx: &Expression,
) -> Result<Expression, RuntimeError> {
check_args_len("to_map", &args, 1..=2, ctx)?;
let mut it = args.into_iter();
let list_exp = it.next().unwrap();
let fn_o = it.next().map(Rc::new);
let list = get_list_ref(&list_exp, ctx)?;
if fn_o.is_none() {
let mut map = BTreeMap::new();
for pair in list.as_ref().chunks(2) {
match pair {
[k, v] => {
map.insert(k.to_string(), v.clone());
}
[k] => {
return Err(RuntimeError::common(
format!(
"to_map: odd number of elements, key '{}' has no matching value",
k
)
.into(),
ctx.clone(),
0,
));
}
_ => unreachable!(),
}
}
return Ok(Expression::from(map));
}
let f = fn_o.unwrap();
let mut map = BTreeMap::new();
let state = &mut State::new();
for item in list.as_ref().iter() {
let r = f.eval_apply(&f, std::slice::from_ref(item), state, env, 0)?;
match r {
Expression::List(pair) if pair.as_ref().len() == 2 => {
let key = match &pair.as_ref()[0] {
Expression::String(s) => s.clone(),
other => other.to_string(),
};
let value = pair.as_ref()[1].clone();
map.insert(key, value);
}
Expression::Map(nm) => map.extend(nm.as_ref().clone()),
Expression::HMap(nm) => map.extend(nm.as_ref().clone()),
other => {
return Err(RuntimeError::common(
format!(
"to_map: callback must return a [key, value] pair, got: {}",
other
)
.into(),
ctx.clone(),
0,
));
}
}
}
Ok(Expression::from(map))
}
fn to_hmap(
args: Vec<Expression>,
env: &mut Environment,
ctx: &Expression,
) -> Result<Expression, RuntimeError> {
check_args_len("to_hmap", &args, 1..=2, ctx)?;
let mut it = args.into_iter();
let list_exp = it.next().unwrap();
let fn_o = it.next().map(Rc::new);
let list = get_list_ref(&list_exp, ctx)?;
if fn_o.is_none() {
let mut map = HashMap::new();
for pair in list.as_ref().chunks(2) {
match pair {
[k, v] => {
map.insert(k.to_string(), v.clone());
}
[k] => {
return Err(RuntimeError::common(
format!(
"to_hmap: odd number of elements, key '{}' has no matching value",
k
)
.into(),
ctx.clone(),
0,
));
}
_ => unreachable!(),
}
}
return Ok(Expression::from(map));
}
let f = fn_o.unwrap();
let mut map = HashMap::new();
let state = &mut State::new();
for item in list.as_ref().iter() {
let r = f.eval_apply(&f, std::slice::from_ref(item), state, env, 0)?;
match r {
Expression::List(pair) if pair.as_ref().len() == 2 => {
let key = match &pair.as_ref()[0] {
Expression::String(s) => s.clone(),
other => other.to_string(),
};
let value = pair.as_ref()[1].clone();
map.insert(key, value);
}
Expression::Map(nm) => map.extend(nm.as_ref().clone()),
Expression::HMap(nm) => map.extend(nm.as_ref().clone()),
other => {
return Err(RuntimeError::common(
format!(
"to_hmap: callback must return a [key, value] pair, got: {}",
other
)
.into(),
ctx.clone(),
0,
));
}
}
}
Ok(Expression::from(map))
}
fn to_set(
args: Vec<Expression>,
_env: &mut Environment,
ctx: &Expression,
) -> Result<Expression, RuntimeError> {
check_exact_args_len("to_set", &args, 1, ctx)?;
let list = get_list_ref(&args[0], ctx)?;
let set: BTreeSet<_> = list.iter().cloned().collect();
Ok(Expression::BSet(Rc::new(set)))
}
fn transpose(
args: Vec<Expression>,
_env: &mut Environment,
ctx: &Expression,
) -> Result<Expression, RuntimeError> {
check_exact_args_len("transpose", &args, 1, ctx)?;
let matrix = get_list_ref(&args[0], ctx)?;
if matrix.as_ref().is_empty() {
return Ok(Expression::List(Rc::new(vec![])));
}
let row_len = match matrix.as_ref().first() {
Some(Expression::List(row)) => row.as_ref().len(),
_ => {
return Err(RuntimeError::common(
"transpose requires list of lists as argument".into(),
ctx.clone(),
0,
));
}
};
for row in matrix.as_ref().iter() {
if let Expression::List(r) = row {
if r.as_ref().len() != row_len {
return Err(RuntimeError::common(
"all rows must have the same length".into(),
ctx.clone(),
0,
));
}
} else {
return Err(RuntimeError::common(
"transpose requires list of lists as argument".into(),
ctx.clone(),
0,
));
}
}
let mut transposed = Vec::with_capacity(row_len);
for i in 0..row_len {
let mut new_row = Vec::with_capacity(matrix.as_ref().len());
for row in matrix.as_ref().iter() {
if let Expression::List(r) = row {
new_row.push(r.as_ref()[i].clone());
}
}
transposed.push(Expression::List(Rc::new(new_row)));
}
Ok(Expression::List(Rc::new(transposed)))
}
fn chunks(
args: Vec<Expression>,
_env: &mut Environment,
ctx: &Expression,
) -> Result<Expression, RuntimeError> {
check_exact_args_len("chunks", &args, 2, ctx)?;
let list = get_list_ref(&args[0], ctx)?;
let n = get_integer_ref(&args[1], ctx)?;
let mut result = Vec::new();
let mut chunk = Vec::new();
for item in list.as_ref().iter() {
chunk.push(item.clone());
if chunk.len() == n as usize {
result.push(Expression::List(Rc::new(chunk)));
chunk = Vec::new();
}
}
if !chunk.is_empty() {
result.push(Expression::List(Rc::new(chunk)));
}
Ok(Expression::List(Rc::new(result)))
}
fn fold(
args: Vec<Expression>,
env: &mut Environment,
ctx: &Expression,
) -> Result<Expression, RuntimeError> {
check_args_len("fold", &args, 2..=3, ctx)?;
let mut it = args.into_iter();
let list_exp = it.next().unwrap();
let func = it.next().unwrap();
let list = get_list_ref(&list_exp, ctx)?;
let mut acc = it.next().unwrap_or(Expression::Integer(0));
check_fn_arg(&func, 2, ctx)?;
let state = &mut State::new();
for item in list.as_ref().iter() {
acc = func.eval_apply(&func, &[acc, item.clone()], state, env, 0)?;
}
Ok(acc)
}
fn rfold(
args: Vec<Expression>,
env: &mut Environment,
ctx: &Expression,
) -> Result<Expression, RuntimeError> {
check_args_len("rfold", &args, 2..=3, ctx)?;
let mut it = args.into_iter();
let list_exp = it.next().unwrap();
let func = Rc::new(it.next().unwrap());
let list = get_list_ref(&list_exp, ctx)?;
let mut acc = it.next().unwrap_or(Expression::Integer(0));
check_fn_arg(&func, 2, ctx)?;
let state = &mut State::new();
for item in list.as_ref().iter().rev() {
acc = func.eval_apply(&func, &[acc, item.clone()], state, env, 0)?;
}
Ok(acc)
}
fn zip(
args: Vec<Expression>,
_env: &mut Environment,
ctx: &Expression,
) -> Result<Expression, RuntimeError> {
check_exact_args_len("zip", &args, 2, ctx)?;
let list1 = get_list_ref(&args[0], ctx)?;
let list2 = get_list_ref(&args[1], ctx)?;
let mut result = Vec::with_capacity(list1.as_ref().len().min(list2.as_ref().len()));
for (item1, item2) in list1
.as_ref()
.iter()
.cloned()
.zip(list2.as_ref().iter().cloned())
{
result.push(Expression::List(Rc::new(vec![item1, item2])));
}
Ok(Expression::List(Rc::new(result)))
}
fn unzip(
args: Vec<Expression>,
_env: &mut Environment,
ctx: &Expression,
) -> Result<Expression, RuntimeError> {
check_exact_args_len("unzip", &args, 1, ctx)?;
let list = get_list_ref(&args[0], ctx)?;
let mut list1 = Vec::with_capacity(list.as_ref().len());
let mut list2 = Vec::with_capacity(list.as_ref().len());
for item in list.as_ref().iter() {
if let Expression::List(pair) = item {
if pair.as_ref().len() != 2 {
return Err(RuntimeError::common(
"unzip requires list of pairs".into(),
ctx.clone(),
0,
));
}
list1.push(pair.as_ref()[0].clone());
list2.push(pair.as_ref()[1].clone());
} else {
return Err(RuntimeError::common(
"unzip requires list of pairs".into(),
ctx.clone(),
0,
));
}
}
Ok(Expression::List(Rc::new(vec![
Expression::List(Rc::new(list1)),
Expression::List(Rc::new(list2)),
])))
}
fn swap(
args: Vec<Expression>,
_env: &mut Environment,
ctx: &Expression,
) -> Result<Expression, RuntimeError> {
check_exact_args_len("swap", &args, 3, ctx)?;
let list = get_list_ref(&args[0], ctx)?;
let i = get_integer_ref(&args[1], ctx)?;
let j = get_integer_ref(&args[2], ctx)?;
let len = list.as_ref().len();
let idx_i = clamp(i, len);
let idx_j = clamp(j, len);
if idx_i >= len || idx_j >= len {
return Err(RuntimeError::common(
format!("swap index out of bounds for list of length {}", len).into(),
ctx.clone(),
0,
));
}
let mut new_list = list.as_ref().clone();
new_list.swap(idx_i, idx_j);
Ok(Expression::List(Rc::new(new_list)))
}
fn rotate(
args: Vec<Expression>,
_env: &mut Environment,
ctx: &Expression,
) -> Result<Expression, RuntimeError> {
check_exact_args_len("rotate", &args, 2, ctx)?;
let list = get_list_ref(&args[0], ctx)?;
let n = get_integer_ref(&args[1], ctx)?;
let len = list.as_ref().len();
if len == 0 {
return Ok(Expression::List(Rc::new(vec![])));
}
let shift = ((n % len as Int + len as Int) % len as Int) as usize;
let mut new_list = list.as_ref().clone();
new_list.rotate_right(shift);
Ok(Expression::List(Rc::new(new_list)))
}
fn shuffle(
args: Vec<Expression>,
_env: &mut Environment,
ctx: &Expression,
) -> Result<Expression, RuntimeError> {
check_exact_args_len("shuffle", &args, 1, ctx)?;
let list = get_list_ref(&args[0], ctx)?;
let mut new_list = list.as_ref().clone();
let mut rng = rand::rng();
new_list.shuffle(&mut rng);
Ok(Expression::List(Rc::new(new_list)))
}
fn sample(
args: Vec<Expression>,
_env: &mut Environment,
ctx: &Expression,
) -> Result<Expression, RuntimeError> {
check_exact_args_len("sample", &args, 2, ctx)?;
let list = get_list_ref(&args[0], ctx)?;
let n = get_integer_ref(&args[1], ctx)?.max(0) as usize;
let mut rng = rand::rng();
let sampled: Vec<Expression> = list.as_ref().sample(&mut rng, n).cloned().collect();
Ok(Expression::List(Rc::new(sampled)))
}
fn windows(
args: Vec<Expression>,
_env: &mut Environment,
ctx: &Expression,
) -> Result<Expression, RuntimeError> {
check_exact_args_len("windows", &args, 2, ctx)?;
let list = get_list_ref(&args[0], ctx)?;
let size = get_integer_ref(&args[1], ctx)?;
if size <= 0 {
return Err(RuntimeError::common(
"windows size must be positive".into(),
ctx.clone(),
0,
));
}
let size = size as usize;
if size > list.as_ref().len() {
return Ok(Expression::List(Rc::new(vec![])));
}
let result = list
.as_ref()
.windows(size)
.map(|w| Expression::List(Rc::new(w.to_vec())))
.collect::<Vec<_>>();
Ok(Expression::List(Rc::new(result)))
}
fn fill(
args: Vec<Expression>,
_env: &mut Environment,
ctx: &Expression,
) -> Result<Expression, RuntimeError> {
check_exact_args_len("fill", &args, 2, ctx)?;
let value = args[0].clone();
let n = get_integer_ref(&args[1], ctx)?.max(0) as usize;
Ok(Expression::List(Rc::new(vec![value; n])))
}
fn splice(
args: Vec<Expression>,
_env: &mut Environment,
ctx: &Expression,
) -> Result<Expression, RuntimeError> {
check_args_len("splice", &args, 3.., ctx)?;
let mut it = args.into_iter();
let list_exp = it.next().unwrap();
let start_expr = it.next().unwrap();
let delete_expr = it.next().unwrap();
let items: Vec<Expression> = it.collect();
let list = get_list_ref(&list_exp, ctx)?;
let len = list.as_ref().len();
let start = clamp(get_integer_ref(&start_expr, ctx)?, len);
let delete_count = get_integer_ref(&delete_expr, ctx)?.max(0) as usize;
let end = (start + delete_count).min(len);
let mut new_list = Vec::with_capacity(len - (end - start) + items.len());
new_list.extend(list.as_ref()[..start].iter().cloned());
new_list.extend(items);
new_list.extend(list.as_ref()[end..].iter().cloned());
Ok(Expression::List(Rc::new(new_list)))
}
pub fn get_list_ref<'a>(
expr: &'a Expression,
ctx: &Expression,
) -> Result<&'a Rc<Vec<Expression>>, RuntimeError> {
match expr {
Expression::List(s) => Ok(s),
e => Err(RuntimeError::new(
RuntimeErrorKind::TypeError {
expected: "List".into(),
found: e.type_name(),
sym: e.to_string(),
},
ctx.clone(),
0,
)),
}
}