use crate::scheme::arena::{Arena, ValueId, ValueData, NIL_ID};
use std::rc::Rc;
pub type ArenaResult = Result<ValueId, String>;
#[inline]
pub fn car(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("car requires exactly 1 argument".to_string());
}
match arena.get(args[0]) {
ValueData::Pair { car, .. } => Ok(*car),
_ => Err(format!("car: not a pair")),
}
}
#[inline]
pub fn cdr(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("cdr requires exactly 1 argument".to_string());
}
match arena.get(args[0]) {
ValueData::Pair { cdr, .. } => Ok(*cdr),
_ => Err(format!("cdr: not a pair")),
}
}
#[inline]
pub fn cons(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err("cons requires exactly 2 arguments".to_string());
}
Ok(arena.cons(args[0], args[1]))
}
#[inline]
pub fn null(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("null? requires exactly 1 argument".to_string());
}
Ok(if args[0] == NIL_ID {
crate::scheme::arena::TRUE_ID
} else {
crate::scheme::arena::FALSE_ID
})
}
pub fn equal(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err("equal? requires exactly 2 arguments".to_string());
}
Ok(if equal_impl(arena, args[0], args[1]) {
crate::scheme::arena::TRUE_ID
} else {
crate::scheme::arena::FALSE_ID
})
}
fn equal_impl(arena: &Arena, a: ValueId, b: ValueId) -> bool {
if a == b {
return true;
}
match (arena.get(a), arena.get(b)) {
(ValueData::Pair { car: car1, cdr: cdr1, .. },
ValueData::Pair { car: car2, cdr: cdr2, .. }) => {
equal_impl(arena, *car1, *car2) && equal_impl(arena, *cdr1, *cdr2)
}
(ValueData::Vector(v1), ValueData::Vector(v2)) => {
v1.len() == v2.len() &&
v1.iter().zip(v2.iter()).all(|(a, b)| equal_impl(arena, *a, *b))
}
(ValueData::Integer(n1), ValueData::Integer(n2)) => n1 == n2,
(ValueData::Real(f1), ValueData::Real(f2)) => f1 == f2,
(ValueData::Bool(b1), ValueData::Bool(b2)) => b1 == b2,
(ValueData::Char(c1), ValueData::Char(c2)) => c1 == c2,
(ValueData::String(s1), ValueData::String(s2)) => s1 == s2,
(ValueData::Symbol(s1), ValueData::Symbol(s2)) => s1 == s2,
_ => false,
}
}
#[inline]
pub fn cadr(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("cadr requires exactly 1 argument".to_string());
}
let cdr_result = cdr(arena, args)?;
car(arena, &[cdr_result])
}
#[inline]
pub fn caddr(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("caddr requires exactly 1 argument".to_string());
}
let cdr1 = cdr(arena, args)?;
let cdr2 = cdr(arena, &[cdr1])?;
car(arena, &[cdr2])
}
#[inline]
pub fn cadddr(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("cadddr requires exactly 1 argument".to_string());
}
let cdr1 = cdr(arena, args)?;
let cdr2 = cdr(arena, &[cdr1])?;
let cdr3 = cdr(arena, &[cdr2])?;
car(arena, &[cdr3])
}
pub fn list(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
let mut result = NIL_ID;
for &arg in args.iter().rev() {
result = arena.cons(arg, result);
}
Ok(result)
}
pub fn length(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("length requires exactly 1 argument".to_string());
}
let mut len = 0i64;
let mut current = args[0];
loop {
if current == NIL_ID {
return Ok(arena.int(len));
}
match arena.get(current) {
ValueData::Pair { cdr, .. } => {
len += 1;
current = *cdr;
}
_ => return Err("length: not a proper list".to_string()),
}
}
}
pub fn reverse(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("reverse requires exactly 1 argument".to_string());
}
let mut result = NIL_ID;
let mut current = args[0];
loop {
if current == NIL_ID {
return Ok(result);
}
let (car_val, cdr_val) = match arena.get(current) {
ValueData::Pair { car, cdr, .. } => (*car, *cdr),
_ => return Err("reverse: not a proper list".to_string()),
};
result = arena.cons(car_val, result);
current = cdr_val;
}
}
pub fn append(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.is_empty() {
return Ok(NIL_ID);
}
if args.len() == 1 {
return Ok(args[0]);
}
let mut result = args[args.len() - 1];
for i in (0..args.len() - 1).rev() {
let mut items = Vec::new();
let mut current = args[i];
loop {
if current == NIL_ID {
break;
}
match arena.get(current) {
ValueData::Pair { car, cdr, .. } => {
items.push(*car);
current = *cdr;
}
_ => return Err("append: not a proper list".to_string()),
}
}
for &item in items.iter().rev() {
result = arena.cons(item, result);
}
}
Ok(result)
}
pub fn list_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("list? requires exactly 1 argument".to_string());
}
let mut current = args[0];
loop {
if current == NIL_ID {
return Ok(crate::scheme::arena::TRUE_ID);
}
match arena.get(current) {
ValueData::Pair { cdr, .. } => {
current = *cdr;
}
_ => return Ok(crate::scheme::arena::FALSE_ID),
}
}
}
pub fn list_ref(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err("list-ref requires exactly 2 arguments".to_string());
}
let k = match arena.get(args[1]) {
ValueData::Integer(n) if *n >= 0 => *n as usize,
ValueData::Integer(_) => return Err("list-ref: index must be non-negative".to_string()),
_ => return Err("list-ref: index must be an integer".to_string()),
};
let mut current = args[0];
for _ in 0..k {
match arena.get(current) {
ValueData::Pair { cdr, .. } => {
current = *cdr;
}
_ => return Err("list-ref: index out of bounds".to_string()),
}
}
match arena.get(current) {
ValueData::Pair { car, .. } => Ok(*car),
_ => Err("list-ref: index out of bounds".to_string()),
}
}
#[inline]
pub fn pair_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("pair? requires exactly 1 argument".to_string());
}
Ok(match arena.get(args[0]) {
ValueData::Pair { .. } => crate::scheme::arena::TRUE_ID,
_ => crate::scheme::arena::FALSE_ID,
})
}
#[inline]
pub fn number_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("number? requires exactly 1 argument".to_string());
}
Ok(match arena.get(args[0]) {
ValueData::Integer(_) | ValueData::Real(_) => crate::scheme::arena::TRUE_ID,
_ => crate::scheme::arena::FALSE_ID,
})
}
#[inline]
pub fn integer_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("integer? requires exactly 1 argument".to_string());
}
Ok(match arena.get(args[0]) {
ValueData::Integer(_) => crate::scheme::arena::TRUE_ID,
_ => crate::scheme::arena::FALSE_ID,
})
}
#[inline]
pub fn real_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("real? requires exactly 1 argument".to_string());
}
Ok(match arena.get(args[0]) {
ValueData::Real(_) => crate::scheme::arena::TRUE_ID,
_ => crate::scheme::arena::FALSE_ID,
})
}
#[inline]
pub fn string_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("string? requires exactly 1 argument".to_string());
}
Ok(match arena.get(args[0]) {
ValueData::String(_) => crate::scheme::arena::TRUE_ID,
_ => crate::scheme::arena::FALSE_ID,
})
}
#[inline]
pub fn symbol_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("symbol? requires exactly 1 argument".to_string());
}
Ok(match arena.get(args[0]) {
ValueData::Symbol(_) => crate::scheme::arena::TRUE_ID,
_ => crate::scheme::arena::FALSE_ID,
})
}
#[inline]
pub fn char_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("char? requires exactly 1 argument".to_string());
}
Ok(match arena.get(args[0]) {
ValueData::Char(_) => crate::scheme::arena::TRUE_ID,
_ => crate::scheme::arena::FALSE_ID,
})
}
#[inline]
pub fn boolean_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("boolean? requires exactly 1 argument".to_string());
}
Ok(match arena.get(args[0]) {
ValueData::Bool(_) => crate::scheme::arena::TRUE_ID,
_ => crate::scheme::arena::FALSE_ID,
})
}
#[inline]
pub fn zero_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("zero? requires exactly 1 argument".to_string());
}
Ok(match arena.get(args[0]) {
ValueData::Integer(n) => {
if *n == 0 {
crate::scheme::arena::TRUE_ID
} else {
crate::scheme::arena::FALSE_ID
}
}
ValueData::Real(f) => {
if *f == 0.0 {
crate::scheme::arena::TRUE_ID
} else {
crate::scheme::arena::FALSE_ID
}
}
_ => return Err("zero?: not a number".to_string()),
})
}
#[inline]
pub fn positive_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("positive? requires exactly 1 argument".to_string());
}
Ok(match arena.get(args[0]) {
ValueData::Integer(n) => {
if *n > 0 {
crate::scheme::arena::TRUE_ID
} else {
crate::scheme::arena::FALSE_ID
}
}
ValueData::Real(f) => {
if *f > 0.0 {
crate::scheme::arena::TRUE_ID
} else {
crate::scheme::arena::FALSE_ID
}
}
_ => return Err("positive?: not a number".to_string()),
})
}
#[inline]
pub fn negative_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("negative? requires exactly 1 argument".to_string());
}
Ok(match arena.get(args[0]) {
ValueData::Integer(n) => {
if *n < 0 {
crate::scheme::arena::TRUE_ID
} else {
crate::scheme::arena::FALSE_ID
}
}
ValueData::Real(f) => {
if *f < 0.0 {
crate::scheme::arena::TRUE_ID
} else {
crate::scheme::arena::FALSE_ID
}
}
_ => return Err("negative?: not a number".to_string()),
})
}
#[inline]
pub fn odd_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("odd? requires exactly 1 argument".to_string());
}
match arena.get(args[0]) {
ValueData::Integer(n) => {
Ok(if n % 2 != 0 {
crate::scheme::arena::TRUE_ID
} else {
crate::scheme::arena::FALSE_ID
})
}
_ => Err("odd?: not an integer".to_string()),
}
}
#[inline]
pub fn even_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("even? requires exactly 1 argument".to_string());
}
match arena.get(args[0]) {
ValueData::Integer(n) => {
Ok(if n % 2 == 0 {
crate::scheme::arena::TRUE_ID
} else {
crate::scheme::arena::FALSE_ID
})
}
_ => Err("even?: not an integer".to_string()),
}
}
fn to_number(arena: &Arena, id: ValueId) -> Result<f64, String> {
match arena.get(id) {
ValueData::Integer(n) => Ok(*n as f64),
ValueData::Real(f) => Ok(*f),
ValueData::Quantity { magnitude, unit } => {
Ok(unit.to_inches(*magnitude))
}
_ => Err("not a number".to_string()),
}
}
fn from_number(arena: &mut Arena, n: f64) -> ValueId {
if n.fract() == 0.0 && n.is_finite() && n >= i64::MIN as f64 && n <= i64::MAX as f64 {
arena.int(n as i64)
} else {
arena.real(n)
}
}
pub fn add(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.is_empty() {
return Ok(arena.int(0));
}
let mut sum = 0.0;
let mut all_ints = true;
let mut int_sum = 0i64;
let mut quantity_magnitude: Option<f64> = None;
let mut quantity_unit: Option<crate::scheme::value::Unit> = None;
for &arg in args {
match arena.get(arg) {
ValueData::Integer(n) => {
if quantity_magnitude.is_none() {
int_sum = int_sum.wrapping_add(*n);
}
sum += *n as f64;
}
ValueData::Real(f) => {
all_ints = false;
sum += f;
}
ValueData::Quantity { magnitude, unit } => {
if let Some(_existing_unit) = quantity_unit {
quantity_magnitude = Some(quantity_magnitude.unwrap() + magnitude);
} else {
quantity_magnitude = Some(*magnitude + sum);
quantity_unit = Some(*unit);
sum = 0.0; }
all_ints = false;
}
_ => return Err("+: not a number".to_string()),
}
}
if let (Some(mag), Some(unit)) = (quantity_magnitude, quantity_unit) {
Ok(arena.alloc(ValueData::Quantity {
magnitude: mag + sum,
unit,
}))
} else {
Ok(if all_ints {
arena.int(int_sum)
} else {
arena.real(sum)
})
}
}
pub fn subtract(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.is_empty() {
return Err("-: requires at least 1 argument".to_string());
}
if args.len() == 1 {
match arena.get(args[0]) {
ValueData::Integer(n) => return Ok(arena.int(-n)),
ValueData::Real(f) => return Ok(arena.real(-f)),
_ => return Err("-: not a number".to_string()),
}
}
let mut result = to_number(arena, args[0])?;
let mut all_ints = matches!(arena.get(args[0]), ValueData::Integer(_));
for &arg in &args[1..] {
result -= to_number(arena, arg)?;
if !matches!(arena.get(arg), ValueData::Integer(_)) {
all_ints = false;
}
}
Ok(if all_ints && result.fract() == 0.0 {
arena.int(result as i64)
} else {
arena.real(result)
})
}
pub fn multiply(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.is_empty() {
return Ok(arena.int(1));
}
let mut product = 1.0;
let mut all_ints = true;
let mut int_product = 1i64;
let mut quantity_magnitude: Option<f64> = None;
let mut quantity_unit: Option<crate::scheme::value::Unit> = None;
for &arg in args {
match arena.get(arg) {
ValueData::Integer(n) => {
int_product = int_product.wrapping_mul(*n);
product *= *n as f64;
}
ValueData::Real(f) => {
all_ints = false;
product *= f;
}
ValueData::Quantity { magnitude, unit } => {
if quantity_unit.is_some() {
return Err("*: cannot multiply two quantities".to_string());
}
quantity_magnitude = Some(*magnitude);
quantity_unit = Some(*unit);
all_ints = false;
}
_ => return Err("*: not a number".to_string()),
}
}
if let (Some(mag), Some(unit)) = (quantity_magnitude, quantity_unit) {
Ok(arena.alloc(ValueData::Quantity {
magnitude: mag * product,
unit,
}))
} else {
Ok(if all_ints {
arena.int(int_product)
} else {
arena.real(product)
})
}
}
pub fn divide(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.is_empty() {
return Err("/: requires at least 1 argument".to_string());
}
if args.len() == 1 {
let n = to_number(arena, args[0])?;
if n == 0.0 {
return Err("/: division by zero".to_string());
}
return Ok(arena.real(1.0 / n));
}
let mut result = to_number(arena, args[0])?;
for &arg in &args[1..] {
let n = to_number(arena, arg)?;
if n == 0.0 {
return Err("/: division by zero".to_string());
}
result /= n;
}
Ok(arena.real(result))
}
pub fn quotient(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err("quotient requires exactly 2 arguments".to_string());
}
match (arena.get(args[0]), arena.get(args[1])) {
(ValueData::Integer(n1), ValueData::Integer(n2)) => {
if *n2 == 0 {
return Err("quotient: division by zero".to_string());
}
Ok(arena.int(n1 / n2))
}
_ => Err("quotient: arguments must be integers".to_string()),
}
}
pub fn remainder(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err("remainder requires exactly 2 arguments".to_string());
}
match (arena.get(args[0]), arena.get(args[1])) {
(ValueData::Integer(n1), ValueData::Integer(n2)) => {
if *n2 == 0 {
return Err("remainder: division by zero".to_string());
}
Ok(arena.int(n1 % n2))
}
_ => Err("remainder: arguments must be integers".to_string()),
}
}
pub fn modulo(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err("modulo requires exactly 2 arguments".to_string());
}
match (arena.get(args[0]), arena.get(args[1])) {
(ValueData::Integer(n1), ValueData::Integer(n2)) => {
if *n2 == 0 {
return Err("modulo: division by zero".to_string());
}
Ok(arena.int(n1.rem_euclid(*n2)))
}
_ => Err("modulo: arguments must be integers".to_string()),
}
}
pub fn num_eq(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() < 2 {
return Err("=: requires at least 2 arguments".to_string());
}
let first = to_number(arena, args[0])?;
for &arg in &args[1..] {
if to_number(arena, arg)? != first {
return Ok(crate::scheme::arena::FALSE_ID);
}
}
Ok(crate::scheme::arena::TRUE_ID)
}
pub fn num_lt(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() < 2 {
return Err("<: requires at least 2 arguments".to_string());
}
let mut prev = to_number(arena, args[0])?;
for &arg in &args[1..] {
let curr = to_number(arena, arg)?;
if prev >= curr {
return Ok(crate::scheme::arena::FALSE_ID);
}
prev = curr;
}
Ok(crate::scheme::arena::TRUE_ID)
}
pub fn num_gt(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() < 2 {
return Err(">: requires at least 2 arguments".to_string());
}
let mut prev = to_number(arena, args[0])?;
for &arg in &args[1..] {
let curr = to_number(arena, arg)?;
if prev <= curr {
return Ok(crate::scheme::arena::FALSE_ID);
}
prev = curr;
}
Ok(crate::scheme::arena::TRUE_ID)
}
pub fn num_le(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() < 2 {
return Err("<=: requires at least 2 arguments".to_string());
}
let mut prev = to_number(arena, args[0])?;
for &arg in &args[1..] {
let curr = to_number(arena, arg)?;
if prev > curr {
return Ok(crate::scheme::arena::FALSE_ID);
}
prev = curr;
}
Ok(crate::scheme::arena::TRUE_ID)
}
pub fn num_ge(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() < 2 {
return Err(">=: requires at least 2 arguments".to_string());
}
let mut prev = to_number(arena, args[0])?;
for &arg in &args[1..] {
let curr = to_number(arena, arg)?;
if prev < curr {
return Ok(crate::scheme::arena::FALSE_ID);
}
prev = curr;
}
Ok(crate::scheme::arena::TRUE_ID)
}
pub fn abs(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("abs requires exactly 1 argument".to_string());
}
match arena.get(args[0]) {
ValueData::Integer(n) => Ok(arena.int(n.abs())),
ValueData::Real(f) => Ok(arena.real(f.abs())),
_ => Err("abs: not a number".to_string()),
}
}
pub fn min(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.is_empty() {
return Err("min requires at least 1 argument".to_string());
}
let mut min_val = to_number(arena, args[0])?;
let mut all_ints = matches!(arena.get(args[0]), ValueData::Integer(_));
for &arg in &args[1..] {
let val = to_number(arena, arg)?;
if val < min_val {
min_val = val;
}
if !matches!(arena.get(arg), ValueData::Integer(_)) {
all_ints = false;
}
}
Ok(if all_ints {
arena.int(min_val as i64)
} else {
arena.real(min_val)
})
}
pub fn max(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.is_empty() {
return Err("max requires at least 1 argument".to_string());
}
let mut max_val = to_number(arena, args[0])?;
let mut all_ints = matches!(arena.get(args[0]), ValueData::Integer(_));
for &arg in &args[1..] {
let val = to_number(arena, arg)?;
if val > max_val {
max_val = val;
}
if !matches!(arena.get(arg), ValueData::Integer(_)) {
all_ints = false;
}
}
Ok(if all_ints {
arena.int(max_val as i64)
} else {
arena.real(max_val)
})
}
pub fn floor(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("floor requires exactly 1 argument".to_string());
}
let n = to_number(arena, args[0])?;
Ok(arena.int(n.floor() as i64))
}
pub fn ceiling(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("ceiling requires exactly 1 argument".to_string());
}
let n = to_number(arena, args[0])?;
Ok(arena.int(n.ceil() as i64))
}
pub fn truncate(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("truncate requires exactly 1 argument".to_string());
}
let n = to_number(arena, args[0])?;
Ok(arena.int(n.trunc() as i64))
}
pub fn round(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("round requires exactly 1 argument".to_string());
}
let n = to_number(arena, args[0])?;
Ok(arena.int(n.round() as i64))
}
pub fn sqrt(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("sqrt requires exactly 1 argument".to_string());
}
let n = to_number(arena, args[0])?;
Ok(arena.real(n.sqrt()))
}
pub fn sin(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("sin requires exactly 1 argument".to_string());
}
let n = to_number(arena, args[0])?;
Ok(arena.real(n.sin()))
}
pub fn cos(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("cos requires exactly 1 argument".to_string());
}
let n = to_number(arena, args[0])?;
Ok(arena.real(n.cos()))
}
pub fn tan(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("tan requires exactly 1 argument".to_string());
}
let n = to_number(arena, args[0])?;
Ok(arena.real(n.tan()))
}
pub fn asin(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("asin requires exactly 1 argument".to_string());
}
let n = to_number(arena, args[0])?;
Ok(arena.real(n.asin()))
}
pub fn acos(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("acos requires exactly 1 argument".to_string());
}
let n = to_number(arena, args[0])?;
Ok(arena.real(n.acos()))
}
pub fn atan(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("atan requires exactly 1 argument".to_string());
}
let n = to_number(arena, args[0])?;
Ok(arena.real(n.atan()))
}
pub fn exp(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("exp requires exactly 1 argument".to_string());
}
let n = to_number(arena, args[0])?;
Ok(arena.real(n.exp()))
}
pub fn log(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("log requires exactly 1 argument".to_string());
}
let n = to_number(arena, args[0])?;
Ok(arena.real(n.ln()))
}
pub fn expt(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err("expt requires exactly 2 arguments".to_string());
}
let base = to_number(arena, args[0])?;
let exp = to_number(arena, args[1])?;
Ok(arena.real(base.powf(exp)))
}
pub fn string_length(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("string-length requires exactly 1 argument".to_string());
}
match arena.get(args[0]) {
ValueData::String(s) => Ok(arena.int(s.chars().count() as i64)),
_ => Err("string-length: not a string".to_string()),
}
}
pub fn string_ref(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err("string-ref requires exactly 2 arguments".to_string());
}
let s = match arena.get(args[0]) {
ValueData::String(s) => s,
_ => return Err("string-ref: first argument must be a string".to_string()),
};
let k = match arena.get(args[1]) {
ValueData::Integer(n) if *n >= 0 => *n as usize,
ValueData::Integer(_) => return Err("string-ref: index must be non-negative".to_string()),
_ => return Err("string-ref: second argument must be an integer".to_string()),
};
match s.chars().nth(k) {
Some(c) => Ok(arena.char(c)),
None => Err("string-ref: index out of bounds".to_string()),
}
}
pub fn substring(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 3 {
return Err("substring requires exactly 3 arguments".to_string());
}
let s = get_string_data(arena, args[0], "substring")?;
let start = match arena.get(args[1]) {
ValueData::Integer(n) if *n >= 0 => *n as usize,
ValueData::Integer(_) => return Err("substring: start must be non-negative".to_string()),
_ => return Err("substring: start must be an integer".to_string()),
};
let end = match arena.get(args[2]) {
ValueData::Integer(n) if *n >= 0 => *n as usize,
ValueData::Integer(_) => return Err("substring: end must be non-negative".to_string()),
_ => return Err("substring: end must be an integer".to_string()),
};
let chars: Vec<char> = s.chars().collect();
let len = chars.len();
let clamped_start = start.min(len);
let clamped_end = end.min(len);
if clamped_start > clamped_end {
return Err("substring: start must be <= end".to_string());
}
let result: String = chars[clamped_start..clamped_end].iter().collect();
Ok(arena.string(result))
}
pub fn string_append(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
let mut result = String::new();
for &arg in args {
match arena.get(arg) {
ValueData::String(s) => result.push_str(s),
ValueData::Unspecified => {}, _ => return Err("string-append: all arguments must be strings".to_string()),
}
}
Ok(arena.string(result))
}
fn get_string_data<'a>(arena: &'a Arena, id: ValueId, prim_name: &str) -> Result<std::borrow::Cow<'a, str>, String> {
match arena.get(id) {
ValueData::String(s) => Ok(std::borrow::Cow::Borrowed(s.as_str())),
ValueData::Symbol(s) => Ok(std::borrow::Cow::Borrowed(s.as_ref())),
_ => Err(format!("{}: not a string or symbol", prim_name)),
}
}
pub fn string_eq(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() < 2 {
return Err("string=?: requires at least 2 arguments".to_string());
}
let first = get_string_data(arena, args[0], "string=?")?;
for &arg in &args[1..] {
let s = get_string_data(arena, arg, "string=?")?;
if s != first {
return Ok(crate::scheme::arena::FALSE_ID);
}
}
Ok(crate::scheme::arena::TRUE_ID)
}
pub fn string_lt(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() < 2 {
return Err("string<?: requires at least 2 arguments".to_string());
}
let mut prev = get_string_data(arena, args[0], "string<?")?.into_owned();
for &arg in &args[1..] {
let s = get_string_data(arena, arg, "string<?")?;
if prev.as_str() >= s.as_ref() {
return Ok(crate::scheme::arena::FALSE_ID);
}
prev = s.into_owned();
}
Ok(crate::scheme::arena::TRUE_ID)
}
pub fn string_gt(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() < 2 {
return Err("string>?: requires at least 2 arguments".to_string());
}
let mut prev = get_string_data(arena, args[0], "string>?")?.into_owned();
for &arg in &args[1..] {
let s = get_string_data(arena, arg, "string>?")?;
if prev.as_str() <= s.as_ref() {
return Ok(crate::scheme::arena::FALSE_ID);
}
prev = s.into_owned();
}
Ok(crate::scheme::arena::TRUE_ID)
}
pub fn string_le(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() < 2 {
return Err("string<=?: requires at least 2 arguments".to_string());
}
let mut prev = get_string_data(arena, args[0], "string<=?")?.into_owned();
for &arg in &args[1..] {
let s = get_string_data(arena, arg, "string<=?")?;
if prev.as_str() > s.as_ref() {
return Ok(crate::scheme::arena::FALSE_ID);
}
prev = s.into_owned();
}
Ok(crate::scheme::arena::TRUE_ID)
}
pub fn string_ge(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() < 2 {
return Err("string>=?: requires at least 2 arguments".to_string());
}
let mut prev = get_string_data(arena, args[0], "string>=?")?.into_owned();
for &arg in &args[1..] {
let s = get_string_data(arena, arg, "string>=?")?;
if prev.as_str() < s.as_ref() {
return Ok(crate::scheme::arena::FALSE_ID);
}
prev = s.into_owned();
}
Ok(crate::scheme::arena::TRUE_ID)
}
pub fn string_ci_eq(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() < 2 {
return Err("string-ci=?: requires at least 2 arguments".to_string());
}
let first = get_string_data(arena, args[0], "string-ci=?")?.to_lowercase();
for &arg in &args[1..] {
let s = get_string_data(arena, arg, "string-ci=?")?;
if s.to_lowercase() != first {
return Ok(crate::scheme::arena::FALSE_ID);
}
}
Ok(crate::scheme::arena::TRUE_ID)
}
pub fn string_ci_lt(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() < 2 {
return Err("string-ci<?: requires at least 2 arguments".to_string());
}
let mut prev = get_string_data(arena, args[0], "string-ci<?")?.to_lowercase();
for &arg in &args[1..] {
let s = get_string_data(arena, arg, "string-ci<?")?;
let curr = s.to_lowercase();
if prev >= curr {
return Ok(crate::scheme::arena::FALSE_ID);
}
prev = curr;
}
Ok(crate::scheme::arena::TRUE_ID)
}
pub fn string_ci_gt(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() < 2 {
return Err("string-ci>?: requires at least 2 arguments".to_string());
}
let mut prev = get_string_data(arena, args[0], "string-ci>?")?.to_lowercase();
for &arg in &args[1..] {
let s = get_string_data(arena, arg, "string-ci>?")?;
let curr = s.to_lowercase();
if prev <= curr {
return Ok(crate::scheme::arena::FALSE_ID);
}
prev = curr;
}
Ok(crate::scheme::arena::TRUE_ID)
}
pub fn string_ci_le(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() < 2 {
return Err("string-ci<=?: requires at least 2 arguments".to_string());
}
let mut prev = get_string_data(arena, args[0], "string-ci<=?")?.to_lowercase();
for &arg in &args[1..] {
let s = get_string_data(arena, arg, "string-ci<=?")?;
let curr = s.to_lowercase();
if prev > curr {
return Ok(crate::scheme::arena::FALSE_ID);
}
prev = curr;
}
Ok(crate::scheme::arena::TRUE_ID)
}
pub fn string_ci_ge(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() < 2 {
return Err("string-ci>=?: requires at least 2 arguments".to_string());
}
let mut prev = get_string_data(arena, args[0], "string-ci>=?")?.to_lowercase();
for &arg in &args[1..] {
let s = get_string_data(arena, arg, "string-ci>=?")?;
let curr = s.to_lowercase();
if prev < curr {
return Ok(crate::scheme::arena::FALSE_ID);
}
prev = curr;
}
Ok(crate::scheme::arena::TRUE_ID)
}
#[inline]
pub fn char_eq(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err("char=? requires exactly 2 arguments".to_string());
}
let c1 = match arena.get(args[0]) {
ValueData::Char(c) => *c,
_ => return Err("char=?: not a character".to_string()),
};
let c2 = match arena.get(args[1]) {
ValueData::Char(c) => *c,
_ => return Err("char=?: not a character".to_string()),
};
Ok(if c1 == c2 {
crate::scheme::arena::TRUE_ID
} else {
crate::scheme::arena::FALSE_ID
})
}
#[inline]
pub fn char_lt(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err("char<? requires exactly 2 arguments".to_string());
}
let c1 = match arena.get(args[0]) {
ValueData::Char(c) => *c,
_ => return Err("char<?: not a character".to_string()),
};
let c2 = match arena.get(args[1]) {
ValueData::Char(c) => *c,
_ => return Err("char<?: not a character".to_string()),
};
Ok(if c1 < c2 {
crate::scheme::arena::TRUE_ID
} else {
crate::scheme::arena::FALSE_ID
})
}
#[inline]
pub fn char_gt(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err("char>? requires exactly 2 arguments".to_string());
}
let c1 = match arena.get(args[0]) {
ValueData::Char(c) => *c,
_ => return Err("char>?: not a character".to_string()),
};
let c2 = match arena.get(args[1]) {
ValueData::Char(c) => *c,
_ => return Err("char>?: not a character".to_string()),
};
Ok(if c1 > c2 {
crate::scheme::arena::TRUE_ID
} else {
crate::scheme::arena::FALSE_ID
})
}
#[inline]
pub fn char_le(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err("char<=? requires exactly 2 arguments".to_string());
}
let c1 = match arena.get(args[0]) {
ValueData::Char(c) => *c,
_ => return Err("char<=?: not a character".to_string()),
};
let c2 = match arena.get(args[1]) {
ValueData::Char(c) => *c,
_ => return Err("char<=?: not a character".to_string()),
};
Ok(if c1 <= c2 {
crate::scheme::arena::TRUE_ID
} else {
crate::scheme::arena::FALSE_ID
})
}
#[inline]
pub fn char_ge(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err("char>=? requires exactly 2 arguments".to_string());
}
let c1 = match arena.get(args[0]) {
ValueData::Char(c) => *c,
_ => return Err("char>=?: not a character".to_string()),
};
let c2 = match arena.get(args[1]) {
ValueData::Char(c) => *c,
_ => return Err("char>=?: not a character".to_string()),
};
Ok(if c1 >= c2 {
crate::scheme::arena::TRUE_ID
} else {
crate::scheme::arena::FALSE_ID
})
}
#[inline]
pub fn char_ci_eq(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err("char-ci=? requires exactly 2 arguments".to_string());
}
let c1 = match arena.get(args[0]) {
ValueData::Char(c) => c.to_ascii_lowercase(),
_ => return Err("char-ci=?: not a character".to_string()),
};
let c2 = match arena.get(args[1]) {
ValueData::Char(c) => c.to_ascii_lowercase(),
_ => return Err("char-ci=?: not a character".to_string()),
};
Ok(if c1 == c2 {
crate::scheme::arena::TRUE_ID
} else {
crate::scheme::arena::FALSE_ID
})
}
#[inline]
pub fn char_ci_lt(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err("char-ci<? requires exactly 2 arguments".to_string());
}
let c1 = match arena.get(args[0]) {
ValueData::Char(c) => c.to_ascii_lowercase(),
_ => return Err("char-ci<?: not a character".to_string()),
};
let c2 = match arena.get(args[1]) {
ValueData::Char(c) => c.to_ascii_lowercase(),
_ => return Err("char-ci<?: not a character".to_string()),
};
Ok(if c1 < c2 {
crate::scheme::arena::TRUE_ID
} else {
crate::scheme::arena::FALSE_ID
})
}
#[inline]
pub fn char_ci_gt(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err("char-ci>? requires exactly 2 arguments".to_string());
}
let c1 = match arena.get(args[0]) {
ValueData::Char(c) => c.to_ascii_lowercase(),
_ => return Err("char-ci>?: not a character".to_string()),
};
let c2 = match arena.get(args[1]) {
ValueData::Char(c) => c.to_ascii_lowercase(),
_ => return Err("char-ci>?: not a character".to_string()),
};
Ok(if c1 > c2 {
crate::scheme::arena::TRUE_ID
} else {
crate::scheme::arena::FALSE_ID
})
}
#[inline]
pub fn char_ci_le(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err("char-ci<=? requires exactly 2 arguments".to_string());
}
let c1 = match arena.get(args[0]) {
ValueData::Char(c) => c.to_ascii_lowercase(),
_ => return Err("char-ci<=?: not a character".to_string()),
};
let c2 = match arena.get(args[1]) {
ValueData::Char(c) => c.to_ascii_lowercase(),
_ => return Err("char-ci<=?: not a character".to_string()),
};
Ok(if c1 <= c2 {
crate::scheme::arena::TRUE_ID
} else {
crate::scheme::arena::FALSE_ID
})
}
#[inline]
pub fn char_ci_ge(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err("char-ci>=? requires exactly 2 arguments".to_string());
}
let c1 = match arena.get(args[0]) {
ValueData::Char(c) => c.to_ascii_lowercase(),
_ => return Err("char-ci>=?: not a character".to_string()),
};
let c2 = match arena.get(args[1]) {
ValueData::Char(c) => c.to_ascii_lowercase(),
_ => return Err("char-ci>=?: not a character".to_string()),
};
Ok(if c1 >= c2 {
crate::scheme::arena::TRUE_ID
} else {
crate::scheme::arena::FALSE_ID
})
}
pub fn char_upcase(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("char-upcase requires exactly 1 argument".to_string());
}
match arena.get(args[0]) {
ValueData::Char(c) => Ok(arena.char(c.to_ascii_uppercase())),
_ => Err("char-upcase: not a character".to_string()),
}
}
pub fn char_downcase(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("char-downcase requires exactly 1 argument".to_string());
}
match arena.get(args[0]) {
ValueData::Char(c) => Ok(arena.char(c.to_ascii_lowercase())),
_ => Err("char-downcase: not a character".to_string()),
}
}
#[inline]
pub fn char_alphabetic_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("char-alphabetic? requires exactly 1 argument".to_string());
}
match arena.get(args[0]) {
ValueData::Char(c) => Ok(if c.is_alphabetic() {
crate::scheme::arena::TRUE_ID
} else {
crate::scheme::arena::FALSE_ID
}),
_ => Err("char-alphabetic?: not a character".to_string()),
}
}
#[inline]
pub fn char_numeric_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("char-numeric? requires exactly 1 argument".to_string());
}
match arena.get(args[0]) {
ValueData::Char(c) => Ok(if c.is_numeric() {
crate::scheme::arena::TRUE_ID
} else {
crate::scheme::arena::FALSE_ID
}),
_ => Err("char-numeric?: not a character".to_string()),
}
}
#[inline]
pub fn char_whitespace_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("char-whitespace? requires exactly 1 argument".to_string());
}
match arena.get(args[0]) {
ValueData::Char(c) => Ok(if c.is_whitespace() {
crate::scheme::arena::TRUE_ID
} else {
crate::scheme::arena::FALSE_ID
}),
_ => Err("char-whitespace?: not a character".to_string()),
}
}
pub fn char_to_integer(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("char->integer requires exactly 1 argument".to_string());
}
match arena.get(args[0]) {
ValueData::Char(c) => Ok(arena.int(*c as i64)),
_ => Err("char->integer: not a character".to_string()),
}
}
pub fn integer_to_char(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("integer->char requires exactly 1 argument".to_string());
}
match arena.get(args[0]) {
ValueData::Integer(n) => {
if *n < 0 || *n > 0x10FFFF {
return Err(format!("integer->char: invalid code point: {}", n));
}
match char::from_u32(*n as u32) {
Some(c) => Ok(arena.char(c)),
None => Err(format!("integer->char: invalid Unicode code point: {}", n)),
}
}
_ => Err("integer->char: not an integer".to_string()),
}
}
pub fn char_property(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("char-property requires exactly 1 argument".to_string());
}
match arena.get(args[0]) {
ValueData::Char(_) => Ok(arena.string("unknown".to_string())),
_ => Err("char-property: not a character".to_string()),
}
}
pub fn char_script_case(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("char-script-case requires exactly 1 argument".to_string());
}
match arena.get(args[0]) {
ValueData::Char(_) => Ok(arena.symbol(std::rc::Rc::from("unknown"))),
_ => Err("char-script-case: not a character".to_string()),
}
}
pub fn symbol_to_string(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("symbol->string requires exactly 1 argument".to_string());
}
match arena.get(args[0]) {
ValueData::Symbol(s) => Ok(arena.string(s.to_string())),
_ => Err("symbol->string: not a symbol".to_string()),
}
}
pub fn string_to_symbol(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("string->symbol requires exactly 1 argument".to_string());
}
match arena.get(args[0]) {
ValueData::String(s) => Ok(arena.symbol(std::rc::Rc::from(s.as_str()))),
_ => Err("string->symbol: not a string".to_string()),
}
}
#[inline]
pub fn keyword_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("keyword? requires exactly 1 argument".to_string());
}
Ok(match arena.get(args[0]) {
ValueData::Keyword(_) => crate::scheme::arena::TRUE_ID,
_ => crate::scheme::arena::FALSE_ID,
})
}
pub fn keyword_to_string(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("keyword->string requires exactly 1 argument".to_string());
}
match arena.get(args[0]) {
ValueData::Keyword(k) => Ok(arena.string(k.to_string())),
_ => Err("keyword->string: not a keyword".to_string()),
}
}
pub fn string_to_keyword(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("string->keyword requires exactly 1 argument".to_string());
}
match arena.get(args[0]) {
ValueData::String(s) => Ok(arena.keyword(std::rc::Rc::from(s.as_str()))),
_ => Err("string->keyword: not a string".to_string()),
}
}
fn eq(arena: &Arena, a: ValueId, b: ValueId) -> bool {
if a == b {
return true;
}
match (arena.get(a), arena.get(b)) {
(ValueData::Integer(n1), ValueData::Integer(n2)) => n1 == n2,
(ValueData::Char(c1), ValueData::Char(c2)) => c1 == c2,
(ValueData::Bool(b1), ValueData::Bool(b2)) => b1 == b2,
(ValueData::Symbol(s1), ValueData::Symbol(s2)) => s1 == s2,
_ => false, }
}
fn eqv(arena: &Arena, a: ValueId, b: ValueId) -> bool {
if a == b {
return true;
}
match (arena.get(a), arena.get(b)) {
(ValueData::Integer(n1), ValueData::Integer(n2)) => n1 == n2,
(ValueData::Real(f1), ValueData::Real(f2)) => f1 == f2,
(ValueData::Char(c1), ValueData::Char(c2)) => c1 == c2,
(ValueData::Bool(b1), ValueData::Bool(b2)) => b1 == b2,
(ValueData::Symbol(s1), ValueData::Symbol(s2)) => s1 == s2,
_ => false,
}
}
pub fn memq(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err("memq requires exactly 2 arguments".to_string());
}
let obj = args[0];
let mut current = args[1];
loop {
if current == crate::scheme::arena::NIL_ID {
return Ok(crate::scheme::arena::FALSE_ID);
}
match arena.get(current) {
ValueData::Pair { car, cdr, .. } => {
if eq(arena, obj, *car) {
return Ok(current);
}
current = *cdr;
}
_ => return Err("memq: not a proper list".to_string()),
}
}
}
pub fn memv(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err("memv requires exactly 2 arguments".to_string());
}
let obj = args[0];
let mut current = args[1];
loop {
if current == crate::scheme::arena::NIL_ID {
return Ok(crate::scheme::arena::FALSE_ID);
}
match arena.get(current) {
ValueData::Pair { car, cdr, .. } => {
if eqv(arena, obj, *car) {
return Ok(current);
}
current = *cdr;
}
_ => return Err("memv: not a proper list".to_string()),
}
}
}
pub fn member(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err("member requires exactly 2 arguments".to_string());
}
let obj = args[0];
let mut current = args[1];
loop {
if current == crate::scheme::arena::NIL_ID {
return Ok(crate::scheme::arena::FALSE_ID);
}
match arena.get(current) {
ValueData::Pair { car, cdr, .. } => {
if equal_impl(arena, obj, *car) {
return Ok(current);
}
current = *cdr;
}
_ => return Err("member: not a proper list".to_string()),
}
}
}
pub fn assq(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err("assq requires exactly 2 arguments".to_string());
}
let obj = args[0];
let mut current = args[1];
loop {
if current == crate::scheme::arena::NIL_ID {
return Ok(crate::scheme::arena::FALSE_ID);
}
match arena.get(current) {
ValueData::Pair { car, cdr, .. } => {
if let ValueData::Pair { car: inner_car, .. } = arena.get(*car) {
if eq(arena, obj, *inner_car) {
return Ok(*car);
}
}
current = *cdr;
}
_ => return Err("assq: not a proper list".to_string()),
}
}
}
pub fn assv(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err("assv requires exactly 2 arguments".to_string());
}
let obj = args[0];
let mut current = args[1];
loop {
if current == crate::scheme::arena::NIL_ID {
return Ok(crate::scheme::arena::FALSE_ID);
}
match arena.get(current) {
ValueData::Pair { car, cdr, .. } => {
if let ValueData::Pair { car: inner_car, .. } = arena.get(*car) {
if eqv(arena, obj, *inner_car) {
return Ok(*car);
}
}
current = *cdr;
}
_ => return Err("assv: not a proper list".to_string()),
}
}
}
pub fn assoc(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err("assoc requires exactly 2 arguments".to_string());
}
let obj = args[0];
let mut current = args[1];
loop {
if current == crate::scheme::arena::NIL_ID {
return Ok(crate::scheme::arena::FALSE_ID);
}
match arena.get(current) {
ValueData::Pair { car, cdr, .. } => {
if let ValueData::Pair { car: inner_car, .. } = arena.get(*car) {
if equal_impl(arena, obj, *inner_car) {
return Ok(*car);
}
}
current = *cdr;
}
_ => return Err("assoc: not a proper list".to_string()),
}
}
}
#[inline]
pub fn not(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("not requires exactly 1 argument".to_string());
}
let val = args[0];
Ok(if val == crate::scheme::arena::FALSE_ID {
crate::scheme::arena::TRUE_ID
} else {
crate::scheme::arena::FALSE_ID
})
}
#[inline]
pub fn eq_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err("eq? requires exactly 2 arguments".to_string());
}
Ok(if eq(arena, args[0], args[1]) {
crate::scheme::arena::TRUE_ID
} else {
crate::scheme::arena::FALSE_ID
})
}
#[inline]
pub fn eqv_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err("eqv? requires exactly 2 arguments".to_string());
}
Ok(if eqv(arena, args[0], args[1]) {
crate::scheme::arena::TRUE_ID
} else {
crate::scheme::arena::FALSE_ID
})
}
#[inline]
pub fn caar(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("caar requires exactly 1 argument".to_string());
}
let car1 = car(arena, args)?;
car(arena, &[car1])
}
#[inline]
pub fn cdar(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("cdar requires exactly 1 argument".to_string());
}
let car_val = car(arena, args)?;
cdr(arena, &[car_val])
}
#[inline]
pub fn cddr(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("cddr requires exactly 1 argument".to_string());
}
let cdr1 = cdr(arena, args)?;
cdr(arena, &[cdr1])
}
#[inline]
pub fn caaar(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("caaar requires exactly 1 argument".to_string());
}
let car1 = car(arena, args)?;
let car2 = car(arena, &[car1])?;
car(arena, &[car2])
}
#[inline]
pub fn caadr(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("caadr requires exactly 1 argument".to_string());
}
let cdr1 = cdr(arena, args)?;
let car2 = car(arena, &[cdr1])?;
car(arena, &[car2])
}
#[inline]
pub fn cadar(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("cadar requires exactly 1 argument".to_string());
}
let car1 = car(arena, args)?;
let cdr2 = cdr(arena, &[car1])?;
car(arena, &[cdr2])
}
#[inline]
pub fn cdaar(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("cdaar requires exactly 1 argument".to_string());
}
let car1 = car(arena, args)?;
let car2 = car(arena, &[car1])?;
cdr(arena, &[car2])
}
#[inline]
pub fn cdadr(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("cdadr requires exactly 1 argument".to_string());
}
let cdr1 = cdr(arena, args)?;
let car2 = car(arena, &[cdr1])?;
cdr(arena, &[car2])
}
#[inline]
pub fn cddar(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("cddar requires exactly 1 argument".to_string());
}
let car1 = car(arena, args)?;
let cdr2 = cdr(arena, &[car1])?;
cdr(arena, &[cdr2])
}
#[inline]
pub fn cdddr(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("cdddr requires exactly 1 argument".to_string());
}
let cdr1 = cdr(arena, args)?;
let cdr2 = cdr(arena, &[cdr1])?;
cdr(arena, &[cdr2])
}
#[inline]
pub fn vector(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
Ok(arena.vector(args.to_vec()))
}
#[inline]
pub fn make_vector(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.is_empty() || args.len() > 2 {
return Err("make-vector requires 1 or 2 arguments".to_string());
}
match arena.get(args[0]) {
ValueData::Integer(k) if *k >= 0 => {
let size = *k as usize;
let fill = if args.len() == 2 {
args[1]
} else {
crate::scheme::arena::UNSPECIFIED_ID
};
let elements = vec![fill; size];
Ok(arena.vector(elements))
}
ValueData::Integer(_) => Err("make-vector: size must be non-negative".to_string()),
_ => Err("make-vector: first argument must be an integer".to_string()),
}
}
#[inline]
pub fn vector_length(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("vector-length requires exactly 1 argument".to_string());
}
match arena.get(args[0]) {
ValueData::Vector(elements) => Ok(arena.int(elements.len() as i64)),
_ => Err("vector-length: argument must be a vector".to_string()),
}
}
#[inline]
pub fn vector_ref(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err("vector-ref requires exactly 2 arguments".to_string());
}
let vec_id = args[0];
match arena.get(vec_id) {
ValueData::Vector(elements) => {
match arena.get(args[1]) {
ValueData::Integer(k) if *k >= 0 => {
let index = *k as usize;
if index < elements.len() {
Ok(elements[index])
} else {
Err(format!("vector-ref: index {} out of bounds (length {})", index, elements.len()))
}
}
ValueData::Integer(_) => Err("vector-ref: index must be non-negative".to_string()),
_ => Err("vector-ref: second argument must be an integer".to_string()),
}
}
_ => Err("vector-ref: first argument must be a vector".to_string()),
}
}
#[inline]
pub fn vector_set(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 3 {
return Err("vector-set! requires exactly 3 arguments".to_string());
}
let vec_id = args[0];
let new_val = args[2];
match arena.get(args[1]) {
ValueData::Integer(k) if *k >= 0 => {
let index = *k as usize;
match arena.get_mut(vec_id) {
ValueData::Vector(ref mut elements) => {
if index < elements.len() {
elements[index] = new_val;
Ok(crate::scheme::arena::UNSPECIFIED_ID)
} else {
Err(format!("vector-set!: index {} out of bounds (length {})", index, elements.len()))
}
}
_ => Err("vector-set!: first argument must be a vector".to_string()),
}
}
ValueData::Integer(_) => Err("vector-set!: index must be non-negative".to_string()),
_ => Err("vector-set!: second argument must be an integer".to_string()),
}
}
#[inline]
pub fn vector_to_list(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("vector->list requires exactly 1 argument".to_string());
}
let elements = match arena.get(args[0]) {
ValueData::Vector(elements) => elements.clone(),
_ => return Err("vector->list: argument must be a vector".to_string()),
};
let mut result = crate::scheme::arena::NIL_ID;
for &elem in elements.iter().rev() {
result = arena.cons(elem, result);
}
Ok(result)
}
#[inline]
pub fn list_to_vector(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("list->vector requires exactly 1 argument".to_string());
}
let mut elements = Vec::new();
let mut current = args[0];
loop {
match arena.get(current) {
ValueData::Nil => break,
ValueData::Pair { car, cdr, .. } => {
elements.push(*car);
current = *cdr;
}
_ => return Err("list->vector: argument must be a proper list".to_string()),
}
}
Ok(arena.vector(elements))
}
#[inline]
pub fn vector_fill(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err("vector-fill! requires exactly 2 arguments".to_string());
}
let vec_id = args[0];
let fill = args[1];
match arena.get_mut(vec_id) {
ValueData::Vector(ref mut elements) => {
for elem in elements.iter_mut() {
*elem = fill;
}
Ok(crate::scheme::arena::UNSPECIFIED_ID)
}
_ => Err("vector-fill!: first argument must be a vector".to_string()),
}
}
#[inline]
pub fn vector_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("vector? requires exactly 1 argument".to_string());
}
match arena.get(args[0]) {
ValueData::Vector(_) => Ok(crate::scheme::arena::TRUE_ID),
_ => Ok(crate::scheme::arena::FALSE_ID),
}
}
#[inline]
pub fn procedure_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("procedure? requires exactly 1 argument".to_string());
}
match arena.get(args[0]) {
ValueData::Procedure(_) => Ok(crate::scheme::arena::TRUE_ID),
_ => Ok(crate::scheme::arena::FALSE_ID),
}
}
#[inline]
pub fn set_car(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err("set-car! requires exactly 2 arguments".to_string());
}
let pair_id = args[0];
let new_car = args[1];
match arena.get_mut(pair_id) {
ValueData::Pair { car, .. } => {
*car = new_car;
Ok(crate::scheme::arena::UNSPECIFIED_ID)
}
_ => Err("set-car!: first argument must be a pair".to_string()),
}
}
#[inline]
pub fn set_cdr(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err("set-cdr! requires exactly 2 arguments".to_string());
}
let pair_id = args[0];
let new_cdr = args[1];
match arena.get_mut(pair_id) {
ValueData::Pair { cdr, .. } => {
*cdr = new_cdr;
Ok(crate::scheme::arena::UNSPECIFIED_ID)
}
_ => Err("set-cdr!: first argument must be a pair".to_string()),
}
}
#[inline]
pub fn list_tail(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err("list-tail requires exactly 2 arguments".to_string());
}
let k = match arena.get(args[1]) {
ValueData::Integer(n) if *n >= 0 => *n as usize,
ValueData::Integer(_) => return Err("list-tail: index must be non-negative".to_string()),
_ => return Err("list-tail: second argument must be an integer".to_string()),
};
let mut current = args[0];
for _ in 0..k {
match arena.get(current) {
ValueData::Pair { cdr, .. } => {
current = *cdr;
}
ValueData::Nil => return Err("list-tail: list too short".to_string()),
_ => return Err("list-tail: argument must be a list".to_string()),
}
}
Ok(current)
}
#[inline]
pub fn string_upcase(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("string-upcase requires exactly 1 argument".to_string());
}
match arena.get(args[0]) {
ValueData::String(s) => Ok(arena.string(s.to_uppercase())),
_ => Err("string-upcase: argument must be a string".to_string()),
}
}
#[inline]
pub fn string_downcase(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("string-downcase requires exactly 1 argument".to_string());
}
match arena.get(args[0]) {
ValueData::String(s) => Ok(arena.string(s.to_lowercase())),
_ => Err("string-downcase: argument must be a string".to_string()),
}
}
#[inline]
pub fn case_fold_down(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
string_downcase(arena, args)
}
#[inline]
pub fn string_index(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err("string-index requires exactly 2 arguments".to_string());
}
let s = match arena.get(args[0]) {
ValueData::String(s) => s.clone(),
_ => return Err("string-index: first argument must be a string".to_string()),
};
let search_str = match arena.get(args[1]) {
ValueData::Char(c) => c.to_string(),
ValueData::String(search) => search.clone(),
_ => return Err("string-index: second argument must be a character or string".to_string()),
};
match s.find(search_str.as_str()) {
Some(idx) => {
let char_idx = s[..idx].chars().count();
Ok(arena.int(char_idx as i64))
}
None => Ok(arena.int(-1)), }
}
#[inline]
pub fn string_to_number(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("string->number requires exactly 1 argument".to_string());
}
match arena.get(args[0]) {
ValueData::String(s) => {
if let Ok(n) = s.parse::<i64>() {
return Ok(arena.int(n));
}
if let Ok(f) = s.parse::<f64>() {
return Ok(arena.real(f));
}
Ok(crate::scheme::arena::FALSE_ID)
}
_ => Err("string->number: argument must be a string".to_string()),
}
}
#[inline]
pub fn number_to_string(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("number->string requires exactly 1 argument".to_string());
}
match arena.get(args[0]) {
ValueData::Integer(n) => Ok(arena.string(n.to_string())),
ValueData::Real(f) => Ok(arena.string(f.to_string())),
_ => Err("number->string: argument must be a number".to_string()),
}
}
#[inline]
pub fn string_to_list(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("string->list requires exactly 1 argument".to_string());
}
let s = match arena.get(args[0]) {
ValueData::String(s) => s.clone(),
_ => return Err("string->list: argument must be a string".to_string()),
};
let mut result = crate::scheme::arena::NIL_ID;
for c in s.chars().rev() {
let char_id = arena.char(c);
result = arena.cons(char_id, result);
}
Ok(result)
}
#[inline]
pub fn list_to_string(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("list->string requires exactly 1 argument".to_string());
}
let mut chars = Vec::new();
let mut current = args[0];
loop {
match arena.get(current) {
ValueData::Nil => break,
ValueData::Pair { car, cdr, .. } => {
match arena.get(*car) {
ValueData::Char(c) => {
chars.push(*c);
current = *cdr;
}
_ => return Err("list->string: list must contain only characters".to_string()),
}
}
_ => return Err("list->string: argument must be a proper list".to_string()),
}
}
let s: String = chars.into_iter().collect();
Ok(arena.string(s))
}
#[inline]
pub fn gcd(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.is_empty() {
return Ok(arena.int(0));
}
fn gcd_two(a: i64, b: i64) -> i64 {
let (mut a, mut b) = (a.abs(), b.abs());
while b != 0 {
let temp = b;
b = a % b;
a = temp;
}
a
}
let mut result = 0i64;
for &arg in args {
match arena.get(arg) {
ValueData::Integer(n) => {
result = gcd_two(result, *n);
}
_ => return Err("gcd: all arguments must be integers".to_string()),
}
}
Ok(arena.int(result))
}
#[inline]
pub fn lcm(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.is_empty() {
return Ok(arena.int(1));
}
fn gcd_two(a: i64, b: i64) -> i64 {
let (mut a, mut b) = (a.abs(), b.abs());
while b != 0 {
let temp = b;
b = a % b;
a = temp;
}
a
}
fn lcm_two(a: i64, b: i64) -> i64 {
if a == 0 || b == 0 {
return 0;
}
(a.abs() / gcd_two(a, b)) * b.abs()
}
let mut result = 1i64;
for &arg in args {
match arena.get(arg) {
ValueData::Integer(n) => {
result = lcm_two(result, *n);
}
_ => return Err("lcm: all arguments must be integers".to_string()),
}
}
Ok(arena.int(result))
}
#[inline]
pub fn exact_to_inexact(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("exact->inexact requires exactly 1 argument".to_string());
}
match arena.get(args[0]) {
ValueData::Integer(n) => Ok(arena.real(*n as f64)),
ValueData::Real(_) => Ok(args[0]), _ => Err("exact->inexact: argument must be a number".to_string()),
}
}
#[inline]
pub fn inexact_to_exact(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("inexact->exact requires exactly 1 argument".to_string());
}
match arena.get(args[0]) {
ValueData::Real(f) => Ok(arena.int(f.round() as i64)),
ValueData::Integer(_) => Ok(args[0]), _ => Err("inexact->exact: argument must be a number".to_string()),
}
}
#[inline]
pub fn make_string(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.is_empty() || args.len() > 2 {
return Err("make-string requires 1 or 2 arguments".to_string());
}
let k = match arena.get(args[0]) {
ValueData::Integer(n) if *n >= 0 => *n as usize,
ValueData::Integer(_) => return Err("make-string: length must be non-negative".to_string()),
_ => return Err("make-string: first argument must be an integer".to_string()),
};
let fill_char = if args.len() == 2 {
match arena.get(args[1]) {
ValueData::Char(c) => *c,
_ => return Err("make-string: second argument must be a character".to_string()),
}
} else {
' '
};
let s: String = std::iter::repeat(fill_char).take(k).collect();
Ok(arena.string(s))
}
#[inline]
pub fn string(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
let mut chars = Vec::new();
for &arg in args {
match arena.get(arg) {
ValueData::Char(c) => chars.push(*c),
_ => return Err("string: all arguments must be characters".to_string()),
}
}
let s: String = chars.into_iter().collect();
Ok(arena.string(s))
}
#[inline]
pub fn reverse_bang(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("reverse! requires exactly 1 argument".to_string());
}
let mut pairs = Vec::new();
let mut current = args[0];
loop {
match arena.get(current) {
ValueData::Nil => break,
ValueData::Pair { car, cdr, .. } => {
pairs.push((current, *car));
current = *cdr;
}
_ => return Err("reverse!: argument must be a list".to_string()),
}
}
if pairs.is_empty() {
return Ok(crate::scheme::arena::NIL_ID);
}
let mut prev = crate::scheme::arena::NIL_ID;
for &(pair_id, _car_val) in &pairs {
match arena.get_mut(pair_id) {
ValueData::Pair { cdr, .. } => {
*cdr = prev;
prev = pair_id;
}
_ => unreachable!(),
}
}
Ok(prev)
}
pub fn string_set(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 3 {
return Err("string-set!: expected 3 arguments".to_string());
}
let s = match arena.get(args[0]) {
ValueData::String(s) => s.clone(),
_ => return Err("string-set!: first argument must be a string".to_string()),
};
let k = match arena.get(args[1]) {
ValueData::Integer(n) => *n as usize,
_ => return Err("string-set!: second argument must be an integer".to_string()),
};
let c = match arena.get(args[2]) {
ValueData::Char(c) => *c,
_ => return Err("string-set!: third argument must be a character".to_string()),
};
if k >= s.len() {
return Err(format!("string-set!: index {} out of bounds for string of length {}", k, s.len()));
}
let mut chars: Vec<char> = s.chars().collect();
chars[k] = c;
let new_string: String = chars.into_iter().collect();
Ok(arena.string(new_string))
}
pub fn string_copy(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("string-copy: expected 1 argument".to_string());
}
let s = match arena.get(args[0]) {
ValueData::String(s) => s.clone(),
_ => return Err("string-copy: argument must be a string".to_string()),
};
Ok(arena.string(s))
}
pub fn string_fill(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err("string-fill!: expected 2 arguments".to_string());
}
let s = match arena.get(args[0]) {
ValueData::String(s) => s.clone(),
_ => return Err("string-fill!: first argument must be a string".to_string()),
};
let c = match arena.get(args[1]) {
ValueData::Char(c) => *c,
_ => return Err("string-fill!: second argument must be a character".to_string()),
};
let new_string: String = std::iter::repeat(c).take(s.len()).collect();
Ok(arena.string(new_string))
}
pub fn char_lower_case_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("char-lower-case?: expected 1 argument".to_string());
}
match arena.get(args[0]) {
ValueData::Char(c) => Ok(if c.is_lowercase() { crate::scheme::arena::TRUE_ID } else { crate::scheme::arena::FALSE_ID }),
_ => Err("char-lower-case?: argument must be a character".to_string()),
}
}
pub fn char_upper_case_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("char-upper-case?: expected 1 argument".to_string());
}
match arena.get(args[0]) {
ValueData::Char(c) => Ok(if c.is_uppercase() { crate::scheme::arena::TRUE_ID } else { crate::scheme::arena::FALSE_ID }),
_ => Err("char-upper-case?: argument must be a character".to_string()),
}
}
pub fn last(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("last: expected 1 argument".to_string());
}
let mut current = args[0];
let mut prev_car = crate::scheme::arena::NIL_ID;
loop {
match arena.get(current) {
ValueData::Nil => {
if prev_car == crate::scheme::arena::NIL_ID {
return Err("last: argument must be a non-empty list".to_string());
}
return Ok(prev_car);
}
ValueData::Pair { car, cdr, .. } => {
prev_car = *car;
current = *cdr;
}
_ => return Err("last: argument must be a proper list".to_string()),
}
}
}
pub fn last_pair(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("last-pair: expected 1 argument".to_string());
}
let mut current = args[0];
if matches!(arena.get(current), ValueData::Nil) {
return Err("last-pair: argument must be a non-empty list".to_string());
}
loop {
match arena.get(current) {
ValueData::Pair { cdr, .. } => {
if matches!(arena.get(*cdr), ValueData::Nil) {
return Ok(current);
}
current = *cdr;
}
_ => return Err("last-pair: argument must be a proper list".to_string()),
}
}
}
pub fn list_copy(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("list-copy: expected 1 argument".to_string());
}
let mut result = crate::scheme::arena::NIL_ID;
let mut tail = crate::scheme::arena::NIL_ID;
let mut current = args[0];
loop {
let (car_val, cdr_val) = match arena.get(current) {
ValueData::Nil => {
return Ok(result);
}
ValueData::Pair { car, cdr, .. } => (*car, *cdr),
_ => return Err("list-copy: argument must be a proper list".to_string()),
};
let new_pair = arena.cons(car_val, crate::scheme::arena::NIL_ID);
if result == crate::scheme::arena::NIL_ID {
result = new_pair;
tail = new_pair;
} else {
match arena.get_mut(tail) {
ValueData::Pair { cdr: tail_cdr, .. } => {
*tail_cdr = new_pair;
}
_ => unreachable!(),
}
tail = new_pair;
}
current = cdr_val;
}
}
pub fn append_bang(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.is_empty() {
return Ok(crate::scheme::arena::NIL_ID);
}
if args.len() == 1 {
return Ok(args[0]);
}
let first = args[0];
if matches!(arena.get(first), ValueData::Nil) {
return append_bang(arena, &args[1..]);
}
let mut current = first;
loop {
match arena.get(current) {
ValueData::Pair { cdr, .. } => {
if matches!(arena.get(*cdr), ValueData::Nil) {
let rest = if args.len() > 2 {
append_bang(arena, &args[1..])?
} else {
args[1]
};
match arena.get_mut(current) {
ValueData::Pair { cdr: last_cdr, .. } => {
*last_cdr = rest;
}
_ => unreachable!(),
}
return Ok(first);
}
current = *cdr;
}
_ => return Err("append!: arguments must be lists".to_string()),
}
}
}
pub fn iota(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.is_empty() || args.len() > 3 {
return Err("iota: expected 1 to 3 arguments".to_string());
}
let count = match arena.get(args[0]) {
ValueData::Integer(n) => *n,
_ => return Err("iota: first argument must be an integer".to_string()),
};
let start = if args.len() > 1 {
match arena.get(args[1]) {
ValueData::Integer(n) => *n,
_ => return Err("iota: second argument must be an integer".to_string()),
}
} else {
0
};
let step = if args.len() > 2 {
match arena.get(args[2]) {
ValueData::Integer(n) => *n,
_ => return Err("iota: third argument must be an integer".to_string()),
}
} else {
1
};
if count < 0 {
return Err("iota: count must be non-negative".to_string());
}
let mut result = crate::scheme::arena::NIL_ID;
for i in (0..count).rev() {
let val = arena.int(start + i * step);
result = arena.cons(val, result);
}
Ok(result)
}
pub fn take(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err("take: expected 2 arguments".to_string());
}
let list = args[0];
let n = match arena.get(args[1]) {
ValueData::Integer(n) => *n,
_ => return Err("take: second argument must be an integer".to_string()),
};
if n < 0 {
return Err("take: count must be non-negative".to_string());
}
let mut result = crate::scheme::arena::NIL_ID;
let mut tail = crate::scheme::arena::NIL_ID;
let mut current = list;
let mut count = 0;
while count < n {
let (car_val, cdr_val) = match arena.get(current) {
ValueData::Nil => {
return Err(format!("take: list has only {} elements, cannot take {}", count, n));
}
ValueData::Pair { car, cdr, .. } => (*car, *cdr),
_ => return Err("take: first argument must be a list".to_string()),
};
let new_pair = arena.cons(car_val, crate::scheme::arena::NIL_ID);
if result == crate::scheme::arena::NIL_ID {
result = new_pair;
tail = new_pair;
} else {
match arena.get_mut(tail) {
ValueData::Pair { cdr: tail_cdr, .. } => {
*tail_cdr = new_pair;
}
_ => unreachable!(),
}
tail = new_pair;
}
current = cdr_val;
count += 1;
}
Ok(result)
}
pub fn drop(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err("drop: expected 2 arguments".to_string());
}
let mut list = args[0];
let n = match arena.get(args[1]) {
ValueData::Integer(n) => *n,
_ => return Err("drop: second argument must be an integer".to_string()),
};
if n < 0 {
return Err("drop: count must be non-negative".to_string());
}
for i in 0..n {
match arena.get(list) {
ValueData::Nil => {
return Err(format!("drop: list has only {} elements, cannot drop {}", i, n));
}
ValueData::Pair { cdr, .. } => {
list = *cdr;
}
_ => return Err("drop: first argument must be a list".to_string()),
}
}
Ok(list)
}
pub fn split_at(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err("split-at: expected 2 arguments".to_string());
}
let taken = take(arena, args)?;
let dropped = drop(arena, args)?;
Ok(arena.cons(taken, dropped))
}
pub fn filter(_arena: &Arena, _args: &[ValueId]) -> ArenaResult {
Err("filter: not available in arena mode (requires callback to evaluator)".to_string())
}
pub fn remove(_arena: &Arena, _args: &[ValueId]) -> ArenaResult {
Err("remove: not available in arena mode (requires callback to evaluator)".to_string())
}
pub fn numerator(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("numerator: expected 1 argument".to_string());
}
match arena.get(args[0]) {
ValueData::Integer(n) => Ok(arena.int(*n)),
ValueData::Real(f) => {
Ok(arena.int(*f as i64))
}
_ => Err("numerator: argument must be a number".to_string()),
}
}
pub fn denominator(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("denominator: expected 1 argument".to_string());
}
match arena.get(args[0]) {
ValueData::Integer(_) | ValueData::Real(_) => Ok(arena.int(1)),
_ => Err("denominator: argument must be a number".to_string()),
}
}
pub fn rationalize(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err("rationalize: expected 2 arguments".to_string());
}
match arena.get(args[0]) {
ValueData::Integer(_) | ValueData::Real(_) => Ok(args[0]),
_ => Err("rationalize: first argument must be a number".to_string()),
}
}
pub fn angle(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("angle: expected 1 argument".to_string());
}
match arena.get(args[0]) {
ValueData::Integer(n) => {
if *n < 0 {
Ok(arena.real(std::f64::consts::PI))
} else {
Ok(arena.real(0.0))
}
}
ValueData::Real(f) => {
if *f < 0.0 {
Ok(arena.real(std::f64::consts::PI))
} else {
Ok(arena.real(0.0))
}
}
_ => Err("angle: argument must be a number".to_string()),
}
}
pub fn magnitude(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("magnitude: expected 1 argument".to_string());
}
match arena.get(args[0]) {
ValueData::Integer(n) => Ok(arena.int(n.abs())),
ValueData::Real(f) => Ok(arena.real(f.abs())),
_ => Err("magnitude: argument must be a number".to_string()),
}
}
pub fn string_to_number_radix(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.is_empty() || args.len() > 2 {
return Err("string->number: expected 1 or 2 arguments".to_string());
}
let s = match arena.get(args[0]) {
ValueData::String(s) => s.clone(),
_ => return Err("string->number: first argument must be a string".to_string()),
};
let radix = if args.len() == 2 {
match arena.get(args[1]) {
ValueData::Integer(r) => *r as u32,
_ => return Err("string->number: radix must be an integer".to_string()),
}
} else {
10
};
if radix < 2 || radix > 36 {
return Err("string->number: radix must be between 2 and 36".to_string());
}
if let Ok(n) = i64::from_str_radix(&s, radix) {
return Ok(arena.int(n));
}
if radix == 10 {
if let Ok(f) = s.parse::<f64>() {
return Ok(arena.real(f));
}
}
Ok(crate::scheme::arena::FALSE_ID)
}
pub fn number_to_string_radix(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.is_empty() || args.len() > 2 {
return Err("number->string: expected 1 or 2 arguments".to_string());
}
let radix = if args.len() == 2 {
match arena.get(args[1]) {
ValueData::Integer(r) => *r as u32,
_ => return Err("number->string: radix must be an integer".to_string()),
}
} else {
10
};
if radix < 2 || radix > 36 {
return Err("number->string: radix must be between 2 and 36".to_string());
}
match arena.get(args[0]) {
ValueData::Integer(n) => {
if radix == 10 {
Ok(arena.string(n.to_string()))
} else {
let s = if *n < 0 {
format!("-{}", format_radix((-n) as u64, radix))
} else {
format_radix(*n as u64, radix)
};
Ok(arena.string(s))
}
}
ValueData::Real(f) => {
if radix != 10 {
return Err("number->string: non-decimal radix not supported for real numbers".to_string());
}
Ok(arena.string(f.to_string()))
}
_ => Err("number->string: first argument must be a number".to_string()),
}
}
fn format_radix(mut n: u64, radix: u32) -> String {
if n == 0 {
return "0".to_string();
}
let digits = "0123456789abcdefghijklmnopqrstuvwxyz";
let mut result = String::new();
while n > 0 {
let digit = (n % radix as u64) as usize;
result.insert(0, digits.chars().nth(digit).unwrap());
n /= radix as u64;
}
result
}
pub fn list_p_improved(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("list?: expected 1 argument".to_string());
}
let mut current = args[0];
let mut visited = std::collections::HashSet::new();
loop {
if !visited.insert(current) {
return Ok(crate::scheme::arena::FALSE_ID);
}
match arena.get(current) {
ValueData::Nil => return Ok(crate::scheme::arena::TRUE_ID),
ValueData::Pair { cdr, .. } => {
current = *cdr;
}
_ => return Ok(crate::scheme::arena::FALSE_ID),
}
}
}
pub fn null_list_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("null-list?: expected 1 argument".to_string());
}
match arena.get(args[0]) {
ValueData::Nil => Ok(crate::scheme::arena::TRUE_ID),
_ => Ok(crate::scheme::arena::FALSE_ID),
}
}
pub fn improper_list_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("improper-list?: expected 1 argument".to_string());
}
let mut current = args[0];
loop {
match arena.get(current) {
ValueData::Nil => return Ok(crate::scheme::arena::FALSE_ID), ValueData::Pair { cdr, .. } => {
current = *cdr;
}
_ => return Ok(crate::scheme::arena::TRUE_ID), }
}
}
pub fn circular_list_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("circular-list?: expected 1 argument".to_string());
}
let mut slow = args[0];
let mut fast = args[0];
loop {
match arena.get(slow) {
ValueData::Pair { cdr, .. } => slow = *cdr,
_ => return Ok(crate::scheme::arena::FALSE_ID), }
match arena.get(fast) {
ValueData::Pair { cdr: cdr1, .. } => {
fast = *cdr1;
match arena.get(fast) {
ValueData::Pair { cdr: cdr2, .. } => fast = *cdr2,
_ => return Ok(crate::scheme::arena::FALSE_ID), }
}
_ => return Ok(crate::scheme::arena::FALSE_ID), }
if slow == fast {
return Ok(crate::scheme::arena::TRUE_ID);
}
}
}
pub fn bitwise_and(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.is_empty() {
return Ok(arena.int(-1)); }
let mut result = -1i64;
for &arg in args {
match arena.get(arg) {
ValueData::Integer(n) => {
result &= n;
}
_ => return Err("bitwise-and: all arguments must be integers".to_string()),
}
}
Ok(arena.int(result))
}
pub fn bitwise_ior(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.is_empty() {
return Ok(arena.int(0)); }
let mut result = 0i64;
for &arg in args {
match arena.get(arg) {
ValueData::Integer(n) => {
result |= n;
}
_ => return Err("bitwise-ior: all arguments must be integers".to_string()),
}
}
Ok(arena.int(result))
}
pub fn bitwise_xor(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.is_empty() {
return Ok(arena.int(0)); }
let mut result = 0i64;
for &arg in args {
match arena.get(arg) {
ValueData::Integer(n) => {
result ^= n;
}
_ => return Err("bitwise-xor: all arguments must be integers".to_string()),
}
}
Ok(arena.int(result))
}
pub fn bitwise_not(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("bitwise-not: expected 1 argument".to_string());
}
match arena.get(args[0]) {
ValueData::Integer(n) => Ok(arena.int(!n)),
_ => Err("bitwise-not: argument must be an integer".to_string()),
}
}
pub fn arithmetic_shift(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err("arithmetic-shift: expected 2 arguments".to_string());
}
let n = match arena.get(args[0]) {
ValueData::Integer(n) => *n,
_ => return Err("arithmetic-shift: first argument must be an integer".to_string()),
};
let shift = match arena.get(args[1]) {
ValueData::Integer(s) => *s,
_ => return Err("arithmetic-shift: second argument must be an integer".to_string()),
};
let result = if shift >= 0 {
if shift >= 64 {
0 } else {
n.wrapping_shl(shift as u32)
}
} else {
if shift <= -64 {
if n < 0 { -1 } else { 0 }
} else {
n >> (-shift)
}
};
Ok(arena.int(result))
}
pub fn bit_extract(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 3 {
return Err("bit-extract: expected 3 arguments".to_string());
}
let n = match arena.get(args[0]) {
ValueData::Integer(n) => *n as u64,
_ => return Err("bit-extract: first argument must be an integer".to_string()),
};
let start = match arena.get(args[1]) {
ValueData::Integer(s) => *s as u32,
_ => return Err("bit-extract: second argument must be an integer".to_string()),
};
let end = match arena.get(args[2]) {
ValueData::Integer(e) => *e as u32,
_ => return Err("bit-extract: third argument must be an integer".to_string()),
};
if start > end {
return Err("bit-extract: start must be <= end".to_string());
}
if end > 64 {
return Err("bit-extract: end must be <= 64".to_string());
}
let count = end - start;
let mask = if count >= 64 {
u64::MAX
} else {
(1u64 << count) - 1
};
let result = (n >> start) & mask;
Ok(arena.int(result as i64))
}
pub fn bitwise_bit_set_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err("bitwise-bit-set?: expected 2 arguments".to_string());
}
let n = match arena.get(args[0]) {
ValueData::Integer(n) => *n,
_ => return Err("bitwise-bit-set?: first argument must be an integer".to_string()),
};
let pos = match arena.get(args[1]) {
ValueData::Integer(p) => *p,
_ => return Err("bitwise-bit-set?: second argument must be an integer".to_string()),
};
if pos < 0 || pos >= 64 {
return Err("bitwise-bit-set?: position must be between 0 and 63".to_string());
}
let is_set = (n & (1i64 << pos)) != 0;
Ok(if is_set { crate::scheme::arena::TRUE_ID } else { crate::scheme::arena::FALSE_ID })
}
pub fn bitwise_bit_count(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("bitwise-bit-count: expected 1 argument".to_string());
}
match arena.get(args[0]) {
ValueData::Integer(n) => {
let count = if *n < 0 {
(*n as u64).count_ones()
} else {
(*n as u64).count_ones()
};
Ok(arena.int(count as i64))
}
_ => Err("bitwise-bit-count: argument must be an integer".to_string()),
}
}
pub fn display(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("display: expected 1 argument".to_string());
}
let _s = value_to_display_string(arena, args[0]);
Ok(crate::scheme::arena::UNSPECIFIED_ID)
}
fn value_to_display_string(arena: &Arena, id: ValueId) -> String {
match arena.get(id) {
ValueData::Nil => "()".to_string(),
ValueData::Bool(true) => "#t".to_string(),
ValueData::Bool(false) => "#f".to_string(),
ValueData::Integer(n) => n.to_string(),
ValueData::Real(f) => f.to_string(),
ValueData::Char(c) => c.to_string(),
ValueData::String(s) => s.clone(),
ValueData::Symbol(s) => s.to_string(),
ValueData::Keyword(k) => format!("{}:", k),
ValueData::Pair { .. } => {
let mut result = String::from("(");
let mut current = id;
let mut first = true;
loop {
match arena.get(current) {
ValueData::Pair { car, cdr, .. } => {
if !first {
result.push(' ');
}
first = false;
result.push_str(&value_to_display_string(arena, *car));
current = *cdr;
if matches!(arena.get(current), ValueData::Nil) {
break;
} else if !matches!(arena.get(current), ValueData::Pair { .. }) {
result.push_str(" . ");
result.push_str(&value_to_display_string(arena, current));
break;
}
}
_ => break,
}
}
result.push(')');
result
}
ValueData::Vector(elements) => {
let mut result = String::from("#(");
for (i, elem) in elements.iter().enumerate() {
if i > 0 {
result.push(' ');
}
result.push_str(&value_to_display_string(arena, *elem));
}
result.push(')');
result
}
ValueData::Procedure(_) => "#<procedure>".to_string(),
ValueData::Unspecified => "#<unspecified>".to_string(),
ValueData::Error => "#<error>".to_string(),
_ => "#<unknown>".to_string(),
}
}
pub fn newline(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
if !args.is_empty() {
return Err("newline: expected 0 arguments".to_string());
}
Ok(crate::scheme::arena::UNSPECIFIED_ID)
}
pub fn write(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("write: expected 1 argument".to_string());
}
let _s = value_to_write_string(arena, args[0]);
Ok(crate::scheme::arena::UNSPECIFIED_ID)
}
fn value_to_write_string(arena: &Arena, id: ValueId) -> String {
match arena.get(id) {
ValueData::String(s) => format!("\"{}\"", s.escape_default()),
ValueData::Char(c) => format!("#\\{}", c),
_ => value_to_display_string(arena, id),
}
}
pub fn write_char(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("write-char: expected 1 argument".to_string());
}
match arena.get(args[0]) {
ValueData::Char(_) => {
Ok(crate::scheme::arena::UNSPECIFIED_ID)
}
_ => Err("write-char: argument must be a character".to_string()),
}
}
pub fn read_char(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
if !args.is_empty() {
return Err("read-char: expected 0 arguments".to_string());
}
Err("read-char: not available in arena mode (requires I/O)".to_string())
}
pub fn eof_object_p(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("eof-object?: expected 1 argument".to_string());
}
Ok(crate::scheme::arena::FALSE_ID)
}
pub fn format_number(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err("format-number: expected 2 arguments".to_string());
}
let n = match arena.get(args[0]) {
ValueData::Integer(i) => *i,
ValueData::Real(r) => *r as i64,
_ => return Err("format-number: first argument must be a number".to_string()),
};
let format = match arena.get(args[1]) {
ValueData::String(s) => s.as_str(),
ValueData::Symbol(s) => s.as_ref(),
_ => return Err("format-number: second argument must be a string or symbol".to_string()),
};
let result = match format {
"1" | "decimal" => n.to_string(),
"I" | "roman-upper" => {
let roman_str = match n {
1 => "I",
2 => "II",
3 => "III",
4 => "IV",
5 => "V",
6 => "VI",
7 => "VII",
8 => "VIII",
9 => "IX",
10 => "X",
11 => "XI",
12 => "XII",
13 => "XIII",
14 => "XIV",
15 => "XV",
16 => "XVI",
17 => "XVII",
18 => "XVIII",
19 => "XIX",
20 => "XX",
_ => return Ok(arena.string(n.to_string())),
};
roman_str.to_string()
}
"i" | "roman-lower" => {
let roman_str = match n {
1 => "i",
2 => "ii",
3 => "iii",
4 => "iv",
5 => "v",
6 => "vi",
7 => "vii",
8 => "viii",
9 => "ix",
10 => "x",
11 => "xi",
12 => "xii",
13 => "xiii",
14 => "xiv",
15 => "xv",
16 => "xvi",
17 => "xvii",
18 => "xviii",
19 => "xix",
20 => "xx",
_ => return Ok(arena.string(n.to_string())),
};
roman_str.to_string()
}
"a" | "alpha-lower" => {
if n >= 1 && n <= 26 {
((b'a' + (n as u8 - 1)) as char).to_string()
} else {
n.to_string()
}
}
"A" | "alpha-upper" => {
if n >= 1 && n <= 26 {
((b'A' + (n as u8 - 1)) as char).to_string()
} else {
n.to_string()
}
}
_ => n.to_string(), };
Ok(arena.string(result))
}
pub fn format_number_list(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.is_empty() || args.len() > 2 {
return Err("format-number-list: expected 1 or 2 arguments".to_string());
}
let separator = if args.len() == 2 {
match arena.get(args[1]) {
ValueData::String(s) => s.as_str(),
ValueData::Symbol(s) => s.as_ref(),
_ => ".",
}
} else {
"."
};
let mut numbers = Vec::new();
let mut current = args[0];
loop {
match arena.get(current) {
ValueData::Nil => break,
ValueData::Pair { car, cdr, .. } => {
match arena.get(*car) {
ValueData::Integer(n) => numbers.push(n.to_string()),
ValueData::Real(r) => numbers.push((*r as i64).to_string()),
_ => {
return Err(
"format-number-list: list must contain only numbers".to_string()
)
}
}
current = *cdr;
}
_ => return Err("format-number-list: not a proper list".to_string()),
}
}
Ok(arena.string(numbers.join(separator)))
}
pub fn empty_sosofo(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if !args.is_empty() {
return Err("empty-sosofo: expected no arguments".to_string());
}
Ok(arena.alloc(ValueData::Sosofo))
}
pub fn sosofo_append(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.is_empty() {
return Ok(arena.alloc(ValueData::Sosofo));
}
for &arg in args {
match arena.get(arg) {
ValueData::Sosofo | ValueData::Unspecified | ValueData::Nil => {},
_ => return Err(format!("sosofo-append: not a sosofo")),
}
}
Ok(arena.alloc(ValueData::Sosofo))
}
#[inline]
pub fn if_first_page(_arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err("if-first-page: expected 2 arguments (then-sosofo else-sosofo)".to_string());
}
Ok(args[1])
}
#[inline]
pub fn if_front_page(_arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err("if-front-page: expected 2 arguments (then-sosofo else-sosofo)".to_string());
}
Ok(args[1])
}
pub fn current_node(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if !args.is_empty() {
return Err("current-node: expected no arguments".to_string());
}
match &arena.current_node {
Some(node) => Ok(arena.alloc(ValueData::Node(node.clone()))),
None => Err("current-node: no current node set".to_string()),
}
}
pub fn gi(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
let node = if args.is_empty() {
match &arena.current_node {
Some(n) => n.clone(),
None => return Err("gi: no current node".to_string()),
}
} else if args.len() == 1 {
match arena.get(args[0]) {
ValueData::Bool(false) => {
return Ok(crate::scheme::arena::FALSE_ID);
}
ValueData::Symbol(_s) => {
return Ok(args[0]);
}
ValueData::Node(n) => n.clone(),
ValueData::NodeList(nl) => {
if nl.is_empty() {
return Ok(crate::scheme::arena::FALSE_ID);
}
let first = nl.first();
let rest = nl.rest();
if rest.is_empty() {
Rc::new(first.unwrap())
} else {
return Err("gi: node-list must be a singleton".to_string());
}
}
other => {
let type_name = match other {
ValueData::Nil => "nil",
ValueData::Bool(_) => "boolean",
ValueData::Integer(_) => "integer",
ValueData::Real(_) => "real",
ValueData::Quantity { .. } => "quantity",
ValueData::Char(_) => "char",
ValueData::String(_) => "string",
ValueData::Keyword(_) => "keyword",
ValueData::Pair { .. } => "pair",
ValueData::Vector(_) => "vector",
ValueData::Procedure(_) => "procedure",
ValueData::Sosofo => "sosofo",
ValueData::Unspecified => "unspecified",
_ => "unknown",
};
return Err(format!("gi: argument must be a node, node-list, #f, or symbol (got {})", type_name));
}
}
} else {
return Err("gi: expected 0 or 1 arguments".to_string());
};
match node.gi() {
Some(name) => Ok(arena.symbol(name.into())),
None => Ok(crate::scheme::arena::FALSE_ID),
}
}
pub fn data(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() > 1 {
return Err("data: expected 0 or 1 arguments".to_string());
}
let node_list = if args.is_empty() {
match &arena.current_node {
Some(n) => {
let singleton = crate::grove::VecNodeList::new(vec![n.clone_node()]);
Box::new(singleton) as Box<dyn crate::grove::NodeList>
}
None => return Err("data: no current node".to_string()),
}
} else {
match arena.get(args[0]) {
ValueData::Node(n) => {
let singleton = crate::grove::VecNodeList::new(vec![n.clone_node()]);
Box::new(singleton) as Box<dyn crate::grove::NodeList>
}
ValueData::NodeList(nl) => {
let vec_nodes: Vec<Box<dyn crate::grove::Node>> = {
let mut result = Vec::new();
let mut current = nl.clone();
loop {
if current.is_empty() {
break;
}
result.push(current.first().unwrap());
current = Rc::new(current.rest());
}
result
};
Box::new(crate::grove::VecNodeList::new(vec_nodes)) as Box<dyn crate::grove::NodeList>
}
_ => return Err("data: argument must be a node or node-list".to_string()),
}
};
let mut result = String::new();
let mut current_list: Box<dyn crate::grove::NodeList> = node_list;
loop {
if current_list.is_empty() {
break;
}
let node = current_list.first().unwrap();
if let Some(text) = node.data() {
result.push_str(&text);
}
current_list = current_list.rest();
}
Ok(arena.string(result))
}
pub fn id(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
let node = if args.is_empty() {
match &arena.current_node {
Some(n) => n.clone(),
None => return Err("id: no current node".to_string()),
}
} else if args.len() == 1 {
match arena.get(args[0]) {
ValueData::Node(n) => n.clone(),
ValueData::NodeList(nl) => {
if nl.is_empty() {
return Ok(crate::scheme::arena::FALSE_ID);
}
let first = nl.first();
let rest = nl.rest();
if rest.is_empty() {
Rc::new(first.unwrap())
} else {
return Err("id: node-list must be a singleton".to_string());
}
}
_ => return Ok(crate::scheme::arena::FALSE_ID),
}
} else {
return Err("id: expected 0 or 1 arguments".to_string());
};
match node.id() {
Some(id_str) => Ok(arena.string(id_str)),
None => Ok(crate::scheme::arena::FALSE_ID),
}
}
pub fn children(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
let node = if args.is_empty() {
match &arena.current_node {
Some(n) => n.clone(),
None => return Err("children: no current node".to_string()),
}
} else if args.len() == 1 {
match arena.get(args[0]) {
ValueData::Node(n) => n.clone(),
ValueData::NodeList(nl) => {
if nl.is_empty() {
return Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(crate::grove::VecNodeList::new(vec![]))))));
}
let first = nl.first();
let rest = nl.rest();
if rest.is_empty() {
Rc::new(first.unwrap())
} else {
let mut all_children = Vec::new();
let mut current = nl.as_ref();
while !current.is_empty() {
if let Some(node) = current.first() {
let node_children = node.children();
let mut child_current = Box::new(node_children);
while !child_current.is_empty() {
if let Some(child) = child_current.first() {
all_children.push(child);
}
child_current = Box::new(child_current.rest());
}
}
current = Box::leak(Box::new(current.rest()));
}
let result = crate::grove::VecNodeList::new(all_children);
return Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(result)))));
}
}
_ => return Err("children: argument must be a node or node-list".to_string()),
}
} else {
return Err("children: expected 0 or 1 arguments".to_string());
};
let children = node.children();
Ok(arena.alloc(ValueData::NodeList(Rc::new(children))))
}
pub fn parent(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
let node = if args.is_empty() {
match &arena.current_node {
Some(n) => n.clone(),
None => return Err("parent: no current node".to_string()),
}
} else if args.len() == 1 {
match arena.get(args[0]) {
ValueData::Node(n) => n.clone(),
ValueData::NodeList(nl) => {
if nl.is_empty() {
return Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(crate::grove::EmptyNodeList)))));
}
let first = nl.first();
let rest = nl.rest();
if rest.is_empty() {
Rc::new(first.unwrap())
} else {
return Err("parent: node-list must be a singleton".to_string());
}
}
_ => return Err("parent: argument must be a node or node-list".to_string()),
}
} else {
return Err("parent: expected 0 or 1 arguments".to_string());
};
match node.parent() {
Some(parent) => {
use crate::grove::VecNodeList;
let nodes = vec![parent.clone_node()];
Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(VecNodeList::new(nodes))))))
}
None => Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(crate::grove::EmptyNodeList))))),
}
}
pub fn attributes(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
let _node = if args.is_empty() {
match &arena.current_node {
Some(n) => n.clone(),
None => return Err("attributes: no current node".to_string()),
}
} else if args.len() == 1 {
match arena.get(args[0]) {
ValueData::Node(n) => n.clone(),
_ => return Err("attributes: argument must be a node".to_string()),
}
} else {
return Err("attributes: expected 0 or 1 arguments".to_string());
};
Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(crate::grove::EmptyNodeList::new())))))
}
pub fn node_list_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("node-list?: expected 1 argument".to_string());
}
match arena.get(args[0]) {
ValueData::NodeList(_) => Ok(crate::scheme::arena::TRUE_ID),
_ => Ok(crate::scheme::arena::FALSE_ID),
}
}
pub fn empty_node_list(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if !args.is_empty() {
return Err("empty-node-list: expected no arguments".to_string());
}
Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(crate::grove::EmptyNodeList::new())))))
}
pub fn node_list_empty_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("node-list-empty?: expected 1 argument".to_string());
}
match arena.get(args[0]) {
ValueData::NodeList(nl) => {
if nl.is_empty() {
Ok(crate::scheme::arena::TRUE_ID)
} else {
Ok(crate::scheme::arena::FALSE_ID)
}
}
ValueData::Node(_) => {
Ok(crate::scheme::arena::FALSE_ID)
}
_ => Err("node-list-empty?: argument must be a node or node-list".to_string()),
}
}
pub fn node_list_length(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("node-list-length: expected 1 argument".to_string());
}
match arena.get(args[0]) {
ValueData::NodeList(nl) => Ok(arena.int(nl.length() as i64)),
_ => Err("node-list-length: argument must be a node-list".to_string()),
}
}
pub fn node_list_first(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("node-list-first: expected 1 argument".to_string());
}
match arena.get(args[0]) {
ValueData::NodeList(nl) => {
match nl.first() {
Some(node) => Ok(arena.alloc(ValueData::Node(Rc::new(node)))),
None => Ok(crate::scheme::arena::FALSE_ID),
}
}
_ => Err("node-list-first: argument must be a node-list".to_string()),
}
}
pub fn attribute_string(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.is_empty() || args.len() > 2 {
return Err("attribute-string: expected 1 or 2 arguments".to_string());
}
let attr_name = match arena.get(args[0]) {
ValueData::String(s) => s.clone(),
ValueData::Symbol(s) => s.to_string(),
_ => return Err("attribute-string: first argument must be a string or symbol".to_string()),
};
let node = if args.len() == 2 {
match arena.get(args[1]) {
ValueData::Node(n) => Some(n.clone()),
ValueData::NodeList(nl) => {
if nl.is_empty() {
return Ok(crate::scheme::arena::FALSE_ID);
}
let first = nl.first();
let rest = nl.rest();
if rest.is_empty() {
Some(Rc::new(first.unwrap()))
} else {
return Err("attribute-string: node-list must be a singleton".to_string());
}
}
_ => return Ok(crate::scheme::arena::FALSE_ID),
}
} else {
arena.current_node.clone()
};
let node = match node {
Some(n) => n,
None => return Err("attribute-string: no current node".to_string()),
};
match node.attribute_string(&attr_name) {
Some(value) => Ok(arena.string(value)),
None => Ok(crate::scheme::arena::FALSE_ID),
}
}
pub fn node_list_rest(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("node-list-rest: expected 1 argument".to_string());
}
match arena.get(args[0]) {
ValueData::NodeList(nl) => {
let rest = nl.rest();
Ok(arena.alloc(ValueData::NodeList(Rc::new(rest))))
}
_ => Err("node-list-rest: argument must be a node-list".to_string()),
}
}
pub fn node_list_ref(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err("node-list-ref: expected 2 arguments".to_string());
}
let nl = match arena.get(args[0]) {
ValueData::NodeList(nl) => nl.clone(),
_ => return Err("node-list-ref: first argument must be a node-list".to_string()),
};
let index = match arena.get(args[1]) {
ValueData::Integer(n) => {
if *n < 0 {
return Err("node-list-ref: index must be non-negative".to_string());
}
*n as usize
}
_ => return Err("node-list-ref: second argument must be an integer".to_string()),
};
let mut current = nl.as_ref();
let mut i = 0;
while i < index {
if current.is_empty() {
return Ok(crate::scheme::arena::FALSE_ID);
}
let rest = current.rest();
current = Box::leak(Box::new(rest)); i += 1;
}
match current.first() {
Some(node) => Ok(arena.alloc(ValueData::Node(Rc::new(node)))),
None => Ok(crate::scheme::arena::FALSE_ID),
}
}
pub fn node_list_reverse(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("node-list-reverse: expected 1 argument".to_string());
}
match arena.get(args[0]) {
ValueData::NodeList(nl) => {
let mut nodes = Vec::new();
let mut current = nl.as_ref();
while !current.is_empty() {
if let Some(node) = current.first() {
nodes.push(node);
}
current = Box::leak(Box::new(current.rest()));
}
nodes.reverse();
let reversed = crate::grove::VecNodeList::new(nodes);
Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(reversed)))))
}
_ => Err("node-list-reverse: argument must be a node-list".to_string()),
}
}
pub fn node_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("node?: expected 1 argument".to_string());
}
match arena.get(args[0]) {
ValueData::Node(_) => Ok(crate::scheme::arena::TRUE_ID),
_ => Ok(crate::scheme::arena::FALSE_ID),
}
}
pub fn sosofo_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("sosofo?: expected 1 argument".to_string());
}
match arena.get(args[0]) {
ValueData::Sosofo => Ok(crate::scheme::arena::TRUE_ID),
_ => Ok(crate::scheme::arena::FALSE_ID),
}
}
pub fn quantity_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("quantity?: expected 1 argument".to_string());
}
match arena.get(args[0]) {
ValueData::Quantity { .. } => Ok(crate::scheme::arena::TRUE_ID),
_ => Ok(crate::scheme::arena::FALSE_ID),
}
}
pub fn color_p(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("color?: expected 1 argument".to_string());
}
Ok(crate::scheme::arena::FALSE_ID)
}
pub fn color(_arena: &mut Arena, _args: &[ValueId]) -> ArenaResult {
Ok(crate::scheme::arena::FALSE_ID)
}
pub fn display_space_p(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("display-space?: expected 1 argument".to_string());
}
Ok(crate::scheme::arena::FALSE_ID)
}
pub fn inline_space_p(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("inline-space?: expected 1 argument".to_string());
}
Ok(crate::scheme::arena::FALSE_ID)
}
pub fn quantity_to_number(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("quantity->number: expected 1 argument".to_string());
}
match arena.get(args[0]) {
ValueData::Quantity { magnitude, .. } => Ok(arena.real(*magnitude)),
_ => Err("quantity->number: argument must be a quantity".to_string()),
}
}
pub fn number_to_quantity(_arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() < 2 || args.len() > 3 {
return Err("number->quantity: expected 2 or 3 arguments".to_string());
}
Ok(args[0])
}
pub fn quantity_convert(_arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err("quantity-convert: expected 2 arguments".to_string());
}
Ok(args[0])
}
pub fn device_length(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("device-length: expected 1 argument".to_string());
}
Ok(arena.int(0))
}
pub fn label_distance(arena: &mut Arena, _args: &[ValueId]) -> ArenaResult {
Ok(arena.int(0))
}
pub fn ancestor(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.is_empty() || args.len() > 2 {
return Err("ancestor: expected 1 or 2 arguments".to_string());
}
let ancestor_name = match arena.get(args[0]) {
ValueData::String(s) => s.clone(),
ValueData::Symbol(s) => s.to_string(),
_ => return Err("ancestor: first argument must be a string or symbol".to_string()),
};
let node = if args.len() == 2 {
match arena.get(args[1]) {
ValueData::Node(n) => n.clone(),
ValueData::NodeList(nl) => {
if nl.is_empty() {
return Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(crate::grove::EmptyNodeList)))));
}
let first = nl.first();
let rest = nl.rest();
if rest.is_empty() {
Rc::new(first.unwrap())
} else {
return Err("ancestor: node-list must be a singleton".to_string());
}
}
_ => return Err("ancestor: second argument must be a node or singleton node-list".to_string()),
}
} else {
match &arena.current_node {
Some(n) => n.clone(),
None => return Err("ancestor: no current node".to_string()),
}
};
let mut current = node.parent();
while let Some(parent_node) = current {
if let Some(gi) = parent_node.gi() {
if gi == ancestor_name {
use crate::grove::VecNodeList;
let nodes = vec![parent_node.clone_node()];
return Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(VecNodeList::new(nodes))))));
}
}
current = parent_node.parent();
}
Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(crate::grove::EmptyNodeList)))))
}
pub fn descendants(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() > 1 {
return Err("descendants: expected 0 or 1 arguments".to_string());
}
fn collect_descendants(node: &dyn crate::grove::Node, result: &mut Vec<Box<dyn crate::grove::Node>>) {
let children = node.children();
let mut current = children.as_ref();
while !current.is_empty() {
if let Some(child) = current.first() {
collect_descendants(child.as_ref(), result);
result.push(child);
}
current = Box::leak(current.rest());
}
}
if args.is_empty() {
let node = match &arena.current_node {
Some(n) => n.clone(),
None => return Err("descendants: no current node".to_string()),
};
let mut descendants = Vec::new();
collect_descendants(node.as_ref().as_ref(), &mut descendants);
let node_list = crate::grove::VecNodeList::new(descendants);
return Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(node_list)))));
}
match arena.get(args[0]) {
ValueData::NodeList(nl) => {
let mut all_descendants = Vec::new();
let mut current = nl.as_ref().as_ref();
while !current.is_empty() {
if let Some(node) = current.first() {
collect_descendants(node.as_ref(), &mut all_descendants);
}
current = Box::leak(current.rest());
}
let result = crate::grove::VecNodeList::new(all_descendants);
Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(result)))))
}
ValueData::Node(n) => {
let mut descendants = Vec::new();
collect_descendants(n.as_ref().as_ref(), &mut descendants);
let node_list = crate::grove::VecNodeList::new(descendants);
Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(node_list)))))
}
_ => Err("descendants: argument must be a node or node-list".to_string()),
}
}
pub fn follow(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() > 1 {
return Err("follow: expected 0 or 1 arguments".to_string());
}
let _node = if args.is_empty() {
match &arena.current_node {
Some(n) => n.clone(),
None => return Err("follow: no current node".to_string()),
}
} else {
match arena.get(args[0]) {
ValueData::Node(n) => n.clone(),
_ => return Err("follow: argument must be a node".to_string()),
}
};
Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(crate::grove::EmptyNodeList)))))
}
pub fn preced(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() > 1 {
return Err("preced: expected 0 or 1 arguments".to_string());
}
let _node = if args.is_empty() {
match &arena.current_node {
Some(n) => n.clone(),
None => return Err("preced: no current node".to_string()),
}
} else {
match arena.get(args[0]) {
ValueData::Node(n) => n.clone(),
_ => return Err("preced: argument must be a node".to_string()),
}
};
Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(crate::grove::EmptyNodeList)))))
}
pub fn ipreced(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() > 1 {
return Err("ipreced: expected 0 or 1 arguments".to_string());
}
let _node = if args.is_empty() {
match &arena.current_node {
Some(n) => n.clone(),
None => return Err("ipreced: no current node".to_string()),
}
} else {
match arena.get(args[0]) {
ValueData::Node(n) => n.clone(),
_ => return Err("ipreced: argument must be a node".to_string()),
}
};
Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(crate::grove::EmptyNodeList)))))
}
pub fn node_list_last(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("node-list-last: expected 1 argument".to_string());
}
match arena.get(args[0]) {
ValueData::NodeList(nl) => {
let mut last_node = None;
let mut current = nl.as_ref().as_ref();
while !current.is_empty() {
if let Some(node) = current.first() {
last_node = Some(node);
}
current = Box::leak(current.rest());
}
match last_node {
Some(node) => Ok(arena.alloc(ValueData::Node(Rc::new(node)))),
None => Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(crate::grove::EmptyNodeList))))),
}
}
_ => Err("node-list-last: argument must be a node-list".to_string()),
}
}
pub fn node_list_union(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.is_empty() {
return Err("node-list-union: expected at least 1 argument".to_string());
}
let mut nodes: Vec<Box<dyn crate::grove::Node>> = Vec::new();
for (i, arg_id) in args.iter().enumerate() {
let nl = match arena.get(*arg_id) {
ValueData::NodeList(nl) => nl.clone(),
_ => return Err(format!("node-list-union: argument {} must be a node-list", i + 1)),
};
let len = nl.length();
for idx in 0..len {
if let Some(node) = nl.get(idx) {
let mut already_present = false;
for existing_node in &nodes {
if node.node_eq(existing_node.as_ref()) {
already_present = true;
break;
}
}
if !already_present {
nodes.push(node);
}
}
}
}
let union = crate::grove::VecNodeList::new(nodes);
Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(union)))))
}
pub fn node_list_intersection(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err("node-list-intersection: expected 2 arguments".to_string());
}
let _nl1 = match arena.get(args[0]) {
ValueData::NodeList(nl) => nl.clone(),
_ => return Err("node-list-intersection: first argument must be a node-list".to_string()),
};
let _nl2 = match arena.get(args[1]) {
ValueData::NodeList(nl) => nl.clone(),
_ => return Err("node-list-intersection: second argument must be a node-list".to_string()),
};
Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(crate::grove::EmptyNodeList)))))
}
pub fn node_list_difference(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err("node-list-difference: expected 2 arguments".to_string());
}
let nl1 = match arena.get(args[0]) {
ValueData::NodeList(nl) => nl.clone(),
_ => return Err("node-list-difference: first argument must be a node-list".to_string()),
};
let _nl2 = match arena.get(args[1]) {
ValueData::NodeList(nl) => nl.clone(),
_ => return Err("node-list-difference: second argument must be a node-list".to_string()),
};
Ok(arena.alloc(ValueData::NodeList(nl1)))
}
pub fn node_list_remove_duplicates(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("node-list-remove-duplicates: expected 1 argument".to_string());
}
let nl = match arena.get(args[0]) {
ValueData::NodeList(nl) => nl.clone(),
_ => return Err("node-list-remove-duplicates: argument must be a node-list".to_string()),
};
Ok(arena.alloc(ValueData::NodeList(nl)))
}
pub fn select_elements(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err("select-elements: expected 2 arguments".to_string());
}
let nl = match arena.get(args[0]) {
ValueData::NodeList(nl) => nl.clone(),
_ => return Err("select-elements: first argument must be a node-list".to_string()),
};
let element_name = match arena.get(args[1]) {
ValueData::String(s) => s.clone(),
ValueData::Symbol(s) => s.to_string(),
_ => return Err("select-elements: second argument must be a string or symbol".to_string()),
};
let mut selected = Vec::new();
let mut current = nl.as_ref().as_ref();
while !current.is_empty() {
if let Some(node) = current.first() {
if let Some(gi) = node.gi() {
if gi == element_name {
selected.push(node);
}
}
}
current = Box::leak(current.rest());
}
let result = crate::grove::VecNodeList::new(selected);
Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(result)))))
}
pub fn first_sibling_p(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() > 1 {
return Err("first-sibling?: expected 0 or 1 arguments".to_string());
}
let node = if args.is_empty() {
match &arena.current_node {
Some(n) => n.clone(),
None => return Err("first-sibling?: no current node".to_string()),
}
} else {
match arena.get(args[0]) {
ValueData::Node(n) => n.clone(),
_ => return Err("first-sibling?: argument must be a node".to_string()),
}
};
let gi = node.gi();
match node.parent() {
Some(parent) => {
let children = parent.children();
let len = children.length();
for idx in 0..len {
if let Some(sibling) = children.get(idx) {
if sibling.gi() == gi {
if node.node_eq(sibling.as_ref()) {
return Ok(crate::scheme::arena::TRUE_ID);
} else {
return Ok(crate::scheme::arena::FALSE_ID);
}
}
}
}
Ok(crate::scheme::arena::FALSE_ID)
}
None => Ok(crate::scheme::arena::TRUE_ID), }
}
pub fn last_sibling_p(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() > 1 {
return Err("last-sibling?: expected 0 or 1 arguments".to_string());
}
let node = if args.is_empty() {
match &arena.current_node {
Some(n) => n.clone(),
None => return Err("last-sibling?: no current node".to_string()),
}
} else {
match arena.get(args[0]) {
ValueData::Node(n) => n.clone(),
_ => return Err("last-sibling?: argument must be a node".to_string()),
}
};
let gi = node.gi();
match node.parent() {
Some(parent) => {
let children = parent.children();
let len = children.length();
for idx in (0..len).rev() {
if let Some(sibling) = children.get(idx) {
if sibling.gi() == gi {
if node.node_eq(sibling.as_ref()) {
return Ok(crate::scheme::arena::TRUE_ID);
} else {
return Ok(crate::scheme::arena::FALSE_ID);
}
}
}
}
Ok(crate::scheme::arena::FALSE_ID)
}
None => Ok(crate::scheme::arena::TRUE_ID), }
}
pub fn child_number(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() > 1 {
return Err("child-number: expected 0 or 1 arguments".to_string());
}
let node = if args.is_empty() {
match &arena.current_node {
Some(n) => n.clone(),
None => return Err("child-number: no current node".to_string()),
}
} else {
match arena.get(args[0]) {
ValueData::Node(n) => n.clone(),
_ => return Err("child-number: argument must be a node".to_string()),
}
};
let gi = node.gi();
match node.parent() {
Some(parent) => {
let children = parent.children();
let len = children.length();
let mut position = 0;
for idx in 0..len {
if let Some(sibling) = children.get(idx) {
if sibling.gi() == gi {
position += 1;
if node.node_eq(sibling.as_ref()) {
return Ok(arena.int(position as i64));
}
}
}
}
Ok(arena.int(1))
}
None => Ok(arena.int(1)), }
}
pub fn element_with_id(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("element-with-id: expected 1 argument".to_string());
}
let id = match arena.get(args[0]) {
ValueData::String(s) => s.clone(),
ValueData::Symbol(s) => s.to_string(),
_ => return Err("element-with-id: argument must be a string or symbol".to_string()),
};
match &arena.grove {
Some(grove) => {
match grove.element_with_id(&id) {
Some(node) => {
let singleton = crate::grove::VecNodeList::new(vec![node]);
Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(singleton)))))
}
None => Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(crate::grove::EmptyNodeList))))),
}
}
None => Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(crate::grove::EmptyNodeList))))),
}
}
pub fn element_number(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() > 1 {
return Err("element-number: expected 0 or 1 arguments".to_string());
}
let _node = if args.is_empty() {
match &arena.current_node {
Some(n) => n.clone(),
None => return Err("element-number: no current node".to_string()),
}
} else {
match arena.get(args[0]) {
ValueData::Node(n) => n.clone(),
_ => return Err("element-number: argument must be a node".to_string()),
}
};
Ok(arena.int(1))
}
pub fn hierarchical_number(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() > 1 {
return Err("hierarchical-number: expected 0 or 1 arguments".to_string());
}
let _node = if args.is_empty() {
match &arena.current_node {
Some(n) => n.clone(),
None => return Err("hierarchical-number: no current node".to_string()),
}
} else {
match arena.get(args[0]) {
ValueData::Node(n) => n.clone(),
_ => return Err("hierarchical-number: argument must be a node".to_string()),
}
};
let one = arena.int(1);
Ok(arena.cons(one, crate::scheme::arena::NIL_ID))
}
pub fn hierarchical_number_recursive(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() < 1 || args.len() > 2 {
return Err("hierarchical-number-recursive: expected 1 or 2 arguments".to_string());
}
let _element_names = args[0];
let _node = if args.len() == 2 {
match arena.get(args[1]) {
ValueData::Node(n) => n.clone(),
_ => return Err("hierarchical-number-recursive: second argument must be a node".to_string()),
}
} else {
match &arena.current_node {
Some(n) => n.clone(),
None => return Err("hierarchical-number-recursive: no current node".to_string()),
}
};
let one = arena.int(1);
Ok(arena.cons(one, crate::scheme::arena::NIL_ID))
}
fn int_to_roman(mut n: i32, uppercase: bool) -> String {
if n <= 0 || n > 3999 {
return n.to_string();
}
let values = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1];
let symbols_upper = ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"];
let symbols_lower = ["m", "cm", "d", "cd", "c", "xc", "l", "xl", "x", "ix", "v", "iv", "i"];
let symbols = if uppercase { &symbols_upper } else { &symbols_lower };
let mut result = String::new();
for (i, &value) in values.iter().enumerate() {
while n >= value {
result.push_str(symbols[i]);
n -= value;
}
}
result
}
fn int_to_alpha(n: i32, uppercase: bool) -> String {
if n <= 0 {
return n.to_string();
}
let base = if uppercase { b'A' } else { b'a' };
let mut result = String::new();
let mut num = n - 1;
loop {
result.insert(0, (base + (num % 26) as u8) as char);
num /= 26;
if num == 0 {
break;
}
num -= 1;
}
result
}
pub fn ancestors(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() > 1 {
return Err("ancestors: expected 0 or 1 arguments".to_string());
}
let node = if args.is_empty() {
match &arena.current_node {
Some(n) => n.clone(),
None => return Err("ancestors: no current node".to_string()),
}
} else {
match arena.get(args[0]) {
ValueData::Node(n) => n.clone(),
ValueData::NodeList(nl) => {
if nl.is_empty() {
return Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(
crate::grove::VecNodeList::new(Vec::new())
)))));
}
let first = nl.first();
let rest = nl.rest();
if rest.is_empty() {
Rc::new(first.unwrap())
} else {
return Err("ancestors: node-list must be a singleton".to_string());
}
}
_ => return Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(
crate::grove::VecNodeList::new(Vec::new())
))))),
}
};
let mut ancestors = Vec::new();
let mut current = node.parent();
while let Some(parent_node) = current {
ancestors.push(parent_node.clone_node());
current = parent_node.parent();
}
let node_list = crate::grove::VecNodeList::new(ancestors);
Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(node_list)))))
}
pub fn document_element(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if !args.is_empty() {
return Err("document-element: expected no arguments".to_string());
}
match &arena.grove {
Some(grove) => {
let root = grove.root();
Ok(arena.alloc(ValueData::Node(Rc::new(root))))
}
None => Err("document-element: no grove available".to_string()),
}
}
pub fn have_ancestor_p(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.is_empty() || args.len() > 2 {
return Err("have-ancestor?: expected 1 or 2 arguments".to_string());
}
let ancestor_name = match arena.get(args[0]) {
ValueData::String(s) => s.clone(),
ValueData::Symbol(s) => s.to_string(),
_ => return Err("have-ancestor?: first argument must be a string or symbol".to_string()),
};
let node = if args.len() == 2 {
match arena.get(args[1]) {
ValueData::Node(n) => n.clone(),
_ => return Err("have-ancestor?: second argument must be a node".to_string()),
}
} else {
match &arena.current_node {
Some(n) => n.clone(),
None => return Err("have-ancestor?: no current node".to_string()),
}
};
let mut current = node.parent();
while let Some(parent_node) = current {
if let Some(gi) = parent_node.gi() {
if gi == ancestor_name {
return Ok(crate::scheme::arena::TRUE_ID);
}
}
current = parent_node.parent();
}
Ok(crate::scheme::arena::FALSE_ID)
}
pub fn match_element_p(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.is_empty() || args.len() > 2 {
return Err("match-element?: expected 1 or 2 arguments".to_string());
}
let element_name = match arena.get(args[0]) {
ValueData::String(s) => s.clone(),
ValueData::Symbol(s) => s.to_string(),
_ => return Err("match-element?: first argument must be a string or symbol".to_string()),
};
let node = if args.len() == 2 {
match arena.get(args[1]) {
ValueData::Node(n) => n.clone(),
_ => return Err("match-element?: second argument must be a node".to_string()),
}
} else {
match &arena.current_node {
Some(n) => n.clone(),
None => return Err("match-element?: no current node".to_string()),
}
};
match node.gi() {
Some(gi) if gi == element_name => Ok(crate::scheme::arena::TRUE_ID),
_ => Ok(crate::scheme::arena::FALSE_ID),
}
}
pub fn node_list_map(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err("node-list-map: expected 2 arguments".to_string());
}
let _func = args[0];
let nl = match arena.get(args[1]) {
ValueData::NodeList(nl) => nl.clone(),
_ => return Err("node-list-map: second argument must be a node-list".to_string()),
};
Ok(arena.alloc(ValueData::NodeList(nl)))
}
pub fn node_property(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() < 1 || args.len() > 2 {
return Err("node-property: expected 1 or 2 arguments".to_string());
}
let _property_name = match arena.get(args[0]) {
ValueData::String(s) => s.clone(),
ValueData::Symbol(s) => s.to_string(),
_ => return Err("node-property: first argument must be a string or symbol".to_string()),
};
let _node = if args.len() == 2 {
match arena.get(args[1]) {
ValueData::Bool(false) => {
return Ok(crate::scheme::arena::FALSE_ID);
}
ValueData::Node(n) => n.clone(),
ValueData::NodeList(nl) => {
if nl.is_empty() {
return Ok(crate::scheme::arena::FALSE_ID);
}
let first = nl.first();
let rest = nl.rest();
if rest.is_empty() {
Rc::new(first.unwrap())
} else {
return Err("node-property: node-list must be a singleton".to_string());
}
}
_ => {
return Err("node-property: second argument must be a node, singleton node-list, or #f".to_string());
}
}
} else {
match &arena.current_node {
Some(n) => n.clone(),
None => return Err("node-property: no current node".to_string()),
}
};
Ok(crate::scheme::arena::FALSE_ID)
}
pub fn absolute_first_sibling_p(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() > 1 {
return Err("absolute-first-sibling?: expected 0 or 1 arguments".to_string());
}
let _node = if args.is_empty() {
match &arena.current_node {
Some(n) => n.clone(),
None => return Err("absolute-first-sibling?: no current node".to_string()),
}
} else {
match arena.get(args[0]) {
ValueData::Node(n) => n.clone(),
_ => return Err("absolute-first-sibling?: argument must be a node".to_string()),
}
};
Ok(crate::scheme::arena::FALSE_ID)
}
pub fn absolute_last_sibling_p(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() > 1 {
return Err("absolute-last-sibling?: expected 0 or 1 arguments".to_string());
}
let _node = if args.is_empty() {
match &arena.current_node {
Some(n) => n.clone(),
None => return Err("absolute-last-sibling?: no current node".to_string()),
}
} else {
match arena.get(args[0]) {
ValueData::Node(n) => n.clone(),
_ => return Err("absolute-last-sibling?: argument must be a node".to_string()),
}
};
Ok(crate::scheme::arena::FALSE_ID)
}
pub fn node_list_to_list(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err("node-list->list: expected 1 argument".to_string());
}
let nl = match arena.get(args[0]) {
ValueData::NodeList(nl) => nl.clone(),
_ => return Err("node-list->list: argument must be a node-list".to_string()),
};
let mut nodes = Vec::new();
let mut current = nl.as_ref().as_ref();
while !current.is_empty() {
if let Some(node) = current.first() {
let node_id = arena.alloc(ValueData::Node(Rc::new(node)));
nodes.push(node_id);
}
current = Box::leak(current.rest());
}
let mut result = crate::scheme::arena::NIL_ID;
for node_id in nodes.iter().rev() {
result = arena.cons(*node_id, result);
}
Ok(result)
}
pub fn node_list_contains_p(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err("node-list-contains?: expected 2 arguments".to_string());
}
let nl = match arena.get(args[0]) {
ValueData::NodeList(nl) => nl.clone(),
_ => return Err("node-list-contains?: first argument must be a node-list".to_string()),
};
let target_node = match arena.get(args[1]) {
ValueData::Node(n) => n.clone(),
ValueData::NodeList(nl) => {
if nl.is_empty() {
return Ok(crate::scheme::arena::FALSE_ID);
}
let first = nl.first();
let rest = nl.rest();
if rest.is_empty() {
Rc::new(first.unwrap())
} else {
return Err("node-list-contains?: second argument (node-list) must be a singleton".to_string());
}
}
_ => return Err("node-list-contains?: second argument must be a node or node-list".to_string()),
};
let mut nodes = Vec::new();
let mut current_nl = nl.clone();
loop {
if current_nl.is_empty() {
break;
}
nodes.push(current_nl.first().unwrap());
current_nl = Rc::new(current_nl.rest());
}
for node in nodes {
if node.as_ref().node_eq(&**target_node) {
return Ok(crate::scheme::arena::TRUE_ID);
}
}
Ok(crate::scheme::arena::FALSE_ID)
}
pub fn entity_system_id(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.is_empty() {
return Err("entity-system-id: requires at least 1 argument".to_string());
}
let _entity_name = match arena.get(args[0]) {
ValueData::String(s) => s.clone(),
ValueData::Symbol(s) => s.to_string(),
_ => return Err("entity-system-id: argument must be a string or symbol".to_string()),
};
Ok(crate::scheme::arena::FALSE_ID)
}
pub fn entity_public_id(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.is_empty() {
return Err("entity-public-id: requires at least 1 argument".to_string());
}
let _entity_name = match arena.get(args[0]) {
ValueData::String(s) => s.clone(),
ValueData::Symbol(s) => s.to_string(),
_ => return Err("entity-public-id: argument must be a string or symbol".to_string()),
};
Ok(crate::scheme::arena::FALSE_ID)
}
pub fn entity_type(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.is_empty() {
return Err("entity-type: requires at least 1 argument".to_string());
}
let _entity_name = match arena.get(args[0]) {
ValueData::String(s) => s.clone(),
ValueData::Symbol(s) => s.to_string(),
_ => return Err("entity-type: argument must be a string or symbol".to_string()),
};
Ok(crate::scheme::arena::FALSE_ID)
}
pub fn notation_system_id(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.is_empty() {
return Err("notation-system-id: requires at least 1 argument".to_string());
}
let _notation_name = match arena.get(args[0]) {
ValueData::String(s) => s.clone(),
ValueData::Symbol(s) => s.to_string(),
_ => return Err("notation-system-id: argument must be a string or symbol".to_string()),
};
Ok(crate::scheme::arena::FALSE_ID)
}
pub fn notation_public_id(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.is_empty() {
return Err("notation-public-id: requires at least 1 argument".to_string());
}
let _notation_name = match arena.get(args[0]) {
ValueData::String(s) => s.clone(),
ValueData::Symbol(s) => s.to_string(),
_ => return Err("notation-public-id: argument must be a string or symbol".to_string()),
};
Ok(crate::scheme::arena::FALSE_ID)
}
pub fn current_language(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
if !args.is_empty() {
return Err("current-language: requires 0 arguments".to_string());
}
Ok(crate::scheme::arena::FALSE_ID)
}
pub fn current_mode(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
if !args.is_empty() {
return Err("current-mode: requires 0 arguments".to_string());
}
Ok(crate::scheme::arena::FALSE_ID)
}
pub fn current_node_address(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
if !args.is_empty() {
return Err("current-node-address: requires 0 arguments".to_string());
}
Ok(crate::scheme::arena::FALSE_ID)
}
pub fn current_node_page_number_sosofo(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if !args.is_empty() {
return Err("current-node-page-number-sosofo: requires 0 arguments".to_string());
}
Ok(arena.alloc(ValueData::Sosofo))
}
pub fn debug(arena: &Arena, args: &[ValueId]) -> ArenaResult {
eprint!("[DEBUG]");
for arg_id in args {
match arena.get(*arg_id) {
ValueData::Integer(n) => eprint!(" {}", n),
ValueData::Real(f) => eprint!(" {}", f),
ValueData::String(s) => eprint!(" \"{}\"", s),
ValueData::Symbol(s) => eprint!(" {}", s),
ValueData::Bool(b) => eprint!(" {}", if *b { "#t" } else { "#f" }),
ValueData::Char(c) => eprint!(" #\\{}", c),
ValueData::Nil => eprint!(" ()"),
ValueData::Pair { .. } => eprint!(" <pair>"),
ValueData::Vector(_) => eprint!(" <vector>"),
ValueData::Node(_) => eprint!(" <node>"),
ValueData::NodeList(_) => eprint!(" <node-list>"),
ValueData::Sosofo => eprint!(" <sosofo>"),
ValueData::Unspecified => eprint!(" <unspecified>"),
ValueData::Error => eprint!(" <error>"),
ValueData::Procedure(_) => eprint!(" <procedure>"),
ValueData::Quantity { magnitude, .. } => eprint!(" {}pt", magnitude),
ValueData::Keyword(k) => eprint!(" #:{}", k),
}
}
eprintln!();
Ok(crate::scheme::arena::UNSPECIFIED_ID)
}
pub fn exact_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err(format!("exact?: expected 1 argument, got {}", args.len()));
}
match arena.get(args[0]) {
ValueData::Integer(_) => Ok(crate::scheme::arena::TRUE_ID),
ValueData::Real(_) => Ok(crate::scheme::arena::FALSE_ID),
ValueData::Quantity { .. } => Ok(crate::scheme::arena::FALSE_ID),
_ => Err("exact?: argument must be a number".to_string()),
}
}
pub fn inexact_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err(format!("inexact?: expected 1 argument, got {}", args.len()));
}
match arena.get(args[0]) {
ValueData::Integer(_) => Ok(crate::scheme::arena::FALSE_ID),
ValueData::Real(_) => Ok(crate::scheme::arena::TRUE_ID),
ValueData::Quantity { .. } => Ok(crate::scheme::arena::TRUE_ID),
_ => Err("inexact?: argument must be a number".to_string()),
}
}
pub fn error(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
let msg = if args.is_empty() {
"error".to_string()
} else {
let mut parts = Vec::new();
for arg in args {
match arena.get(*arg) {
ValueData::String(s) => parts.push(s.clone()),
ValueData::Symbol(s) => parts.push(s.to_string()),
ValueData::Integer(n) => parts.push(n.to_string()),
ValueData::Real(f) => parts.push(f.to_string()),
ValueData::Bool(b) => parts.push(if *b { "#t" } else { "#f" }.to_string()),
ValueData::Char(c) => parts.push(format!("#\\{}", c)),
_ => parts.push("<value>".to_string()),
}
}
parts.join(" ")
};
Err(msg)
}
pub fn address_p(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err(format!("address?: expected 1 argument, got {}", args.len()));
}
Ok(crate::scheme::arena::FALSE_ID)
}
pub fn address_local_p(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err(format!("address-local?: expected 1 argument, got {}", args.len()));
}
Ok(crate::scheme::arena::FALSE_ID)
}
pub fn address_visited_p(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err(format!("address-visited?: expected 1 argument, got {}", args.len()));
}
Ok(crate::scheme::arena::FALSE_ID)
}
pub fn color_space_p(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err(format!("color-space?: expected 1 argument, got {}", args.len()));
}
Ok(crate::scheme::arena::FALSE_ID)
}
pub fn color_space(_arena: &Arena, _args: &[ValueId]) -> ArenaResult {
Ok(crate::scheme::arena::UNSPECIFIED_ID)
}
pub fn display_space(_arena: &mut Arena, _args: &[ValueId]) -> ArenaResult {
Ok(crate::scheme::arena::UNSPECIFIED_ID)
}
pub fn inline_space(_arena: &mut Arena, _args: &[ValueId]) -> ArenaResult {
Ok(crate::scheme::arena::UNSPECIFIED_ID)
}
pub fn glyph_id_p(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err(format!("glyph-id?: expected 1 argument, got {}", args.len()));
}
Ok(crate::scheme::arena::FALSE_ID)
}
pub fn glyph_id(_arena: &Arena, _args: &[ValueId]) -> ArenaResult {
Ok(crate::scheme::arena::UNSPECIFIED_ID)
}
pub fn glyph_subst_table_p(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err(format!("glyph-subst-table?: expected 1 argument, got {}", args.len()));
}
Ok(crate::scheme::arena::FALSE_ID)
}
pub fn glyph_subst_table(_arena: &Arena, _args: &[ValueId]) -> ArenaResult {
Ok(crate::scheme::arena::UNSPECIFIED_ID)
}
pub fn glyph_subst(_arena: &Arena, _args: &[ValueId]) -> ArenaResult {
Ok(crate::scheme::arena::UNSPECIFIED_ID)
}
pub fn time(_arena: &Arena, _args: &[ValueId]) -> ArenaResult {
Ok(crate::scheme::arena::UNSPECIFIED_ID)
}
pub fn time_to_string(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err(format!("time->string: expected 1 argument, got {}", args.len()));
}
Ok(arena.string(String::new()))
}
pub fn time_le(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err(format!("time<=?: expected 2 arguments, got {}", args.len()));
}
Ok(crate::scheme::arena::FALSE_ID)
}
pub fn time_lt(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err(format!("time<?: expected 2 arguments, got {}", args.len()));
}
Ok(crate::scheme::arena::FALSE_ID)
}
pub fn time_ge(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err(format!("time>=?: expected 2 arguments, got {}", args.len()));
}
Ok(crate::scheme::arena::FALSE_ID)
}
pub fn time_gt(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err(format!("time>?: expected 2 arguments, got {}", args.len()));
}
Ok(crate::scheme::arena::FALSE_ID)
}
pub fn language_p(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err(format!("language?: expected 1 argument, got {}", args.len()));
}
Ok(crate::scheme::arena::FALSE_ID)
}
pub fn language(arena: &mut Arena, _args: &[ValueId]) -> ArenaResult {
Ok(arena.symbol("en".into()))
}
pub fn style_p(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err(format!("style?: expected 1 argument, got {}", args.len()));
}
Ok(crate::scheme::arena::FALSE_ID)
}
pub fn string_equiv_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err(format!("string-equiv?: expected 2 arguments, got {}", args.len()));
}
let s1 = match arena.get(args[0]) {
ValueData::String(s) => s.to_lowercase(),
_ => return Err("string-equiv?: first argument must be a string".to_string()),
};
let s2 = match arena.get(args[1]) {
ValueData::String(s) => s.to_lowercase(),
_ => return Err("string-equiv?: second argument must be a string".to_string()),
};
Ok(if s1 == s2 {
crate::scheme::arena::TRUE_ID
} else {
crate::scheme::arena::FALSE_ID
})
}
pub fn label_length(arena: &mut Arena, _args: &[ValueId]) -> ArenaResult {
Ok(arena.int(0))
}
pub fn external_procedure(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
use crate::scheme::arena::ValueData;
use std::rc::Rc;
if args.len() != 1 {
return Err("external-procedure: requires 1 argument".to_string());
}
let name_str = match arena.get(args[0]) {
ValueData::String(s) => s.clone(),
_ => return Err("external-procedure: argument must be a string".to_string()),
};
let proc_name = if let Some(pos) = name_str.rfind("::") {
&name_str[pos + 2..]
} else {
&name_str
};
let valid = matches!(proc_name,
"if-first-page" | "if-front-page" | "empty-sosofo" | "literal" |
"sosofo-append" | "page-number-sosofo" | "current-node-page-number-sosofo" |
"process-children" | "process-node-list" | "next-match" |
"current-node" | "node-list-first" | "node-list-rest" | "node-list-length" |
"empty-node-list" | "children" | "parent" | "ancestor" | "descendants" |
"follow" | "preced" | "attributes" | "gi" | "id" | "data" |
"attribute-string" | "inherited-attribute-string"
);
if !valid {
return Err(format!("external-procedure: unknown procedure '{}'", proc_name));
}
let symbol_data = ValueData::Symbol(Rc::from(proc_name));
Ok(arena.alloc(symbol_data))
}
pub fn declaration(_arena: &Arena, _args: &[ValueId]) -> ArenaResult {
Ok(crate::scheme::arena::UNSPECIFIED_ID)
}
pub fn dtd(_arena: &Arena, _args: &[ValueId]) -> ArenaResult {
Ok(crate::scheme::arena::UNSPECIFIED_ID)
}
pub fn epilog(_arena: &Arena, _args: &[ValueId]) -> ArenaResult {
Ok(crate::scheme::arena::UNSPECIFIED_ID)
}
pub fn prolog(_arena: &Arena, _args: &[ValueId]) -> ArenaResult {
Ok(crate::scheme::arena::UNSPECIFIED_ID)
}
pub fn sgml_declaration(_arena: &Arena, _args: &[ValueId]) -> ArenaResult {
Ok(crate::scheme::arena::UNSPECIFIED_ID)
}
pub fn sgml_parse(_arena: &Arena, _args: &[ValueId]) -> ArenaResult {
Ok(crate::scheme::arena::UNSPECIFIED_ID)
}
pub fn entity_address(_arena: &Arena, _args: &[ValueId]) -> ArenaResult {
Ok(crate::scheme::arena::UNSPECIFIED_ID)
}
pub fn entity_generated_system_id(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err(format!("entity-generated-system-id: expected 1 argument, got {}", args.len()));
}
Ok(arena.string(String::new()))
}
pub fn entity_name_normalize(_arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err(format!("entity-name-normalize: expected 1 argument, got {}", args.len()));
}
Ok(args[0])
}
pub fn general_name_normalize(_arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.is_empty() || args.len() > 2 {
return Err(format!("general-name-normalize: expected 1 or 2 arguments, got {}", args.len()));
}
Ok(args[0])
}
pub fn normalize(_arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
general_name_normalize(_arena, args)
}
pub fn first_child_gi(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err(format!("first-child-gi: expected 1 argument, got {}", args.len()));
}
Ok(arena.string(String::new()))
}
pub fn tree_root(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if let Some(ref grove) = arena.grove {
let root = grove.root();
Ok(arena.alloc(ValueData::Node(Rc::new(root))))
} else {
let start_node = if args.is_empty() {
match &arena.current_node {
Some(n) => n.clone(),
None => return Ok(crate::scheme::arena::UNSPECIFIED_ID),
}
} else {
match arena.get(args[0]) {
ValueData::Node(n) => n.clone(),
ValueData::NodeList(nl) => {
if nl.is_empty() {
return Ok(crate::scheme::arena::UNSPECIFIED_ID);
}
let first = nl.first();
let rest = nl.rest();
if rest.is_empty() {
Rc::new(first.unwrap())
} else {
return Err("tree-root: node-list must be a singleton".to_string());
}
}
_ => return Ok(crate::scheme::arena::UNSPECIFIED_ID),
}
};
let mut current = start_node;
loop {
match current.parent() {
Some(parent) => {
current = Rc::new(parent);
}
None => {
break;
}
}
}
Ok(arena.alloc(ValueData::Node(current)))
}
}
pub fn declare_default_language(_arena: &Arena, _args: &[ValueId]) -> ArenaResult {
Ok(crate::scheme::arena::UNSPECIFIED_ID)
}
pub fn read_entity(arena: &mut Arena, _args: &[ValueId]) -> ArenaResult {
Ok(arena.string(String::new()))
}
pub fn set_visited(_arena: &Arena, _args: &[ValueId]) -> ArenaResult {
Ok(crate::scheme::arena::UNSPECIFIED_ID)
}
pub fn sosofo_contains_node_p(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err(format!("sosofo-contains-node?: expected 2 arguments, got {}", args.len()));
}
Ok(crate::scheme::arena::FALSE_ID)
}
pub fn page_number_sosofo(arena: &mut Arena, _args: &[ValueId]) -> ArenaResult {
Ok(arena.alloc(ValueData::Sosofo))
}
pub fn ifollow(_arena: &Arena, _args: &[ValueId]) -> ArenaResult {
Ok(crate::scheme::arena::UNSPECIFIED_ID)
}
pub fn with_language(_arena: &Arena, _args: &[ValueId]) -> ArenaResult {
Ok(crate::scheme::arena::UNSPECIFIED_ID)
}
pub fn all_element_number(arena: &mut Arena, _args: &[ValueId]) -> ArenaResult {
Ok(arena.int(0))
}
pub fn ancestor_child_number(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err(format!("ancestor-child-number: expected 2 arguments, got {}", args.len()));
}
Ok(arena.int(0))
}
pub fn element_number_list(_arena: &Arena, _args: &[ValueId]) -> ArenaResult {
Ok(crate::scheme::arena::NIL_ID)
}
pub fn inherited_attribute_string(_arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.is_empty() || args.len() > 2 {
return Err(format!("inherited-attribute-string: expected 1-2 arguments, got {}", args.len()));
}
Ok(crate::scheme::arena::FALSE_ID)
}
pub fn inherited_element_attribute_string(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() < 1 || args.len() > 2 {
return Err(format!("inherited-element-attribute-string: expected 1 or 2 arguments, got {}", args.len()));
}
Ok(arena.string(String::new()))
}
pub fn inherited_start_indent(arena: &mut Arena, _args: &[ValueId]) -> ArenaResult {
Ok(arena.int(0))
}
pub fn inherited_end_indent(arena: &mut Arena, _args: &[ValueId]) -> ArenaResult {
Ok(arena.int(0))
}
pub fn inherited_line_spacing(arena: &mut Arena, _args: &[ValueId]) -> ArenaResult {
Ok(arena.int(0))
}
pub fn inherited_font_family_name(arena: &mut Arena, _args: &[ValueId]) -> ArenaResult {
Ok(arena.string(String::new()))
}
pub fn inherited_font_size(arena: &mut Arena, _args: &[ValueId]) -> ArenaResult {
Ok(arena.int(12))
}
pub fn inherited_font_weight(arena: &mut Arena, _args: &[ValueId]) -> ArenaResult {
Ok(arena.string(String::new()))
}
pub fn inherited_font_posture(arena: &mut Arena, _args: &[ValueId]) -> ArenaResult {
Ok(arena.string(String::new()))
}
pub fn inherited_dbhtml_value(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err(format!("inherited-dbhtml-value: expected 1 argument, got {}", args.len()));
}
Ok(arena.string(String::new()))
}
pub fn inherited_pi_value(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err(format!("inherited-pi-value: expected 1 argument, got {}", args.len()));
}
Ok(arena.string(String::new()))
}
pub fn node_list(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
use crate::grove::VecNodeList;
let mut result_nodes = Vec::new();
for arg in args {
match arena.get(*arg) {
ValueData::Node(node) => {
result_nodes.push(node.as_ref().clone_node());
}
ValueData::NodeList(nl) => {
let len = nl.length();
for i in 0..len {
if let Some(node) = nl.get(i) {
result_nodes.push(node);
}
}
}
_ => {
return Err("node-list: arguments must be nodes or node-lists".to_string());
}
}
}
Ok(arena.alloc(ValueData::NodeList(Rc::new(Box::new(VecNodeList::new(result_nodes))))))
}
pub fn node_list_eq_p(arena: &Arena, args: &[ValueId]) -> ArenaResult {
use crate::scheme::arena::{FALSE_ID, TRUE_ID};
if args.len() != 2 {
return Err(format!("node-list=?: expected 2 arguments, got {}", args.len()));
}
if args[0] == args[1] {
return Ok(TRUE_ID);
}
let nl1 = match arena.get(args[0]) {
ValueData::Bool(false) => {
vec![]
}
ValueData::Node(n) => {
vec![n.clone()]
}
ValueData::NodeList(nl) => {
let mut nodes = Vec::new();
let mut index = 0;
while let Some(node) = nl.get(index) {
nodes.push(std::rc::Rc::new(node));
index += 1;
}
nodes
}
_ => return Err("node-list=?: first argument must be a node, node-list, or #f".to_string()),
};
let nl2 = match arena.get(args[1]) {
ValueData::Bool(false) => {
vec![]
}
ValueData::Node(n) => {
vec![n.clone()]
}
ValueData::NodeList(nl) => {
let mut nodes = Vec::new();
let mut index = 0;
while let Some(node) = nl.get(index) {
nodes.push(std::rc::Rc::new(node));
index += 1;
}
nodes
}
v => {
let type_name = match v {
ValueData::Nil => "empty list",
ValueData::Bool(_) => "boolean",
ValueData::Integer(_) => "integer",
ValueData::Real(_) => "real",
ValueData::Char(_) => "char",
ValueData::String(_) => "string",
ValueData::Symbol(_) => "symbol",
ValueData::Keyword(_) => "keyword",
ValueData::Pair { .. } => "pair",
ValueData::Vector(_) => "vector",
ValueData::Procedure(_) => "procedure",
ValueData::Quantity { .. } => "quantity",
ValueData::Sosofo => "sosofo",
ValueData::Unspecified => "unspecified",
ValueData::Error => "error",
_ => "unknown",
};
return Err(format!("node-list=?: second argument must be a node, node-list, or #f (got {})", type_name));
}
};
if nl1.len() != nl2.len() {
return Ok(FALSE_ID);
}
for (node1, node2) in nl1.iter().zip(nl2.iter()) {
if !node1.as_ref().as_ref().node_eq(node2.as_ref().as_ref()) {
return Ok(FALSE_ID);
}
}
Ok(TRUE_ID)
}
pub fn node_list_count(arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err(format!("node-list-count: expected 1 argument, got {}", args.len()));
}
let unique_nl = node_list_remove_duplicates(arena, args)?;
node_list_length(arena, &[unique_nl])
}
pub fn node_list_union_map(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err(format!("node-list-union-map: expected 2 arguments, got {}", args.len()));
}
Ok(crate::scheme::arena::NIL_ID)
}
pub fn node_list_symmetrical_difference(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err(format!("node-list-symmetrical-difference: expected 2 arguments, got {}", args.len()));
}
Ok(crate::scheme::arena::NIL_ID)
}
pub fn node_list_address(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.is_empty() || args.len() > 2 {
return Err(format!("node-list-address: expected 1-2 arguments, got {}", args.len()));
}
Ok(crate::scheme::arena::NIL_ID)
}
pub fn node_list_error(_arena: &mut Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err(format!("node-list-error: expected 1 argument, got {}", args.len()));
}
Ok(crate::scheme::arena::NIL_ID)
}
pub fn node_list_no_order(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err(format!("node-list-no-order: expected 1 argument, got {}", args.len()));
}
Ok(crate::scheme::arena::NIL_ID)
}
pub fn origin_to_subnode_rel_forest_addr(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err(format!("origin-to-subnode-rel-forest-addr: expected 1 argument, got {}", args.len()));
}
Ok(crate::scheme::arena::NIL_ID)
}
pub fn named_node(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err(format!("named-node: expected 1 argument, got {}", args.len()));
}
Ok(crate::scheme::arena::FALSE_ID)
}
pub fn named_node_list_p(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err(format!("named-node-list?: expected 1 argument, got {}", args.len()));
}
Ok(crate::scheme::arena::FALSE_ID)
}
pub fn named_node_list_names(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err(format!("named-node-list-names: expected 1 argument, got {}", args.len()));
}
Ok(crate::scheme::arena::NIL_ID)
}
pub fn select_by_class(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err(format!("select-by-class: expected 1 argument, got {}", args.len()));
}
Ok(crate::scheme::arena::NIL_ID)
}
pub fn select_children(_arena: &Arena, _args: &[ValueId]) -> ArenaResult {
Ok(crate::scheme::arena::NIL_ID)
}
pub fn process_children_trim(_arena: &Arena, _args: &[ValueId]) -> ArenaResult {
Ok(crate::scheme::arena::UNSPECIFIED_ID)
}
pub fn process_element_with_id(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err(format!("process-element-with-id: expected 1 argument, got {}", args.len()));
}
Ok(crate::scheme::arena::UNSPECIFIED_ID)
}
pub fn process_first_descendant(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 1 {
return Err(format!("process-first-descendant: expected 1 argument, got {}", args.len()));
}
Ok(crate::scheme::arena::UNSPECIFIED_ID)
}
pub fn process_matching_children(_arena: &Arena, args: &[ValueId]) -> ArenaResult {
if args.len() != 2 {
return Err(format!("process-matching-children: expected 2 arguments, got {}", args.len()));
}
Ok(crate::scheme::arena::UNSPECIFIED_ID)
}
pub fn next_match(_arena: &Arena, _args: &[ValueId]) -> ArenaResult {
Ok(crate::scheme::arena::UNSPECIFIED_ID)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_car() {
let mut arena = Arena::new();
let one = arena.int(1);
let two = arena.int(2);
let pair = arena.cons(one, two);
let result = car(&arena, &[pair]).unwrap();
assert_eq!(result, one);
}
#[test]
fn test_cdr() {
let mut arena = Arena::new();
let one = arena.int(1);
let two = arena.int(2);
let pair = arena.cons(one, two);
let result = cdr(&arena, &[pair]).unwrap();
assert_eq!(result, two);
}
#[test]
fn test_cons() {
let mut arena = Arena::new();
let one = arena.int(1);
let two = arena.int(2);
let pair = cons(&mut arena, &[one, two]).unwrap();
match arena.get(pair) {
ValueData::Pair { car, cdr, .. } => {
assert_eq!(*car, one);
assert_eq!(*cdr, two);
}
_ => panic!("Expected pair"),
}
}
#[test]
fn test_null() {
let mut arena = Arena::new();
assert_eq!(
null(&arena, &[NIL_ID]).unwrap(),
crate::scheme::arena::TRUE_ID
);
let one = arena.int(1);
assert_eq!(
null(&arena, &[one]).unwrap(),
crate::scheme::arena::FALSE_ID
);
}
#[test]
fn test_equal() {
let mut arena = Arena::new();
let one1 = arena.int(1);
let one2 = arena.int(1);
assert_eq!(
equal(&arena, &[one1, one2]).unwrap(),
crate::scheme::arena::TRUE_ID
);
let two = arena.int(2);
assert_eq!(
equal(&arena, &[one1, two]).unwrap(),
crate::scheme::arena::FALSE_ID
);
let list1 = arena.cons(one1, NIL_ID);
let list2 = arena.cons(one2, NIL_ID);
assert_eq!(
equal(&arena, &[list1, list2]).unwrap(),
crate::scheme::arena::TRUE_ID
);
}
}