use super::error::{EvalResult, Flow, signal};
use super::intern::{intern, resolve_sym};
use super::value::*;
#[cfg(test)]
use std::sync::atomic::{AtomicU64, Ordering};
fn expect_args(name: &str, args: &[Value], n: usize) -> Result<(), Flow> {
if args.len() != n {
Err(signal(
"wrong-number-of-arguments",
vec![Value::symbol(name), Value::fixnum(args.len() as i64)],
))
} else {
Ok(())
}
}
fn expect_min_args(name: &str, args: &[Value], min: usize) -> Result<(), Flow> {
if args.len() < min {
Err(signal(
"wrong-number-of-arguments",
vec![Value::symbol(name), Value::fixnum(args.len() as i64)],
))
} else {
Ok(())
}
}
#[cfg(test)]
fn expect_max_args(name: &str, args: &[Value], max: usize) -> Result<(), Flow> {
if args.len() > max {
Err(signal(
"wrong-number-of-arguments",
vec![Value::symbol(name), Value::fixnum(args.len() as i64)],
))
} else {
Ok(())
}
}
#[cfg(test)]
static CL_GENSYM_COUNTER: AtomicU64 = AtomicU64::new(0);
fn expect_int(val: &Value) -> Result<i64, Flow> {
match val.kind() {
ValueKind::Fixnum(n) => Ok(n),
_ => Err(signal(
"wrong-type-argument",
vec![Value::symbol("integerp"), *val],
)),
}
}
fn expect_number_or_marker(val: &Value) -> Result<f64, Flow> {
match val.kind() {
ValueKind::Fixnum(n) => Ok(n as f64),
ValueKind::Float => Ok(val.xfloat()),
_ => Err(signal(
"wrong-type-argument",
vec![Value::symbol("number-or-marker-p"), *val],
)),
}
}
fn collect_sequence(val: &Value) -> Vec<Value> {
match val.kind() {
ValueKind::Nil => Vec::new(),
ValueKind::Cons => list_to_vec(val).unwrap_or_default(),
ValueKind::Veclike(VecLikeType::Vector) => val.as_vector_data().unwrap().clone(),
ValueKind::String => val.as_str().unwrap().chars().map(Value::char).collect(),
_ => vec![*val],
}
}
#[cfg(test)]
fn cl_list_nth(list: &Value, index: usize) -> EvalResult {
let mut cursor = *list;
for _ in 0..index {
match cursor.kind() {
ValueKind::Nil => return Ok(Value::NIL),
ValueKind::Cons => {
let pair_car = cursor.cons_car();
let pair_cdr = cursor.cons_cdr();
cursor = pair_cdr;
}
tail => {
return Err(signal(
"wrong-type-argument",
vec![Value::symbol("listp"), cursor],
));
}
}
}
match cursor.kind() {
ValueKind::Nil => Ok(Value::NIL),
ValueKind::Cons => Ok(cursor.cons_car()),
tail => Err(signal(
"wrong-type-argument",
vec![Value::symbol("listp"), cursor],
)),
}
}
#[cfg(test)]
pub(crate) fn builtin_cl_first(args: Vec<Value>) -> EvalResult {
expect_args("cl-first", &args, 1)?;
cl_list_nth(&args[0], 0)
}
#[cfg(test)]
pub(crate) fn builtin_cl_second(args: Vec<Value>) -> EvalResult {
expect_args("cl-second", &args, 1)?;
cl_list_nth(&args[0], 1)
}
#[cfg(test)]
pub(crate) fn builtin_cl_third(args: Vec<Value>) -> EvalResult {
expect_args("cl-third", &args, 1)?;
cl_list_nth(&args[0], 2)
}
#[cfg(test)]
pub(crate) fn builtin_cl_fourth(args: Vec<Value>) -> EvalResult {
expect_args("cl-fourth", &args, 1)?;
cl_list_nth(&args[0], 3)
}
#[cfg(test)]
pub(crate) fn builtin_cl_fifth(args: Vec<Value>) -> EvalResult {
expect_args("cl-fifth", &args, 1)?;
cl_list_nth(&args[0], 4)
}
#[cfg(test)]
pub(crate) fn builtin_cl_sixth(args: Vec<Value>) -> EvalResult {
expect_args("cl-sixth", &args, 1)?;
cl_list_nth(&args[0], 5)
}
#[cfg(test)]
pub(crate) fn builtin_cl_seventh(args: Vec<Value>) -> EvalResult {
expect_args("cl-seventh", &args, 1)?;
cl_list_nth(&args[0], 6)
}
#[cfg(test)]
pub(crate) fn builtin_cl_eighth(args: Vec<Value>) -> EvalResult {
expect_args("cl-eighth", &args, 1)?;
cl_list_nth(&args[0], 7)
}
#[cfg(test)]
pub(crate) fn builtin_cl_ninth(args: Vec<Value>) -> EvalResult {
expect_args("cl-ninth", &args, 1)?;
cl_list_nth(&args[0], 8)
}
#[cfg(test)]
pub(crate) fn builtin_cl_tenth(args: Vec<Value>) -> EvalResult {
expect_args("cl-tenth", &args, 1)?;
cl_list_nth(&args[0], 9)
}
#[cfg(test)]
pub(crate) fn builtin_cl_rest(args: Vec<Value>) -> EvalResult {
expect_args("cl-rest", &args, 1)?;
match args[0].kind() {
ValueKind::Nil => Ok(Value::NIL),
ValueKind::Cons => Ok(args[0].cons_cdr()),
_ => Err(signal(
"wrong-type-argument",
vec![Value::symbol("listp"), args[0]],
)),
}
}
#[cfg(test)]
pub(crate) fn builtin_cl_evenp(args: Vec<Value>) -> EvalResult {
expect_args("cl-evenp", &args, 1)?;
let n = expect_int(&args[0])?;
Ok(Value::bool_val(n % 2 == 0))
}
#[cfg(test)]
pub(crate) fn builtin_cl_oddp(args: Vec<Value>) -> EvalResult {
expect_args("cl-oddp", &args, 1)?;
let n = expect_int(&args[0])?;
Ok(Value::bool_val(n % 2 != 0))
}
#[cfg(test)]
pub(crate) fn builtin_cl_plusp(args: Vec<Value>) -> EvalResult {
expect_args("cl-plusp", &args, 1)?;
let n = expect_number_or_marker(&args[0])?;
Ok(Value::bool_val(n > 0.0))
}
#[cfg(test)]
pub(crate) fn builtin_cl_minusp(args: Vec<Value>) -> EvalResult {
expect_args("cl-minusp", &args, 1)?;
let n = expect_number_or_marker(&args[0])?;
Ok(Value::bool_val(n < 0.0))
}
#[cfg(test)]
pub(crate) fn builtin_cl_member(args: Vec<Value>) -> EvalResult {
super::builtins::builtin_member(args)
}
#[cfg(test)]
pub(crate) fn builtin_cl_coerce(args: Vec<Value>) -> EvalResult {
expect_args("cl-coerce", &args, 2)?;
builtin_seq_concatenate(vec![args[1], args[0]])
}
#[cfg(test)]
pub(crate) fn builtin_cl_adjoin(args: Vec<Value>) -> EvalResult {
expect_args("cl-adjoin", &args, 2)?;
let item = args[0];
let list = args[1];
let found = super::builtins::builtin_member(vec![item, list])?;
if found.is_truthy() {
Ok(list)
} else {
Ok(Value::cons(item, list))
}
}
#[cfg(test)]
pub(crate) fn builtin_cl_remove(args: Vec<Value>) -> EvalResult {
super::builtins_extra::remove_list_equal(args)
}
fn seq_position_list_elements(seq: &Value) -> Result<Vec<Value>, Flow> {
let mut elements = Vec::new();
let mut cursor = *seq;
loop {
match cursor.kind() {
ValueKind::Nil => return Ok(elements),
ValueKind::Cons => {
let pair_car = cursor.cons_car();
let pair_cdr = cursor.cons_cdr();
elements.push(pair_car);
cursor = pair_cdr;
}
tail => {
return Err(signal(
"wrong-type-argument",
vec![Value::symbol("listp"), cursor],
));
}
}
}
}
fn seq_position_elements(seq: &Value) -> Result<Vec<Value>, Flow> {
match seq.kind() {
ValueKind::Nil => Ok(Vec::new()),
ValueKind::Cons => seq_position_list_elements(seq),
ValueKind::Veclike(VecLikeType::Vector) => Ok(seq.as_vector_data().unwrap().clone()),
ValueKind::String => Ok(seq
.as_str()
.unwrap()
.chars()
.map(|ch| Value::fixnum(ch as i64))
.collect()),
_ => Err(signal(
"wrong-type-argument",
vec![Value::symbol("sequencep"), *seq],
)),
}
}
fn seq_default_match(left: &Value, right: &Value) -> bool {
if equal_value(left, right, 0) {
return true;
}
match (left.kind(), right.kind()) {
(ValueKind::Fixnum(a), ValueKind::Fixnum(b)) => (a as i64) == b,
_ => false,
}
}
fn seq_collect_concat_arg(arg: &Value) -> Result<Vec<Value>, Flow> {
match arg.kind() {
ValueKind::Nil => Ok(Vec::new()),
ValueKind::Cons => {
let mut out = Vec::new();
let mut cursor = *arg;
loop {
match cursor.kind() {
ValueKind::Nil => return Ok(out),
ValueKind::Cons => {
let pair_car = cursor.cons_car();
let pair_cdr = cursor.cons_cdr();
out.push(pair_car);
cursor = pair_cdr;
}
tail => {
return Err(signal(
"wrong-type-argument",
vec![Value::symbol("listp"), cursor],
));
}
}
}
}
ValueKind::Veclike(VecLikeType::Vector) => Ok(arg.as_vector_data().unwrap().clone()),
ValueKind::String => Ok(arg
.as_str()
.unwrap()
.chars()
.map(|ch| Value::fixnum(ch as i64))
.collect()),
_ => Err(signal(
"error",
vec![Value::string(format!(
"Cannot convert {} into a sequence",
super::print::print_value(arg)
))],
)),
}
}
pub(crate) fn builtin_seq_reverse(args: Vec<Value>) -> EvalResult {
expect_args("seq-reverse", &args, 1)?;
let mut elems = seq_position_elements(&args[0])?;
elems.reverse();
match args[0].kind() {
ValueKind::Veclike(VecLikeType::Vector) => Ok(Value::vector(elems)),
ValueKind::String => {
let mut s = String::new();
for value in &elems {
let ch = match value.kind() {
ValueKind::Fixnum(c) => char::from_u32(c as u32).unwrap_or('\0'),
_ => {
return Err(signal(
"wrong-type-argument",
vec![Value::symbol("characterp"), args[0]],
));
}
};
s.push(ch);
}
Ok(Value::string(s))
}
_ => Ok(Value::list(elems)),
}
}
pub(crate) fn builtin_seq_drop(args: Vec<Value>) -> EvalResult {
expect_args("seq-drop", &args, 2)?;
let n = expect_int(&args[1])?;
match args[0].kind() {
ValueKind::Nil => Ok(Value::NIL),
ValueKind::Veclike(VecLikeType::Vector) => {
let elems = args[0].as_vector_data().unwrap().clone();
if n <= 0 {
return Ok(Value::vector(elems.clone()));
}
let n = (n as usize).min(elems.len());
Ok(Value::vector(elems[n..].to_vec()))
}
ValueKind::String => {
let string = args[0].as_str().unwrap().to_owned();
let chars: Vec<char> = string.chars().collect();
if n <= 0 {
return Ok(Value::string(string));
}
let n = (n as usize).min(chars.len());
let out: String = chars[n..].iter().collect();
Ok(Value::string(out))
}
ValueKind::Cons => {
if n <= 0 {
return Ok(args[0]);
}
let mut cursor = args[0];
let mut remaining = n as usize;
while remaining > 0 {
match cursor.kind() {
ValueKind::Nil => return Ok(Value::NIL),
ValueKind::Cons => {
let pair_car = cursor.cons_car();
let pair_cdr = cursor.cons_cdr();
cursor = pair_cdr;
remaining -= 1;
}
_ => {
return Err(signal(
"wrong-type-argument",
vec![Value::symbol("listp"), args[0]],
));
}
}
}
Ok(cursor)
}
_ => Err(signal(
"wrong-type-argument",
vec![Value::symbol("sequencep"), args[0]],
)),
}
}
pub(crate) fn builtin_seq_take(args: Vec<Value>) -> EvalResult {
expect_args("seq-take", &args, 2)?;
let n = expect_int(&args[1])?;
match args[0].kind() {
ValueKind::Nil => Ok(Value::NIL),
ValueKind::Veclike(VecLikeType::Vector) => {
let elems = args[0].as_vector_data().unwrap().clone();
if n <= 0 {
return Ok(Value::vector(Vec::new()));
}
let n = (n as usize).min(elems.len());
Ok(Value::vector(elems[..n].to_vec()))
}
ValueKind::String => {
let string = args[0].as_str().unwrap().to_owned();
let chars: Vec<char> = string.chars().collect();
if n <= 0 {
return Ok(Value::string(""));
}
let n = (n as usize).min(chars.len());
let out: String = chars[..n].iter().collect();
Ok(Value::string(out))
}
ValueKind::Cons => {
if n <= 0 {
return Ok(Value::NIL);
}
let mut out = Vec::new();
let mut cursor = args[0];
let mut remaining = n as usize;
while remaining > 0 {
match cursor.kind() {
ValueKind::Nil => break,
ValueKind::Cons => {
let pair_car = cursor.cons_car();
let pair_cdr = cursor.cons_cdr();
out.push(pair_car);
cursor = pair_cdr;
remaining -= 1;
}
tail => {
return Err(signal(
"wrong-type-argument",
vec![Value::symbol("listp"), cursor],
));
}
}
}
Ok(Value::list(out))
}
_ => Err(signal(
"wrong-type-argument",
vec![Value::symbol("sequencep"), args[0]],
)),
}
}
fn builtin_seq_subseq_legacy(args: &[Value]) -> EvalResult {
let elems = collect_sequence(&args[0]);
let start = expect_int(&args[1])? as usize;
let end = if args.len() > 2 && !args[2].is_nil() {
expect_int(&args[2])? as usize
} else {
elems.len()
};
let start = start.min(elems.len());
let end = end.min(elems.len());
if start > end {
return Ok(Value::NIL);
}
let result: Vec<Value> = elems[start..end].to_vec();
match args[0].kind() {
ValueKind::Veclike(VecLikeType::Vector) => Ok(Value::vector(result)),
_ => Ok(Value::list(result)),
}
}
pub(crate) fn builtin_seq_subseq(args: Vec<Value>) -> EvalResult {
expect_min_args("seq-subseq", &args, 2)?;
let start = expect_int(&args[1])?;
let end = if args.len() > 2 && !args[2].is_nil() {
Some(expect_int(&args[2])?)
} else {
None
};
if start < 0 || end.is_some_and(|v| v < 0) {
return builtin_seq_subseq_legacy(&args);
}
match args[0].kind() {
ValueKind::Nil
| ValueKind::Cons
| ValueKind::Veclike(VecLikeType::Vector)
| ValueKind::String => {
let dropped = builtin_seq_drop(vec![args[0], Value::fixnum(start)])?;
if let Some(end_idx) = end {
let span = end_idx - start;
builtin_seq_take(vec![dropped, Value::fixnum(span)])
} else {
Ok(dropped)
}
}
_ => Err(signal(
"error",
vec![Value::string(format!(
"Unsupported sequence: {}",
super::print::print_value(&args[0])
))],
)),
}
}
pub(crate) fn builtin_seq_concatenate(args: Vec<Value>) -> EvalResult {
expect_min_args("seq-concatenate", &args, 1)?;
let target = match args[0].kind() {
ValueKind::Symbol(id) => resolve_sym(id),
_ => {
return Err(signal(
"error",
vec![Value::string(format!(
"Not a sequence type name: {}",
super::print::print_value(&args[0])
))],
));
}
};
if target != "list" && target != "vector" && target != "string" {
return Err(signal(
"error",
vec![Value::string(format!(
"Not a sequence type name: {}",
target
))],
));
}
let mut combined = Vec::new();
for arg in &args[1..] {
combined.extend(seq_collect_concat_arg(arg)?);
}
match target {
"list" => Ok(Value::list(combined)),
"vector" => Ok(Value::vector(combined)),
"string" => {
let mut s = String::new();
for value in &combined {
let ch = match value.kind() {
ValueKind::Fixnum(c) => char::from_u32(c as u32).unwrap_or('\0'),
_ => {
return Err(signal(
"wrong-type-argument",
vec![Value::symbol("characterp"), *value],
));
}
};
s.push(ch);
}
Ok(Value::string(s))
}
_ => unreachable!(),
}
}
pub(crate) fn builtin_seq_empty_p(args: Vec<Value>) -> EvalResult {
expect_args("seq-empty-p", &args, 1)?;
match args[0].kind() {
ValueKind::Nil => Ok(Value::T),
ValueKind::Cons => Ok(Value::NIL),
ValueKind::Veclike(VecLikeType::Lambda) | ValueKind::Veclike(VecLikeType::ByteCode) => {
Ok(Value::NIL)
}
ValueKind::String => Ok(Value::bool_val(args[0].as_str().unwrap().is_empty())),
ValueKind::Veclike(VecLikeType::Vector) => Ok(Value::bool_val(
args[0].as_vector_data().unwrap().len() == 0,
)),
_ => Err(signal(
"wrong-type-argument",
vec![Value::symbol("sequencep"), args[0]],
)),
}
}
pub(crate) fn builtin_seq_min(args: Vec<Value>) -> EvalResult {
expect_args("seq-min", &args, 1)?;
let elems = seq_position_elements(&args[0])?;
if elems.is_empty() {
return Err(signal(
"wrong-number-of-arguments",
vec![Value::subr(intern("min")), Value::fixnum(0)],
));
}
let mut min_val = &elems[0];
let mut min_num = expect_number_or_marker(min_val)?;
for e in &elems[1..] {
let b = expect_number_or_marker(e)?;
if b < min_num {
min_num = b;
min_val = e;
}
}
Ok(*min_val)
}
pub(crate) fn builtin_seq_max(args: Vec<Value>) -> EvalResult {
expect_args("seq-max", &args, 1)?;
let elems = seq_position_elements(&args[0])?;
if elems.is_empty() {
return Err(signal(
"wrong-number-of-arguments",
vec![Value::subr(intern("max")), Value::fixnum(0)],
));
}
let mut max_val = &elems[0];
let mut max_num = expect_number_or_marker(max_val)?;
for e in &elems[1..] {
let b = expect_number_or_marker(e)?;
if b > max_num {
max_num = b;
max_val = e;
}
}
Ok(*max_val)
}
pub(crate) fn builtin_seq_position(
eval: &mut super::eval::Context,
args: Vec<Value>,
) -> EvalResult {
expect_min_args("seq-position", &args, 2)?;
let seq = &args[0];
if matches!(
seq.kind(),
ValueKind::Veclike(VecLikeType::Lambda) | ValueKind::Veclike(VecLikeType::ByteCode)
) {
return Ok(Value::NIL);
}
let target = args[1];
let test_fn = if args.len() > 2 && !args[2].is_nil() {
Some(args[2])
} else {
None
};
let elements = seq_position_elements(seq)?;
eval.with_gc_scope_result(|ctx| {
ctx.root(target);
if let Some(tf) = &test_fn {
ctx.root(*tf);
}
for e in &elements {
ctx.root(*e);
}
for (idx, element) in elements.into_iter().enumerate() {
let matches = if let Some(test) = &test_fn {
ctx.apply(*test, vec![element, target])?.is_truthy()
} else {
seq_default_match(&element, &target)
};
if matches {
return Ok(Value::fixnum(idx as i64));
}
}
Ok(Value::NIL)
})
}
#[cfg(test)]
pub(crate) fn builtin_cl_position(eval: &mut super::eval::Context, args: Vec<Value>) -> EvalResult {
expect_min_args("cl-position", &args, 2)?;
expect_max_args("cl-position", &args, 3)?;
let mut forwarded = Vec::with_capacity(args.len());
forwarded.push(args[1]);
forwarded.push(args[0]);
if args.len() == 3 {
forwarded.push(args[2]);
}
builtin_seq_position(eval, forwarded)
}
#[cfg(test)]
pub(crate) fn builtin_cl_notany(eval: &mut super::eval::Context, args: Vec<Value>) -> EvalResult {
let found = builtin_seq_some(eval, args)?;
Ok(Value::bool_val(found.is_nil()))
}
#[cfg(test)]
pub(crate) fn builtin_cl_notevery(eval: &mut super::eval::Context, args: Vec<Value>) -> EvalResult {
let every = builtin_seq_every_p(eval, args)?;
Ok(Value::bool_val(!every.is_truthy()))
}
#[cfg(test)]
pub(crate) fn builtin_cl_gensym(args: Vec<Value>) -> EvalResult {
expect_max_args("cl-gensym", &args, 1)?;
let prefix = match args.first() {
None => "G".to_string(),
Some(v) if v.is_nil() => "G".to_string(),
Some(v) if v.is_string() => v.as_str().unwrap().to_owned(),
Some(other) => {
return Err(signal(
"wrong-type-argument",
vec![Value::symbol("stringp"), *other],
));
}
};
let n = CL_GENSYM_COUNTER.fetch_add(1, Ordering::Relaxed);
Ok(Value::symbol(format!("{prefix}{n}")))
}
#[cfg(test)]
pub(crate) fn builtin_cl_find(args: Vec<Value>) -> EvalResult {
expect_args("cl-find", &args, 2)?;
let target = &args[0];
let elements = seq_position_elements(&args[1])?;
for element in elements {
if equal_value(&element, target, 0) {
return Ok(element);
}
}
Ok(Value::NIL)
}
#[cfg(test)]
pub(crate) fn builtin_cl_find_if(eval: &mut super::eval::Context, args: Vec<Value>) -> EvalResult {
expect_args("cl-find-if", &args, 2)?;
let pred = args[0];
let elements = seq_position_elements(&args[1])?;
eval.with_gc_scope_result(|ctx| {
ctx.root(pred);
for e in &elements {
ctx.root(*e);
}
for element in elements {
let matched = ctx.apply(pred, vec![element])?;
if matched.is_truthy() {
return Ok(element);
}
}
Ok(Value::NIL)
})
}
#[cfg(test)]
pub(crate) fn builtin_cl_subsetp(args: Vec<Value>) -> EvalResult {
expect_args("cl-subsetp", &args, 2)?;
let left = seq_position_elements(&args[0])?;
let right = seq_position_elements(&args[1])?;
for item in left {
if !right
.iter()
.any(|candidate| equal_value(&item, candidate, 0))
{
return Ok(Value::NIL);
}
}
Ok(Value::T)
}
#[cfg(test)]
pub(crate) fn builtin_cl_intersection(args: Vec<Value>) -> EvalResult {
expect_args("cl-intersection", &args, 2)?;
let left = seq_position_elements(&args[0])?;
let right = seq_position_elements(&args[1])?;
let mut out = Vec::new();
for item in left {
let in_right = right
.iter()
.any(|candidate| equal_value(&item, candidate, 0));
let already_in_out = out.iter().any(|seen| equal_value(&item, seen, 0));
if in_right && !already_in_out {
out.push(item);
}
}
Ok(Value::list(out))
}
#[cfg(test)]
pub(crate) fn builtin_cl_set_difference(args: Vec<Value>) -> EvalResult {
expect_args("cl-set-difference", &args, 2)?;
let left = seq_position_elements(&args[0])?;
let right = seq_position_elements(&args[1])?;
let mut out = Vec::new();
for item in left {
let in_right = right
.iter()
.any(|candidate| equal_value(&item, candidate, 0));
let already_in_out = out.iter().any(|seen| equal_value(&item, seen, 0));
if !in_right && !already_in_out {
out.push(item);
}
}
Ok(Value::list(out))
}
#[cfg(test)]
pub(crate) fn builtin_cl_union(args: Vec<Value>) -> EvalResult {
expect_args("cl-union", &args, 2)?;
let left = seq_position_elements(&args[0])?;
let right = seq_position_elements(&args[1])?;
let mut out = Vec::new();
for item in left.into_iter().chain(right.into_iter()) {
let already_in_out = out.iter().any(|seen| equal_value(&item, seen, 0));
if !already_in_out {
out.push(item);
}
}
Ok(Value::list(out))
}
#[cfg(test)]
pub(crate) fn builtin_cl_substitute(args: Vec<Value>) -> EvalResult {
expect_args("cl-substitute", &args, 3)?;
let new_value = args[0];
let old_value = &args[1];
let elements = seq_position_elements(&args[2])?;
let replaced = elements
.into_iter()
.map(|item| {
if equal_value(&item, old_value, 0) {
new_value
} else {
item
}
})
.collect::<Vec<_>>();
Ok(Value::list(replaced))
}
#[cfg(test)]
pub(crate) fn builtin_cl_sort(eval: &mut super::eval::Context, args: Vec<Value>) -> EvalResult {
super::builtins::builtin_sort(eval, args)
}
#[cfg(test)]
pub(crate) fn builtin_cl_stable_sort(
eval: &mut super::eval::Context,
args: Vec<Value>,
) -> EvalResult {
super::builtins::builtin_sort(eval, args)
}
#[cfg(test)]
pub(crate) fn builtin_cl_remove_if(
eval: &mut super::eval::Context,
args: Vec<Value>,
) -> EvalResult {
expect_args("cl-remove-if", &args, 2)?;
let pred = args[0];
let elements = seq_position_elements(&args[1])?;
eval.with_gc_scope_result(|ctx| {
ctx.root(pred);
for e in &elements {
ctx.root(*e);
}
let mut out = Vec::new();
for element in elements {
let matched = ctx.apply(pred, vec![element])?;
if !matched.is_truthy() {
out.push(element);
}
}
Ok(Value::list(out))
})
}
#[cfg(test)]
pub(crate) fn builtin_cl_remove_if_not(
eval: &mut super::eval::Context,
args: Vec<Value>,
) -> EvalResult {
expect_args("cl-remove-if-not", &args, 2)?;
let pred = args[0];
let elements = seq_position_elements(&args[1])?;
eval.with_gc_scope_result(|ctx| {
ctx.root(pred);
for e in &elements {
ctx.root(*e);
}
let mut out = Vec::new();
for element in elements {
let matched = ctx.apply(pred, vec![element])?;
if matched.is_truthy() {
out.push(element);
}
}
Ok(Value::list(out))
})
}
#[cfg(test)]
pub(crate) fn builtin_cl_map(eval: &mut super::eval::Context, args: Vec<Value>) -> EvalResult {
expect_min_args("cl-map", &args, 3)?;
let result_type = args[0];
let func = args[1];
let seqs = args[2..].to_vec();
let mut forwarded = Vec::with_capacity(1 + seqs.len());
forwarded.push(func);
forwarded.extend(seqs);
let mapped = builtin_seq_mapn(eval, forwarded)?;
match result_type.kind() {
ValueKind::Symbol(id) if resolve_sym(id) == "list" => Ok(mapped),
ValueKind::Symbol(id) if resolve_sym(id) == "vector" => {
let items = list_to_vec(&mapped).ok_or_else(|| {
signal("wrong-type-argument", vec![Value::symbol("listp"), mapped])
})?;
Ok(Value::vector(items))
}
ValueKind::Symbol(id) if resolve_sym(id) == "string" => {
let items = list_to_vec(&mapped).ok_or_else(|| {
signal("wrong-type-argument", vec![Value::symbol("listp"), mapped])
})?;
let mut out = String::new();
for item in items {
let ch = match item.kind() {
ValueKind::Fixnum(c) => char::from_u32(c as u32).unwrap_or('\0'),
_ => {
return Err(signal(
"wrong-type-argument",
vec![Value::symbol("characterp"), item],
));
}
};
out.push(ch);
}
Ok(Value::string(out))
}
_ => Err(signal(
"error",
vec![Value::string(format!(
"Unsupported cl-map result type: {}",
super::print::print_value(&result_type)
))],
)),
}
}
pub(crate) fn builtin_seq_contains_p(
eval: &mut super::eval::Context,
args: Vec<Value>,
) -> EvalResult {
if !(2..=3).contains(&args.len()) {
return Err(signal(
"wrong-number-of-arguments",
vec![
Value::symbol("seq-contains-p"),
Value::fixnum(args.len() as i64),
],
));
}
let seq = &args[0];
let target = args[1];
let test_fn = if args.len() == 3 && !args[2].is_nil() {
Some(args[2])
} else {
None
};
let elements = seq_position_elements(seq)?;
eval.with_gc_scope_result(|ctx| {
ctx.root(target);
if let Some(tf) = &test_fn {
ctx.root(*tf);
}
for e in &elements {
ctx.root(*e);
}
for element in elements {
let matches = if let Some(test) = &test_fn {
ctx.apply(*test, vec![element, target])?.is_truthy()
} else {
seq_default_match(&element, &target)
};
if matches {
return Ok(Value::T);
}
}
Ok(Value::NIL)
})
}
pub(crate) fn builtin_seq_mapn(eval: &mut super::eval::Context, args: Vec<Value>) -> EvalResult {
expect_min_args("seq-mapn", &args, 2)?;
let func = args[0];
let seqs: Vec<Vec<Value>> = args[1..].iter().map(collect_sequence).collect();
if seqs.is_empty() {
return Ok(Value::NIL);
}
let min_len = seqs.iter().map(|s| s.len()).min().unwrap_or(0);
eval.with_gc_scope_result(|ctx| {
ctx.root(func);
for seq in &seqs {
for e in seq {
ctx.root(*e);
}
}
let mut results = Vec::new();
for i in 0..min_len {
let call_args: Vec<Value> = seqs.iter().map(|s| s[i]).collect();
let val = ctx.apply(func, call_args)?;
ctx.root(val);
results.push(val);
}
Ok(Value::list(results))
})
}
pub(crate) fn builtin_seq_do(eval: &mut super::eval::Context, args: Vec<Value>) -> EvalResult {
expect_args("seq-do", &args, 2)?;
let func = args[0];
let elems = collect_sequence(&args[1]);
eval.with_gc_scope_result(|ctx| {
ctx.root(func);
for e in &elems {
ctx.root(*e);
}
for e in elems {
ctx.apply(func, vec![e])?;
}
Ok(Value::NIL)
})
}
pub(crate) fn builtin_seq_count(eval: &mut super::eval::Context, args: Vec<Value>) -> EvalResult {
expect_args("seq-count", &args, 2)?;
let pred = args[0];
let elems = collect_sequence(&args[1]);
eval.with_gc_scope_result(|ctx| {
ctx.root(pred);
for e in &elems {
ctx.root(*e);
}
let mut count = 0i64;
for e in elems {
let r = ctx.apply(pred, vec![e])?;
if r.is_truthy() {
count += 1;
}
}
Ok(Value::fixnum(count))
})
}
pub(crate) fn builtin_seq_reduce(eval: &mut super::eval::Context, args: Vec<Value>) -> EvalResult {
expect_args("seq-reduce", &args, 3)?;
let func = args[0];
let elems = collect_sequence(&args[1]);
let mut acc = args[2];
eval.with_gc_scope_result(|ctx| {
ctx.root(func);
ctx.root(acc);
for e in &elems {
ctx.root(*e);
}
for e in elems {
acc = ctx.apply(func, vec![acc, e])?;
ctx.root(acc);
}
Ok(acc)
})
}
pub(crate) fn builtin_seq_some(eval: &mut super::eval::Context, args: Vec<Value>) -> EvalResult {
expect_args("seq-some", &args, 2)?;
let pred = args[0];
let elems = collect_sequence(&args[1]);
eval.with_gc_scope_result(|ctx| {
ctx.root(pred);
for e in &elems {
ctx.root(*e);
}
for e in elems {
let r = ctx.apply(pred, vec![e])?;
if r.is_truthy() {
return Ok(r);
}
}
Ok(Value::NIL)
})
}
pub(crate) fn builtin_seq_every_p(eval: &mut super::eval::Context, args: Vec<Value>) -> EvalResult {
expect_args("seq-every-p", &args, 2)?;
let pred = args[0];
let elems = collect_sequence(&args[1]);
eval.with_gc_scope_result(|ctx| {
ctx.root(pred);
for e in &elems {
ctx.root(*e);
}
for e in elems {
let r = ctx.apply(pred, vec![e])?;
if r.is_nil() {
return Ok(Value::NIL);
}
}
Ok(Value::T)
})
}
pub(crate) fn builtin_seq_sort(eval: &mut super::eval::Context, args: Vec<Value>) -> EvalResult {
expect_args("seq-sort", &args, 2)?;
let pred = args[0];
let mut items = collect_sequence(&args[1]);
eval.with_gc_scope_result(|ctx| {
ctx.root(pred);
for e in &items {
ctx.root(*e);
}
for i in 1..items.len() {
let mut j = i;
while j > 0 {
let result = ctx.apply(pred, vec![items[j], items[j - 1]])?;
if result.is_truthy() {
items.swap(j, j - 1);
j -= 1;
} else {
break;
}
}
}
Ok(Value::list(items))
})
}
#[cfg(test)]
#[path = "cl_lib_test.rs"]
mod tests;