use std::collections::BTreeSet;
use crate::error::{Error, Result, type_error};
use crate::value::{Infinite, Value, count};
pub fn call(name: &str, args: &[Value]) -> Result<Value> {
let arity = |n: usize| -> Result<()> {
if args.len() == n {
Ok(())
} else {
Err(Error::Malformed(format!(
"{name} takes {n} argument(s), given {}",
args.len()
)))
}
};
match name {
"Cardinality" => {
arity(1)?;
match &args[0] {
Value::Set(s) => Ok(Value::Int(count(s.len()))),
Value::Infinite(_) => Err(Error::Unbounded(
"Cardinality of an infinite set".to_string(),
)),
other => type_error(format!("Cardinality expects a set, got {other}")),
}
}
"IsFiniteSet" => {
arity(1)?;
Ok(Value::Bool(matches!(args[0], Value::Set(_))))
}
"Len" => {
arity(1)?;
seq(&args[0], name).map(|s| Value::Int(count(s.len())))
}
"Head" => {
arity(1)?;
seq(&args[0], name)?
.first()
.cloned()
.ok_or_else(|| Error::Type("Head of an empty sequence".to_string()))
}
"Tail" => {
arity(1)?;
let s = seq(&args[0], name)?;
if s.is_empty() {
return type_error("Tail of an empty sequence");
}
Ok(Value::Seq(s[1..].to_vec()))
}
"Append" => {
arity(2)?;
let mut s = seq(&args[0], name)?.to_vec();
s.push(args[1].clone());
Ok(Value::Seq(s))
}
"SubSeq" => {
arity(3)?;
let s = seq(&args[0], name)?;
let lo = int(&args[1], name)?.max(1);
let hi = int(&args[2], name)?.min(count(s.len()));
if hi < lo {
return Ok(Value::Seq(Vec::new()));
}
let (lo, hi) = index_range(lo, hi);
Ok(Value::Seq(s[lo..hi].to_vec()))
}
"Seq" => {
arity(1)?;
Ok(Value::Infinite(Infinite::Sequences(Box::new(
args[0].clone(),
))))
}
"Assert" => {
arity(2)?;
if args[0] == Value::Bool(true) {
Ok(Value::Bool(true))
} else {
Err(Error::Malformed(format!("assertion failed: {}", args[1])))
}
}
"ToString" => {
arity(1)?;
Ok(Value::Str(args[0].to_string()))
}
"Print" => {
arity(2)?;
Ok(args[1].clone())
}
"PrintT" => {
arity(1)?;
Ok(Value::Bool(true))
}
_ => Err(Error::Undefined(name.to_string())),
}
}
pub fn constant(name: &str) -> Option<Value> {
Some(match name {
"Nat" => Value::Infinite(Infinite::Nat),
"Int" => Value::Infinite(Infinite::Int),
"STRING" => Value::Infinite(Infinite::Strings),
"BOOLEAN" => Value::Set(BTreeSet::from([Value::Bool(false), Value::Bool(true)])),
_ => return None,
})
}
fn index_range(lo: i64, hi: i64) -> (usize, usize) {
let to_index = |n: i64| usize::try_from(n).expect("clamped to the sequence's own bounds");
(to_index(lo) - 1, to_index(hi))
}
fn seq<'a>(v: &'a Value, who: &str) -> Result<&'a [Value]> {
match v {
Value::Seq(items) => Ok(items),
other => type_error(format!("{who} expects a sequence, got {other}")),
}
}
fn int(v: &Value, who: &str) -> Result<i64> {
match v {
Value::Int(n) => Ok(*n),
other => type_error(format!("{who} expects an integer, got {other}")),
}
}