summaryrefslogtreecommitdiff
path: root/src/builtins/pairlists.rs
blob: 1c188105cfdad0dd846645cb3ddcf517c2e86482 (plain)
use std::sync::Arc;

use crate::interpreter::{self, Interpreter, Value};

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

	let pairs = keys
		.into_iter()
		.zip(vals)
		.map(|(key, value)| Arc::new(Value::Pair(key, value)))
		.collect::<Box<_>>();

	interpreter::vec_to_value(&pairs)
}

fn assoc_inner(key: Arc<Value>, pairs: &[Arc<Value>]) -> Option<Arc<Value>> {
	let get_value = |pair: &Arc<Value>| {
		if let Value::Pair(k, v) = &**pair {
			(k == &key).then(|| v.clone())
		} else {
			None
		}
	};

	pairs.iter().filter_map(get_value).next()
}

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

	assoc_inner(key, &pairs).unwrap()
}

pub fn sublis(interpreter: &mut Interpreter, args: &[Arc<Value>]) -> Arc<Value> {
	fn sublis_inner(expression: Arc<Value>, replacements: &[Arc<Value>]) -> Arc<Value> {
		if let Some(value) = assoc_inner(expression.clone(), replacements) {
			return value.clone();
		}

		if let Value::Pair(a, b) = &*expression {
			return Arc::new(Value::Pair(
				sublis_inner(a.clone(), replacements),
				sublis_inner(b.clone(), replacements),
			));
		}

		expression
	}

	let expression = interpreter.eval(args[1].clone());
	let replacements = interpreter
		.eval(args[0].clone())
		.list()
		.expect("a list of replacements");

	sublis_inner(expression, &replacements)
}