summaryrefslogtreecommitdiff
path: root/src/builtins/lists.rs
blob: 5ecf4159fa7028d61befd7599ac7aee3dfeaa4a7 (plain)
use std::{collections::HashSet, sync::Arc};

use rust_decimal::Decimal;

use crate::{
	builtins::delsh_bool,
	interpreter::{self, Interpreter, Value, vec_to_value},
};

use super::NIL;

fn car_inner(list: Arc<Value>) -> Arc<Value> {
	let Value::Pair(a, _b) = &*list else {
		panic!("expected a list")
	};

	a.clone()
}

fn cdr_inner(list: Arc<Value>) -> Arc<Value> {
	let Value::Pair(_a, b) = &*list else {
		panic!("expected a list")
	};

	b.clone()
}

pub fn is_member(interpreter: &mut Interpreter, args: &[Arc<Value>]) -> Arc<Value> {
	let pattern = interpreter.eval(args[0].clone());
	let full = interpreter.eval(args[1].clone());

	fn is_among_inner(pattern: Arc<Value>, full: Arc<Value>) -> bool {
		if pattern == full {
			return true;
		}

		if let Some((a, b)) = full.pair() {
			return is_among_inner(pattern.clone(), a.clone())
				|| is_among_inner(pattern, b.clone());
		}

		false
	}

	delsh_bool(is_among_inner(pattern, full))
}

macro_rules! cr {
	($name: ident => $($cr: expr),*) => {
		pub fn $name(interpreter: &mut Interpreter, args: &[Arc<Value>]) -> Arc<Value> {
			let list = interpreter.eval(args[0].clone());
			$(let list = $cr(list);)*
			list
		}
	};
}

cr!(car => car_inner);
cr!(cdr => cdr_inner);
cr!(caar => car_inner, car_inner);
cr!(cadr => cdr_inner, car_inner);
cr!(cdar => car_inner, cdr_inner);
cr!(cddr => cdr_inner, cdr_inner);
cr!(caaar => car_inner, car_inner, car_inner);
cr!(caadr => cdr_inner, car_inner, car_inner);
cr!(cadar => car_inner, cdr_inner, car_inner);
cr!(caddr => cdr_inner, cdr_inner, car_inner);
cr!(cdaar => car_inner, car_inner, cdr_inner);
cr!(cdadr => cdr_inner, car_inner, cdr_inner);
cr!(cddar => car_inner, cdr_inner, cdr_inner);
cr!(cdddr => cdr_inner, cdr_inner, cdr_inner);

macro_rules! index {
	(fn $fn_name: ident => $index: expr) => {
		pub fn $fn_name(interpreter: &mut Interpreter, args: &[Arc<Value>]) -> Arc<Value> {
			let list = interpreter
				.eval(args[0].clone())
				.list()
				.expect("a list to index");
			list[$index].clone()
		}
	};
}

index!(fn first => 0);
index!(fn second => 1);
index!(fn third => 2);
index!(fn fourth => 3);
index!(fn fifth => 4);
index!(fn sixth => 5);
index!(fn seventh => 6);
index!(fn eighth => 7);
index!(fn ninth => 8);
index!(fn tenth => 9);

pub fn list(interpreter: &mut Interpreter, args: &[Arc<Value>]) -> Arc<Value> {
	vec_to_value(
		&(args
			.iter()
			.map(|a| interpreter.eval(a.clone()))
			.collect::<Box<_>>()),
	)
}

pub fn reverse(interpreter: &mut Interpreter, args: &[Arc<Value>]) -> Arc<Value> {
	let mut list = interpreter.eval(args[0].clone()).list().expect("a list");
	list.reverse();

	interpreter::vec_to_value(&list)
}

pub fn append(interpreter: &mut Interpreter, args: &[Arc<Value>]) -> Arc<Value> {
	let mut new_list = Vec::new();

	for arg in args {
		let arg = interpreter.eval(arg.clone()).list().expect("a list");
		new_list.extend(arg);
	}

	vec_to_value(&new_list)
}

pub fn length(interpreter: &mut Interpreter, args: &[Arc<Value>]) -> Arc<Value> {
	let list = interpreter.eval(args[0].clone()).list().expect("a list");
	Arc::new(Value::Number(Decimal::from(list.len())))
}

pub fn efface(interpreter: &mut Interpreter, args: &[Arc<Value>]) -> Arc<Value> {
	let element = interpreter.eval(args[0].clone());
	let mut list = interpreter.eval(args[1].clone()).list().expect("a list");

	let Some(index_to_remove) = list.iter().position(|i| *i == element) else {
		return NIL.clone();
	};

	list.remove(index_to_remove);
	interpreter::vec_to_value(&list)
}

pub fn union(interpreter: &mut Interpreter, args: &[Arc<Value>]) -> Arc<Value> {
	let mut unique_values = HashSet::new();

	for arg in args {
		let arg = interpreter
			.eval(arg.clone())
			.set()
			.expect("all arguments to be lists");
		for value in arg {
			unique_values.insert(value);
		}
	}

	interpreter::vec_to_value(&unique_values.into_iter().collect::<Box<[_]>>())
}

pub fn intersection(interpreter: &mut Interpreter, args: &[Arc<Value>]) -> Arc<Value> {
	let mut common_values = Vec::new();

	for arg in args {
		let arg = interpreter
			.eval(arg.clone())
			.list()
			.expect("all arguments to be lists");
		let arg = arg.into_iter().collect::<HashSet<_>>();

		common_values.retain(|element| arg.contains(element));
	}

	interpreter::vec_to_value(&common_values.into_iter().collect::<Box<[_]>>())
}