use crate::error::StdlibError;
use crate::module::StdlibModule;
use crate::value::Value;
pub struct ListModule;
impl ListModule {
pub fn new() -> Self {
Self
}
}
impl Default for ListModule {
fn default() -> Self {
Self::new()
}
}
impl StdlibModule for ListModule {
fn name(&self) -> &'static str {
"list"
}
fn has_function(&self, function: &str) -> bool {
matches!(
function,
"empty" | "of" | "repeat" | "range"
| "length" | "get" | "first" | "last" | "index_of"
| "append" | "prepend" | "insert" | "remove" | "update" | "set"
| "slice" | "concat" | "reverse" | "flatten" | "unique"
| "map" | "filter" | "reduce" | "find" | "find_index"
| "every" | "any" | "some" | "sort" | "count"
| "contains" | "zip" | "take" | "drop"
)
}
fn call(&self, function: &str, args: Vec<Value>) -> Result<Value, StdlibError> {
match function {
"empty" => self.empty(args),
"of" => self.of(args),
"repeat" => self.repeat(args),
"range" => self.range(args),
"length" => self.length(args),
"get" => self.get(args),
"first" => self.first(args),
"last" => self.last(args),
"index_of" => self.index_of(args),
"append" => self.append(args),
"prepend" => self.prepend(args),
"insert" => self.insert(args),
"remove" => self.remove(args),
"update" | "set" => self.update(args),
"slice" => self.slice(args),
"concat" => self.concat(args),
"reverse" => self.reverse(args),
"flatten" => self.flatten(args),
"unique" => self.unique(args),
"map" => self.map(args),
"filter" => self.filter(args),
"reduce" => self.reduce(args),
"find" => self.find(args),
"find_index" => self.find_index(args),
"every" => self.every(args),
"any" | "some" => self.any(args),
"sort" => self.sort(args),
"count" => self.count(args),
"contains" => self.contains(args),
"zip" => self.zip(args),
"take" => self.take(args),
"drop" => self.drop_fn(args),
_ => Err(StdlibError::unknown_function("list", function)),
}
}
}
fn expect_list(fn_name: &str, args: &[Value]) -> Result<Vec<Value>, StdlibError> {
if args.len() != 1 {
return Err(StdlibError::wrong_args(fn_name, 1, args.len()));
}
match &args[0] {
Value::List(items) => Ok(items.clone()),
other => Err(StdlibError::type_mismatch(fn_name, 1, "list", other.type_name())),
}
}
fn extract_list(fn_name: &str, val: &Value) -> Result<Vec<Value>, StdlibError> {
match val {
Value::List(items) => Ok(items.clone()),
other => Err(StdlibError::type_mismatch(fn_name, 1, "list", other.type_name())),
}
}
fn extract_index(fn_name: &str, val: &Value, position: usize) -> Result<i64, StdlibError> {
match val {
Value::Number(n) => {
if n.fract() != 0.0 || !n.is_finite() {
return Err(StdlibError::RuntimeError(format!(
"{fn_name}: index must be a whole number, got {n}"
)));
}
Ok(*n as i64)
}
other => Err(StdlibError::type_mismatch(fn_name, position, "number", other.type_name())),
}
}
fn extract_number(fn_name: &str, val: &Value, position: usize) -> Result<f64, StdlibError> {
match val {
Value::Number(n) => Ok(*n),
other => Err(StdlibError::type_mismatch(fn_name, position, "number", other.type_name())),
}
}
fn extract_function(
fn_name: &str,
val: &Value,
position: usize,
) -> Result<crate::value::StdlibFn, StdlibError> {
match val {
Value::Function(f) => Ok(f.clone()),
other => Err(StdlibError::type_mismatch(
fn_name,
position,
"function",
other.type_name(),
)),
}
}
impl ListModule {
fn empty(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
if !args.is_empty() {
return Err(StdlibError::wrong_args("list.empty", 0, args.len()));
}
Ok(Value::List(vec![]))
}
fn of(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
Ok(Value::List(args))
}
fn repeat(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
if args.len() != 2 {
return Err(StdlibError::wrong_args("list.repeat", 2, args.len()));
}
let count = extract_number("list.repeat", &args[1], 2)?;
if count.fract() != 0.0 || !count.is_finite() || count < 0.0 {
return Err(StdlibError::RuntimeError(
"list.repeat: count must be a non-negative integer".to_string(),
));
}
let count = count as usize;
let item = args[0].clone();
Ok(Value::List(vec![item; count]))
}
fn range(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
if args.len() != 2 {
return Err(StdlibError::wrong_args("list.range", 2, args.len()));
}
let start = extract_number("list.range", &args[0], 1)?;
let end = extract_number("list.range", &args[1], 2)?;
if start.fract() != 0.0 || end.fract() != 0.0 || !start.is_finite() || !end.is_finite() {
return Err(StdlibError::RuntimeError(
"list.range: start and end must be integers".to_string(),
));
}
let start = start as i64;
let end = end as i64;
if end < start {
return Ok(Value::List(vec![]));
}
let len = (end - start) as usize;
if len > 10_000_000 {
return Err(StdlibError::RuntimeError(
"list.range: range too large (max 10,000,000 elements)".to_string(),
));
}
let items: Vec<Value> = (start..end).map(|i| Value::Number(i as f64)).collect();
Ok(Value::List(items))
}
fn length(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
let items = expect_list("list.length", &args)?;
Ok(Value::Number(items.len() as f64))
}
fn get(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
if args.len() != 2 {
return Err(StdlibError::wrong_args("list.get", 2, args.len()));
}
let items = extract_list("list.get", &args[0])?;
let index = extract_index("list.get", &args[1], 2)?;
if index < 0 || index as usize >= items.len() {
Ok(Value::Nil)
} else {
Ok(items[index as usize].clone())
}
}
fn first(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
let items = expect_list("list.first", &args)?;
Ok(items.first().cloned().unwrap_or(Value::Nil))
}
fn last(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
let items = expect_list("list.last", &args)?;
Ok(items.last().cloned().unwrap_or(Value::Nil))
}
fn index_of(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
if args.len() != 2 {
return Err(StdlibError::wrong_args("list.index_of", 2, args.len()));
}
let items = extract_list("list.index_of", &args[0])?;
let needle = &args[1];
let index = items
.iter()
.position(|v| v == needle)
.map(|i| i as f64)
.unwrap_or(-1.0);
Ok(Value::Number(index))
}
fn append(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
if args.len() != 2 {
return Err(StdlibError::wrong_args("list.append", 2, args.len()));
}
let mut items = extract_list("list.append", &args[0])?;
items.push(args[1].clone());
Ok(Value::List(items))
}
fn prepend(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
if args.len() != 2 {
return Err(StdlibError::wrong_args("list.prepend", 2, args.len()));
}
let mut items = extract_list("list.prepend", &args[0])?;
items.insert(0, args[1].clone());
Ok(Value::List(items))
}
fn insert(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
if args.len() != 3 {
return Err(StdlibError::wrong_args("list.insert", 3, args.len()));
}
let mut items = extract_list("list.insert", &args[0])?;
let index = extract_index("list.insert", &args[1], 2)?;
if index < 0 || index as usize > items.len() {
return Err(StdlibError::RuntimeError(format!(
"list.insert: index {} out of bounds for list of length {}",
index,
items.len()
)));
}
items.insert(index as usize, args[2].clone());
Ok(Value::List(items))
}
fn remove(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
if args.len() != 2 {
return Err(StdlibError::wrong_args("list.remove", 2, args.len()));
}
let mut items = extract_list("list.remove", &args[0])?;
let index = extract_index("list.remove", &args[1], 2)?;
if index < 0 || index as usize >= items.len() {
return Err(StdlibError::RuntimeError(format!(
"list.remove: index {} out of bounds for list of length {}",
index,
items.len()
)));
}
items.remove(index as usize);
Ok(Value::List(items))
}
fn update(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
if args.len() != 3 {
return Err(StdlibError::wrong_args("list.update", 3, args.len()));
}
let mut items = extract_list("list.update", &args[0])?;
let index = extract_index("list.update", &args[1], 2)?;
if index < 0 || index as usize >= items.len() {
return Err(StdlibError::RuntimeError(format!(
"list.update: index {} out of bounds for list of length {}",
index,
items.len()
)));
}
items[index as usize] = args[2].clone();
Ok(Value::List(items))
}
fn slice(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
if args.len() != 3 {
return Err(StdlibError::wrong_args("list.slice", 3, args.len()));
}
let items = extract_list("list.slice", &args[0])?;
let start = extract_index("list.slice", &args[1], 2)?;
let end = extract_index("list.slice", &args[2], 3)?;
let len = items.len() as i64;
let start = start.clamp(0, len) as usize;
let end = end.clamp(0, len) as usize;
if start >= end {
return Ok(Value::List(vec![]));
}
Ok(Value::List(items[start..end].to_vec()))
}
fn concat(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
if args.len() != 2 {
return Err(StdlibError::wrong_args("list.concat", 2, args.len()));
}
let mut a = extract_list("list.concat", &args[0])?;
let b = match &args[1] {
Value::List(items) => items.clone(),
other => {
return Err(StdlibError::type_mismatch(
"list.concat",
2,
"list",
other.type_name(),
))
}
};
a.extend(b);
Ok(Value::List(a))
}
fn reverse(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
let mut items = expect_list("list.reverse", &args)?;
items.reverse();
Ok(Value::List(items))
}
fn flatten(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
let items = expect_list("list.flatten", &args)?;
let mut result = Vec::new();
for item in items {
match item {
Value::List(inner) => result.extend(inner),
other => result.push(other),
}
}
Ok(Value::List(result))
}
fn unique(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
let items = expect_list("list.unique", &args)?;
let mut seen = Vec::new();
let mut result = Vec::new();
for item in items {
if !seen.contains(&item) {
seen.push(item.clone());
result.push(item);
}
}
Ok(Value::List(result))
}
fn map(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
if args.len() != 2 {
return Err(StdlibError::wrong_args("list.map", 2, args.len()));
}
let items = extract_list("list.map", &args[0])?;
let f = extract_function("list.map", &args[1], 2)?;
let mut result = Vec::with_capacity(items.len());
for item in items {
result.push(f.call(vec![item])?);
}
Ok(Value::List(result))
}
fn filter(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
if args.len() != 2 {
return Err(StdlibError::wrong_args("list.filter", 2, args.len()));
}
let items = extract_list("list.filter", &args[0])?;
let pred = extract_function("list.filter", &args[1], 2)?;
let mut result = Vec::new();
for item in items {
let keep = pred.call(vec![item.clone()])?;
if keep.is_truthy() {
result.push(item);
}
}
Ok(Value::List(result))
}
fn reduce(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
if args.len() != 3 {
return Err(StdlibError::wrong_args("list.reduce", 3, args.len()));
}
let items = extract_list("list.reduce", &args[0])?;
let mut acc = args[1].clone();
let f = extract_function("list.reduce", &args[2], 3)?;
for item in items {
acc = f.call(vec![acc, item])?;
}
Ok(acc)
}
fn find(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
if args.len() != 2 {
return Err(StdlibError::wrong_args("list.find", 2, args.len()));
}
let items = extract_list("list.find", &args[0])?;
let pred = extract_function("list.find", &args[1], 2)?;
for item in items {
let matches = pred.call(vec![item.clone()])?;
if matches.is_truthy() {
return Ok(item);
}
}
Ok(Value::Nil)
}
fn find_index(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
if args.len() != 2 {
return Err(StdlibError::wrong_args("list.find_index", 2, args.len()));
}
let items = extract_list("list.find_index", &args[0])?;
let pred = extract_function("list.find_index", &args[1], 2)?;
for (i, item) in items.into_iter().enumerate() {
let matches = pred.call(vec![item])?;
if matches.is_truthy() {
return Ok(Value::Number(i as f64));
}
}
Ok(Value::Number(-1.0))
}
fn every(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
if args.len() != 2 {
return Err(StdlibError::wrong_args("list.every", 2, args.len()));
}
let items = extract_list("list.every", &args[0])?;
let pred = extract_function("list.every", &args[1], 2)?;
for item in items {
let result = pred.call(vec![item])?;
if !result.is_truthy() {
return Ok(Value::Bool(false));
}
}
Ok(Value::Bool(true))
}
fn any(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
if args.len() != 2 {
return Err(StdlibError::wrong_args("list.any", 2, args.len()));
}
let items = extract_list("list.any", &args[0])?;
let pred = extract_function("list.any", &args[1], 2)?;
for item in items {
let result = pred.call(vec![item])?;
if result.is_truthy() {
return Ok(Value::Bool(true));
}
}
Ok(Value::Bool(false))
}
fn sort(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
if args.len() != 2 {
return Err(StdlibError::wrong_args("list.sort", 2, args.len()));
}
let mut items = extract_list("list.sort", &args[0])?;
let cmp = extract_function("list.sort", &args[1], 2)?;
let mut sort_error: Option<StdlibError> = None;
items.sort_by(|a, b| {
if sort_error.is_some() {
return std::cmp::Ordering::Equal;
}
match cmp.call(vec![a.clone(), b.clone()]) {
Ok(Value::Number(n)) => {
if n < 0.0 {
std::cmp::Ordering::Less
} else if n > 0.0 {
std::cmp::Ordering::Greater
} else {
std::cmp::Ordering::Equal
}
}
Ok(other) => {
sort_error = Some(StdlibError::RuntimeError(format!(
"list.sort: comparator must return a number, got {}",
other.type_name()
)));
std::cmp::Ordering::Equal
}
Err(e) => {
sort_error = Some(e);
std::cmp::Ordering::Equal
}
}
});
if let Some(e) = sort_error {
return Err(e);
}
Ok(Value::List(items))
}
fn count(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
if args.len() != 2 {
return Err(StdlibError::wrong_args("list.count", 2, args.len()));
}
let items = extract_list("list.count", &args[0])?;
let pred = extract_function("list.count", &args[1], 2)?;
let mut n = 0usize;
for item in items {
let result = pred.call(vec![item])?;
if result.is_truthy() {
n += 1;
}
}
Ok(Value::Number(n as f64))
}
fn contains(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
if args.len() != 2 {
return Err(StdlibError::wrong_args("list.contains", 2, args.len()));
}
let items = extract_list("list.contains", &args[0])?;
let needle = &args[1];
Ok(Value::Bool(items.contains(needle)))
}
fn zip(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
if args.len() != 2 {
return Err(StdlibError::wrong_args("list.zip", 2, args.len()));
}
let a = extract_list("list.zip", &args[0])?;
let b = match &args[1] {
Value::List(items) => items.clone(),
other => {
return Err(StdlibError::type_mismatch(
"list.zip",
2,
"list",
other.type_name(),
))
}
};
let result: Vec<Value> = a
.into_iter()
.zip(b)
.map(|(first, second)| {
let mut fields = std::collections::BTreeMap::new();
fields.insert("first".to_string(), first);
fields.insert("second".to_string(), second);
Value::record(fields)
})
.collect();
Ok(Value::List(result))
}
fn take(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
if args.len() != 2 {
return Err(StdlibError::wrong_args("list.take", 2, args.len()));
}
let items = extract_list("list.take", &args[0])?;
let n = extract_number("list.take", &args[1], 2)?;
if n.fract() != 0.0 || !n.is_finite() || n < 0.0 {
return Err(StdlibError::RuntimeError(
"list.take: count must be a non-negative integer".to_string(),
));
}
let n = (n as usize).min(items.len());
Ok(Value::List(items[..n].to_vec()))
}
fn drop_fn(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
if args.len() != 2 {
return Err(StdlibError::wrong_args("list.drop", 2, args.len()));
}
let items = extract_list("list.drop", &args[0])?;
let n = extract_number("list.drop", &args[1], 2)?;
if n.fract() != 0.0 || !n.is_finite() || n < 0.0 {
return Err(StdlibError::RuntimeError(
"list.drop: count must be a non-negative integer".to_string(),
));
}
let n = (n as usize).min(items.len());
Ok(Value::List(items[n..].to_vec()))
}
}