#![allow(dead_code)]
use crate::diagnostics::{Error as DiagnosticError, Result};
use crate::eval::value::{Value, PrimitiveProcedure, PrimitiveImpl, ThreadSafeEnvironment};
use crate::effects::Effect;
use std::sync::Arc;
pub fn create_list_bindings(env: &Arc<ThreadSafeEnvironment>) {
bind_basic_list_operations(env);
bind_list_predicates(env);
bind_list_accessors(env);
bind_list_manipulation(env);
bind_higher_order_functions(env);
bind_list_utilities(env);
}
fn bind_basic_list_operations(env: &Arc<ThreadSafeEnvironment>) {
if env.lookup("cons").is_none() {
env.define("cons".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "cons".to_string(),
arity_min: 2,
arity_max: Some(2),
implementation: PrimitiveImpl::RustFn(primitive_cons),
effects: vec![Effect::Pure],
})));
}
if env.lookup("car").is_none() {
env.define("car".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "car".to_string(),
arity_min: 1,
arity_max: Some(1),
implementation: PrimitiveImpl::RustFn(primitive_car),
effects: vec![Effect::Pure],
})));
}
if env.lookup("cdr").is_none() {
env.define("cdr".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "cdr".to_string(),
arity_min: 1,
arity_max: Some(1),
implementation: PrimitiveImpl::RustFn(primitive_cdr),
effects: vec![Effect::Pure],
})));
}
env.define("list".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "list".to_string(),
arity_min: 0,
arity_max: None,
implementation: PrimitiveImpl::RustFn(primitive_list),
effects: vec![Effect::Pure],
})));
env.define("list*".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "list*".to_string(),
arity_min: 1,
arity_max: None,
implementation: PrimitiveImpl::RustFn(primitive_list_star),
effects: vec![Effect::Pure],
})));
env.define("make-list".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "make-list".to_string(),
arity_min: 1,
arity_max: Some(2),
implementation: PrimitiveImpl::RustFn(primitive_make_list),
effects: vec![Effect::Pure],
})));
}
fn bind_list_predicates(env: &Arc<ThreadSafeEnvironment>) {
env.define("pair?".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "pair?".to_string(),
arity_min: 1,
arity_max: Some(1),
implementation: PrimitiveImpl::RustFn(primitive_pair_p),
effects: vec![Effect::Pure],
})));
env.define("null?".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "null?".to_string(),
arity_min: 1,
arity_max: Some(1),
implementation: PrimitiveImpl::RustFn(primitive_null_p),
effects: vec![Effect::Pure],
})));
env.define("list?".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "list?".to_string(),
arity_min: 1,
arity_max: Some(1),
implementation: PrimitiveImpl::RustFn(primitive_list_p),
effects: vec![Effect::Pure],
})));
}
fn bind_list_accessors(env: &Arc<ThreadSafeEnvironment>) {
let combinations = [
("caar", "car", "car"),
("cadr", "cdr", "car"),
("cdar", "car", "cdr"),
("cddr", "cdr", "cdr"),
("caaar", "caar", "car"),
("caadr", "cadr", "car"),
("cadar", "cdar", "car"),
("caddr", "cddr", "car"),
("cdaar", "caar", "cdr"),
("cdadr", "cadr", "cdr"),
("cddar", "cdar", "cdr"),
("cdddr", "cddr", "cdr"),
("caaaar", "caaar", "car"),
("caaadr", "caadr", "car"),
("caadar", "cadar", "car"),
("caaddr", "caddr", "car"),
("cadaar", "cdaar", "car"),
("cadadr", "cdadr", "car"),
("caddar", "cddar", "car"),
("cadddr", "cdddr", "car"),
("cdaaar", "caaar", "cdr"),
("cdaadr", "caadr", "cdr"),
("cdadar", "cadar", "cdr"),
("cdaddr", "caddr", "cdr"),
("cddaar", "cdaar", "cdr"),
("cddadr", "cdadr", "cdr"),
("cdddar", "cddar", "cdr"),
("cddddr", "cdddr", "cdr"),
];
for (name, _, _) in &combinations {
env.define(name.to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: name.to_string(),
arity_min: 1,
arity_max: Some(1),
implementation: PrimitiveImpl::RustFn(make_car_cdr_combination(name)),
effects: vec![Effect::Pure],
})));
}
env.define("list-ref".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "list-ref".to_string(),
arity_min: 2,
arity_max: Some(2),
implementation: PrimitiveImpl::RustFn(primitive_list_ref),
effects: vec![Effect::Pure],
})));
env.define("list-tail".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "list-tail".to_string(),
arity_min: 2,
arity_max: Some(2),
implementation: PrimitiveImpl::RustFn(primitive_list_tail),
effects: vec![Effect::Pure],
})));
}
fn bind_list_manipulation(env: &Arc<ThreadSafeEnvironment>) {
env.define("length".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "length".to_string(),
arity_min: 1,
arity_max: Some(1),
implementation: PrimitiveImpl::RustFn(primitive_length),
effects: vec![Effect::Pure],
})));
env.define("append".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "append".to_string(),
arity_min: 0,
arity_max: None,
implementation: PrimitiveImpl::RustFn(primitive_append),
effects: vec![Effect::Pure],
})));
env.define("reverse".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "reverse".to_string(),
arity_min: 1,
arity_max: Some(1),
implementation: PrimitiveImpl::RustFn(primitive_reverse),
effects: vec![Effect::Pure],
})));
env.define("set-car!".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "set-car!".to_string(),
arity_min: 2,
arity_max: Some(2),
implementation: PrimitiveImpl::RustFn(primitive_set_car),
effects: vec![Effect::State], })));
env.define("set-cdr!".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "set-cdr!".to_string(),
arity_min: 2,
arity_max: Some(2),
implementation: PrimitiveImpl::RustFn(primitive_set_cdr),
effects: vec![Effect::State], })));
env.define("list-set!".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "list-set!".to_string(),
arity_min: 3,
arity_max: Some(3),
implementation: PrimitiveImpl::RustFn(primitive_list_set),
effects: vec![Effect::State], })));
env.define("list-copy".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "list-copy".to_string(),
arity_min: 1,
arity_max: Some(1),
implementation: PrimitiveImpl::RustFn(primitive_list_copy),
effects: vec![Effect::Pure],
})));
}
fn bind_higher_order_functions(env: &Arc<ThreadSafeEnvironment>) {
env.define("map".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "map".to_string(),
arity_min: 2,
arity_max: None,
implementation: PrimitiveImpl::EvaluatorIntegrated(evaluator_map),
effects: vec![Effect::Pure], })));
env.define("for-each".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "for-each".to_string(),
arity_min: 2,
arity_max: None,
implementation: PrimitiveImpl::EvaluatorIntegrated(evaluator_for_each),
effects: vec![Effect::State], })));
env.define("filter".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "filter".to_string(),
arity_min: 2,
arity_max: Some(2),
implementation: PrimitiveImpl::EvaluatorIntegrated(evaluator_filter),
effects: vec![Effect::Pure],
})));
env.define("fold-left".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "fold-left".to_string(),
arity_min: 3,
arity_max: None,
implementation: PrimitiveImpl::EvaluatorIntegrated(evaluator_fold_left),
effects: vec![Effect::Pure],
})));
env.define("fold-right".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "fold-right".to_string(),
arity_min: 3,
arity_max: None,
implementation: PrimitiveImpl::EvaluatorIntegrated(evaluator_fold_right),
effects: vec![Effect::Pure],
})));
env.define("any".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "any".to_string(),
arity_min: 2,
arity_max: None,
implementation: PrimitiveImpl::RustFn(primitive_any),
effects: vec![Effect::Pure],
})));
env.define("every".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "every".to_string(),
arity_min: 2,
arity_max: None,
implementation: PrimitiveImpl::RustFn(primitive_every),
effects: vec![Effect::Pure],
})));
}
fn bind_list_utilities(env: &Arc<ThreadSafeEnvironment>) {
env.define("member".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "member".to_string(),
arity_min: 2,
arity_max: Some(3),
implementation: PrimitiveImpl::RustFn(primitive_member),
effects: vec![Effect::Pure],
})));
env.define("memq".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "memq".to_string(),
arity_min: 2,
arity_max: Some(2),
implementation: PrimitiveImpl::RustFn(primitive_memq),
effects: vec![Effect::Pure],
})));
env.define("memv".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "memv".to_string(),
arity_min: 2,
arity_max: Some(2),
implementation: PrimitiveImpl::RustFn(primitive_memv),
effects: vec![Effect::Pure],
})));
env.define("assoc".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "assoc".to_string(),
arity_min: 2,
arity_max: Some(3),
implementation: PrimitiveImpl::RustFn(primitive_assoc),
effects: vec![Effect::Pure],
})));
env.define("assq".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "assq".to_string(),
arity_min: 2,
arity_max: Some(2),
implementation: PrimitiveImpl::RustFn(primitive_assq),
effects: vec![Effect::Pure],
})));
env.define("assv".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "assv".to_string(),
arity_min: 2,
arity_max: Some(2),
implementation: PrimitiveImpl::RustFn(primitive_assv),
effects: vec![Effect::Pure],
})));
env.define("sort".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "sort".to_string(),
arity_min: 2,
arity_max: Some(2),
implementation: PrimitiveImpl::RustFn(primitive_sort),
effects: vec![Effect::Pure],
})));
}
fn primitive_cons(args: &[Value]) -> Result<Value> {
if args.len() != 2 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("cons expects 2 arguments, got {}", args.len()),
None,
)));
}
Ok(Value::pair(args[0].clone(), args[1].clone()))
}
fn primitive_car(args: &[Value]) -> Result<Value> {
if args.len() != 1 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("car expects 1 argument, got {}", args.len()),
None,
)));
}
match &args[0] {
Value::Pair(car, _) => Ok((**car).clone()),
Value::MutablePair(car_ref, _) => {
if let Ok(car) = car_ref.read() {
Ok(car.clone())
} else {
Err(Box::new(DiagnosticError::runtime_error(
"car failed to acquire read lock".to_string(),
None,
)))
}
}
_ => Err(Box::new(DiagnosticError::runtime_error(
"car requires a pair".to_string(),
None,
))),
}
}
fn primitive_cdr(args: &[Value]) -> Result<Value> {
if args.len() != 1 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("cdr expects 1 argument, got {}", args.len()),
None,
)));
}
match &args[0] {
Value::Pair(_, cdr) => Ok((**cdr).clone()),
Value::MutablePair(_, cdr_ref) => {
if let Ok(cdr) = cdr_ref.read() {
Ok(cdr.clone())
} else {
Err(Box::new(DiagnosticError::runtime_error(
"cdr failed to acquire read lock".to_string(),
None,
)))
}
}
_ => Err(Box::new(DiagnosticError::runtime_error(
"cdr requires a pair".to_string(),
None,
))),
}
}
fn primitive_list(args: &[Value]) -> Result<Value> {
Ok(Value::list(args.to_vec()))
}
fn primitive_list_star(args: &[Value]) -> Result<Value> {
if args.is_empty() {
return Err(Box::new(DiagnosticError::runtime_error(
"list* requires at least 1 argument".to_string(),
None,
)));
}
if args.len() == 1 {
return Ok(args[0].clone());
}
let mut result = args[args.len() - 1].clone();
for arg in args[..args.len() - 1].iter().rev() {
result = Value::pair(arg.clone(), result);
}
Ok(result)
}
fn primitive_make_list(args: &[Value]) -> Result<Value> {
if args.is_empty() || args.len() > 2 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("make-list expects 1 or 2 arguments, got {}", args.len()),
None,
)));
}
let length = args[0].as_integer().ok_or_else(|| {
DiagnosticError::runtime_error(
"make-list first argument must be a non-negative integer".to_string(),
None,
)
})?;
if length < 0 {
return Err(Box::new(DiagnosticError::runtime_error(
"make-list length must be non-negative".to_string(),
None,
)));
}
let fill = if args.len() == 2 {
args[1].clone()
} else {
Value::Unspecified
};
let elements = vec![fill; length as usize];
Ok(Value::list(elements))
}
fn primitive_pair_p(args: &[Value]) -> Result<Value> {
if args.len() != 1 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("pair? expects 1 argument, got {}", args.len()),
None,
)));
}
Ok(Value::boolean(args[0].is_pair()))
}
fn primitive_null_p(args: &[Value]) -> Result<Value> {
if args.len() != 1 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("null? expects 1 argument, got {}", args.len()),
None,
)));
}
Ok(Value::boolean(args[0].is_nil()))
}
fn primitive_list_p(args: &[Value]) -> Result<Value> {
if args.len() != 1 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("list? expects 1 argument, got {}", args.len()),
None,
)));
}
Ok(Value::boolean(is_proper_list(&args[0])))
}
fn make_car_cdr_combination(name: &str) -> fn(&[Value]) -> Result<Value> {
match name {
"caar" => |args| {
let result = primitive_car(args)?;
primitive_car(&[result])
},
"cadr" => |args| {
let result = primitive_cdr(args)?;
primitive_car(&[result])
},
"cdar" => |args| {
let result = primitive_car(args)?;
primitive_cdr(&[result])
},
"cddr" => |args| {
let result = primitive_cdr(args)?;
primitive_cdr(&[result])
},
_ => {
fn unknown_combination(_args: &[Value]) -> Result<Value> {
Err(Box::new(DiagnosticError::runtime_error(
"Unknown car/cdr combination".to_string(),
None,
)))
}
unknown_combination
},
}
}
fn primitive_list_ref(args: &[Value]) -> Result<Value> {
if args.len() != 2 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("list-ref expects 2 arguments, got {}", args.len()),
None,
)));
}
let index = args[1].as_integer().ok_or_else(|| {
DiagnosticError::runtime_error(
"list-ref index must be a non-negative integer".to_string(),
None,
)
})?;
if index < 0 {
return Err(Box::new(DiagnosticError::runtime_error(
"list-ref index must be non-negative".to_string(),
None,
)));
}
let mut current = &args[0];
let mut i = 0;
while i < index {
match current {
Value::Pair(_, cdr) => {
current = cdr;
i += 1;
}
Value::MutablePair(_, cdr_ref) => {
if let Some(list_values) = args[0].as_list() {
if index as usize >= list_values.len() {
return Err(Box::new(DiagnosticError::runtime_error(
"list-ref index out of bounds".to_string(),
None,
)));
}
return Ok(list_values[index as usize].clone());
} else {
return Err(Box::new(DiagnosticError::runtime_error(
"list-ref requires a proper list".to_string(),
None,
)));
}
}
_ => {
return Err(Box::new(DiagnosticError::runtime_error(
"list-ref index out of bounds".to_string(),
None,
)));
}
}
}
match current {
Value::Pair(car, _) => Ok((**car).clone()),
Value::MutablePair(car_ref, _) => {
if let Ok(car) = car_ref.read() {
Ok(car.clone())
} else {
Err(Box::new(DiagnosticError::runtime_error(
"list-ref failed to acquire read lock".to_string(),
None,
)))
}
}
_ => Err(Box::new(DiagnosticError::runtime_error(
"list-ref index out of bounds".to_string(),
None,
))),
}
}
fn primitive_list_tail(args: &[Value]) -> Result<Value> {
if args.len() != 2 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("list-tail expects 2 arguments, got {}", args.len()),
None,
)));
}
let k = args[1].as_integer().ok_or_else(|| {
DiagnosticError::runtime_error(
"list-tail k must be a non-negative integer".to_string(),
None,
)
})?;
if k < 0 {
return Err(Box::new(DiagnosticError::runtime_error(
"list-tail k must be non-negative".to_string(),
None,
)));
}
let mut current = args[0].clone();
let mut i = 0;
while i < k {
match current {
Value::Pair(_, cdr) => {
current = cdr.as_ref().clone();
i += 1;
}
_ => {
return Err(Box::new(DiagnosticError::runtime_error(
"list-tail k out of bounds".to_string(),
None,
)));
}
}
}
Ok(current)
}
fn primitive_length(args: &[Value]) -> Result<Value> {
if args.len() != 1 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("length expects 1 argument, got {}", args.len()),
None,
)));
}
let mut current = &args[0];
let mut length = 0;
loop {
match current {
Value::Nil => break,
Value::Pair(_, cdr) => {
current = cdr;
length += 1;
}
Value::MutablePair(_, cdr_ref) => {
if let Ok(cdr) = cdr_ref.read() {
if let Some(list) = args[0].as_list() {
return Ok(Value::integer(list.len() as i64));
} else {
return Err(Box::new(DiagnosticError::runtime_error(
"length requires a proper list".to_string(),
None,
)));
}
} else {
return Err(Box::new(DiagnosticError::runtime_error(
"length failed to acquire read lock".to_string(),
None,
)));
}
}
_ => {
return Err(Box::new(DiagnosticError::runtime_error(
"length requires a proper list".to_string(),
None,
)));
}
}
}
Ok(Value::integer(length))
}
fn primitive_append(args: &[Value]) -> Result<Value> {
if args.is_empty() {
return Ok(Value::Nil);
}
if args.len() == 1 {
return Ok(args[0].clone());
}
for arg in &args[..args.len() - 1] {
if !is_proper_list(arg) {
return Err(Box::new(DiagnosticError::runtime_error(
"append arguments (except the last) must be proper lists".to_string(),
None,
)));
}
}
let mut result = args[args.len() - 1].clone();
for arg in args[..args.len() - 1].iter().rev() {
if let Some(list) = arg.as_list() {
for item in list.into_iter().rev() {
result = Value::pair(item, result);
}
}
}
Ok(result)
}
fn primitive_reverse(args: &[Value]) -> Result<Value> {
if args.len() != 1 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("reverse expects 1 argument, got {}", args.len()),
None,
)));
}
if let Some(list) = args[0].as_list() {
let mut reversed = list;
reversed.reverse();
Ok(Value::list(reversed))
} else {
Err(Box::new(DiagnosticError::runtime_error(
"reverse requires a proper list".to_string(),
None,
)))
}
}
fn primitive_set_car(args: &[Value]) -> Result<Value> {
if args.len() != 2 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("set-car! expects 2 arguments, got {}", args.len()),
None,
)));
}
match &args[0] {
Value::MutablePair(car_ref, _) => {
if let Ok(mut car) = car_ref.write() {
*car = args[1].clone();
Ok(Value::Unspecified)
} else {
Err(Box::new(DiagnosticError::runtime_error(
"set-car! failed to acquire write lock".to_string(),
None,
)))
}
}
Value::Pair(_, _) => {
Err(Box::new(DiagnosticError::runtime_error(
"set-car! requires a mutable pair (immutable pair given)".to_string(),
None,
)))
}
_ => {
Err(Box::new(DiagnosticError::runtime_error(
"set-car! requires a pair".to_string(),
None,
)))
}
}
}
fn primitive_set_cdr(args: &[Value]) -> Result<Value> {
if args.len() != 2 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("set-cdr! expects 2 arguments, got {}", args.len()),
None,
)));
}
match &args[0] {
Value::MutablePair(_, cdr_ref) => {
if let Ok(mut cdr) = cdr_ref.write() {
*cdr = args[1].clone();
Ok(Value::Unspecified)
} else {
Err(Box::new(DiagnosticError::runtime_error(
"set-cdr! failed to acquire write lock".to_string(),
None,
)))
}
}
Value::Pair(_, _) => {
Err(Box::new(DiagnosticError::runtime_error(
"set-cdr! requires a mutable pair (immutable pair given)".to_string(),
None,
)))
}
_ => {
Err(Box::new(DiagnosticError::runtime_error(
"set-cdr! requires a pair".to_string(),
None,
)))
}
}
}
fn primitive_list_set(args: &[Value]) -> Result<Value> {
if args.len() != 3 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("list-set! expects 3 arguments, got {}", args.len()),
None,
)));
}
let index = args[1].as_integer().ok_or_else(|| {
DiagnosticError::runtime_error(
"list-set! index must be a non-negative integer".to_string(),
None,
)
})?;
if index < 0 {
return Err(Box::new(DiagnosticError::runtime_error(
"list-set! index must be non-negative".to_string(),
None,
)));
}
let mut current = &args[0];
let mut steps_remaining = index;
while steps_remaining > 0 {
match current {
Value::MutablePair(_, cdr_ref) => {
if let Ok(cdr) = cdr_ref.read() {
let cdr_clone = cdr.clone();
drop(cdr); match cdr_clone {
Value::MutablePair(_, _) | Value::Pair(_, _) => {
return set_list_element(&args[0], index, args[2].clone());
}
_ => {
return Err(Box::new(DiagnosticError::runtime_error(
"list-set! index out of bounds".to_string(),
None,
)));
}
}
} else {
return Err(Box::new(DiagnosticError::runtime_error(
"list-set! failed to acquire read lock".to_string(),
None,
)));
}
}
Value::Pair(_, cdr) => {
current = cdr;
steps_remaining -= 1;
}
_ => {
return Err(Box::new(DiagnosticError::runtime_error(
"list-set! index out of bounds".to_string(),
None,
)));
}
}
}
match current {
Value::MutablePair(car_ref, _) => {
if let Ok(mut car) = car_ref.write() {
*car = args[2].clone();
Ok(Value::Unspecified)
} else {
Err(Box::new(DiagnosticError::runtime_error(
"list-set! failed to acquire write lock".to_string(),
None,
)))
}
}
_ => {
Err(Box::new(DiagnosticError::runtime_error(
"list-set! requires a mutable list".to_string(),
None,
)))
}
}
}
fn primitive_list_copy(args: &[Value]) -> Result<Value> {
if args.len() != 1 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("list-copy expects 1 argument, got {}", args.len()),
None,
)));
}
copy_list(&args[0])
}
fn set_list_element(list: &Value, index: i64, value: Value) -> Result<Value> {
if index == 0 {
match list {
Value::MutablePair(car_ref, _) => {
if let Ok(mut car) = car_ref.write() {
*car = value;
Ok(Value::Unspecified)
} else {
Err(Box::new(DiagnosticError::runtime_error(
"set-list-element failed to acquire write lock".to_string(),
None,
)))
}
}
_ => Err(Box::new(DiagnosticError::runtime_error(
"set-list-element requires a mutable list".to_string(),
None,
)))
}
} else {
match list {
Value::MutablePair(_, cdr_ref) => {
if let Ok(cdr) = cdr_ref.read() {
let cdr_clone = cdr.clone();
drop(cdr); set_list_element(&cdr_clone, index - 1, value)
} else {
Err(Box::new(DiagnosticError::runtime_error(
"set-list-element failed to acquire read lock".to_string(),
None,
)))
}
}
Value::Pair(_, cdr) => {
set_list_element(cdr, index - 1, value)
}
_ => Err(Box::new(DiagnosticError::runtime_error(
"set-list-element index out of bounds".to_string(),
None,
)))
}
}
}
fn primitive_map(args: &[Value]) -> Result<Value> {
if args.len() < 2 {
return Err(Box::new(DiagnosticError::runtime_error(
"map requires at least 2 arguments".to_string(),
None,
)));
}
let procedure = &args[0];
let lists = &args[1..];
if !procedure.is_procedure() {
return Err(Box::new(DiagnosticError::runtime_error(
"map first argument must be a procedure".to_string(),
None,
)));
}
let mut list_vectors = Vec::new();
let mut min_length = usize::MAX;
for (i, list_arg) in lists.iter().enumerate() {
if let Some(list_values) = list_arg.as_list() {
min_length = min_length.min(list_values.len());
list_vectors.push(list_values);
} else {
return Err(Box::new(DiagnosticError::runtime_error(
format!("map argument {} must be a list", i + 2),
None,
)));
}
}
if min_length == 0 || min_length == usize::MAX {
return Ok(Value::Nil);
}
let mut results = Vec::new();
for i in 0..min_length {
let mut proc_args = Vec::new();
for list in &list_vectors {
proc_args.push(list[i].clone());
}
match procedure {
Value::Primitive(prim) => {
let result = match &prim.implementation {
PrimitiveImpl::RustFn(func) => func(&proc_args)?,
PrimitiveImpl::Native(func) => func(&proc_args)?,
PrimitiveImpl::EvaluatorIntegrated(_) => {
return Err(Box::new(DiagnosticError::runtime_error(
"map with EvaluatorIntegrated functions requires evaluator access (not yet implemented)".to_string(),
None,
)));
}
PrimitiveImpl::ForeignFn { .. } => {
return Err(Box::new(DiagnosticError::runtime_error(
"map with foreign functions not yet implemented".to_string(),
None,
)));
}
};
results.push(result);
},
_ => {
return Err(Box::new(DiagnosticError::runtime_error(
"map with user-defined procedures requires evaluator integration (not yet implemented)".to_string(),
None,
)));
}
}
}
Ok(Value::list(results))
}
fn primitive_for_each(args: &[Value]) -> Result<Value> {
if args.len() < 2 {
return Err(Box::new(DiagnosticError::runtime_error(
"for-each requires at least 2 arguments".to_string(),
None,
)));
}
let procedure = &args[0];
let lists = &args[1..];
if !procedure.is_procedure() {
return Err(Box::new(DiagnosticError::runtime_error(
"for-each first argument must be a procedure".to_string(),
None,
)));
}
let mut list_vectors = Vec::new();
let mut min_length = usize::MAX;
for (i, list_arg) in lists.iter().enumerate() {
if let Some(list_values) = list_arg.as_list() {
min_length = min_length.min(list_values.len());
list_vectors.push(list_values);
} else {
return Err(Box::new(DiagnosticError::runtime_error(
format!("for-each argument {} must be a list", i + 2),
None,
)));
}
}
if min_length == 0 || min_length == usize::MAX {
return Ok(Value::Unspecified);
}
for i in 0..min_length {
let mut proc_args = Vec::new();
for list in &list_vectors {
proc_args.push(list[i].clone());
}
match procedure {
Value::Primitive(prim) => {
match &prim.implementation {
PrimitiveImpl::RustFn(func) => {
func(&proc_args)?;
},
PrimitiveImpl::Native(func) => {
func(&proc_args)?;
},
PrimitiveImpl::EvaluatorIntegrated(_) => {
return Err(Box::new(DiagnosticError::runtime_error(
"for-each with EvaluatorIntegrated functions requires evaluator access (not yet implemented)".to_string(),
None,
)));
}
PrimitiveImpl::ForeignFn { .. } => {
return Err(Box::new(DiagnosticError::runtime_error(
"for-each with foreign functions not yet implemented".to_string(),
None,
)));
}
}
},
_ => {
return Err(Box::new(DiagnosticError::runtime_error(
"for-each with user-defined procedures requires evaluator integration (not yet implemented)".to_string(),
None,
)));
}
}
}
Ok(Value::Unspecified)
}
fn primitive_filter(args: &[Value]) -> Result<Value> {
if args.len() != 2 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("filter expects 2 arguments, got {}", args.len()),
None,
)));
}
let predicate = &args[0];
let list_arg = &args[1];
if !predicate.is_procedure() {
return Err(Box::new(DiagnosticError::runtime_error(
"filter first argument must be a procedure".to_string(),
None,
)));
}
let list = list_arg.as_list().ok_or_else(|| {
DiagnosticError::runtime_error(
"filter requires a proper list".to_string(),
None,
)
})?;
let mut results = Vec::new();
for element in list {
let proc_args = vec![element.clone()];
let keep = match predicate {
Value::Primitive(prim) => {
let result = match &prim.implementation {
PrimitiveImpl::RustFn(func) => func(&proc_args)?,
PrimitiveImpl::Native(func) => func(&proc_args)?,
PrimitiveImpl::EvaluatorIntegrated(_) => {
return Err(Box::new(DiagnosticError::runtime_error(
"filter with EvaluatorIntegrated functions requires evaluator access (not yet implemented)".to_string(),
None,
)));
}
PrimitiveImpl::ForeignFn { .. } => {
return Err(Box::new(DiagnosticError::runtime_error(
"filter with foreign functions not yet implemented".to_string(),
None,
)));
}
};
result.is_truthy()
},
_ => {
return Err(Box::new(DiagnosticError::runtime_error(
"filter with user-defined procedures requires evaluator integration (not yet implemented)".to_string(),
None,
)));
}
};
if keep {
results.push(element);
}
}
Ok(Value::list(results))
}
fn primitive_fold_left(args: &[Value]) -> Result<Value> {
if args.len() < 3 {
return Err(Box::new(DiagnosticError::runtime_error(
"fold-left requires at least 3 arguments".to_string(),
None,
)));
}
let procedure = &args[0];
let mut accumulator = args[1].clone();
let lists = &args[2..];
if !procedure.is_procedure() {
return Err(Box::new(DiagnosticError::runtime_error(
"fold-left first argument must be a procedure".to_string(),
None,
)));
}
let mut list_data = Vec::new();
let mut min_length = usize::MAX;
for (i, list_arg) in lists.iter().enumerate() {
let list = list_arg.as_list().ok_or_else(|| {
DiagnosticError::runtime_error(
format!("fold-left argument {} must be a proper list", i + 3),
None,
)
})?;
min_length = min_length.min(list.len());
list_data.push(list);
}
if min_length == 0 || min_length == usize::MAX {
return Ok(accumulator);
}
for i in 0..min_length {
let mut proc_args = vec![accumulator];
for list in &list_data {
proc_args.push(list[i].clone());
}
match procedure {
Value::Primitive(prim) => {
accumulator = match &prim.implementation {
PrimitiveImpl::RustFn(func) => func(&proc_args)?,
PrimitiveImpl::Native(func) => func(&proc_args)?,
PrimitiveImpl::EvaluatorIntegrated(_) => {
return Err(Box::new(DiagnosticError::runtime_error(
"fold-left with EvaluatorIntegrated functions requires evaluator access (not yet implemented)".to_string(),
None,
)));
}
PrimitiveImpl::ForeignFn { .. } => {
return Err(Box::new(DiagnosticError::runtime_error(
"fold-left with foreign functions not yet implemented".to_string(),
None,
)));
}
};
},
_ => {
return Err(Box::new(DiagnosticError::runtime_error(
"fold-left with user-defined procedures requires evaluator integration (not yet implemented)".to_string(),
None,
)));
}
}
}
Ok(accumulator)
}
fn primitive_fold_right(args: &[Value]) -> Result<Value> {
if args.len() < 3 {
return Err(Box::new(DiagnosticError::runtime_error(
"fold-right requires at least 3 arguments".to_string(),
None,
)));
}
let procedure = &args[0];
let mut accumulator = args[1].clone();
let lists = &args[2..];
if !procedure.is_procedure() {
return Err(Box::new(DiagnosticError::runtime_error(
"fold-right first argument must be a procedure".to_string(),
None,
)));
}
let mut list_data = Vec::new();
let mut min_length = usize::MAX;
for (i, list_arg) in lists.iter().enumerate() {
let list = list_arg.as_list().ok_or_else(|| {
DiagnosticError::runtime_error(
format!("fold-right argument {} must be a proper list", i + 3),
None,
)
})?;
min_length = min_length.min(list.len());
list_data.push(list);
}
if min_length == 0 || min_length == usize::MAX {
return Ok(accumulator);
}
for i in (0..min_length).rev() {
let mut proc_args = Vec::new();
for list in &list_data {
proc_args.push(list[i].clone());
}
proc_args.push(accumulator);
match procedure {
Value::Primitive(prim) => {
accumulator = match &prim.implementation {
PrimitiveImpl::RustFn(func) => func(&proc_args)?,
PrimitiveImpl::Native(func) => func(&proc_args)?,
PrimitiveImpl::EvaluatorIntegrated(_) => {
return Err(Box::new(DiagnosticError::runtime_error(
"fold-right with EvaluatorIntegrated functions requires evaluator access (not yet implemented)".to_string(),
None,
)));
}
PrimitiveImpl::ForeignFn { .. } => {
return Err(Box::new(DiagnosticError::runtime_error(
"fold-right with foreign functions not yet implemented".to_string(),
None,
)));
}
};
},
_ => {
return Err(Box::new(DiagnosticError::runtime_error(
"fold-right with user-defined procedures requires evaluator integration (not yet implemented)".to_string(),
None,
)));
}
}
}
Ok(accumulator)
}
fn primitive_any(args: &[Value]) -> Result<Value> {
if args.len() < 2 {
return Err(Box::new(DiagnosticError::runtime_error(
"any requires at least 2 arguments".to_string(),
None,
)));
}
Err(Box::new(DiagnosticError::runtime_error(
"any requires evaluator integration (not yet implemented)".to_string(),
None,
)))
}
fn primitive_every(args: &[Value]) -> Result<Value> {
if args.len() < 2 {
return Err(Box::new(DiagnosticError::runtime_error(
"every requires at least 2 arguments".to_string(),
None,
)));
}
Err(Box::new(DiagnosticError::runtime_error(
"every requires evaluator integration (not yet implemented)".to_string(),
None,
)))
}
fn primitive_member(args: &[Value]) -> Result<Value> {
if args.len() < 2 || args.len() > 3 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("member expects 2 or 3 arguments, got {}", args.len()),
None,
)));
}
let obj = &args[0];
let mut current = &args[1];
loop {
match current {
Value::Nil => return Ok(Value::boolean(false)),
Value::Pair(car, cdr) => {
if values_equal(obj, car) {
return Ok(current.clone());
}
current = cdr;
}
_ => {
return Err(Box::new(DiagnosticError::runtime_error(
"member requires a proper list".to_string(),
None,
)));
}
}
}
}
fn primitive_memq(args: &[Value]) -> Result<Value> {
if args.len() != 2 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("memq expects 2 arguments, got {}", args.len()),
None,
)));
}
let obj = &args[0];
let mut current = &args[1];
loop {
match current {
Value::Nil => return Ok(Value::boolean(false)),
Value::Pair(car, cdr) => {
if values_eq(obj, car) {
return Ok(current.clone());
}
current = cdr;
}
_ => {
return Err(Box::new(DiagnosticError::runtime_error(
"memq requires a proper list".to_string(),
None,
)));
}
}
}
}
fn primitive_memv(args: &[Value]) -> Result<Value> {
if args.len() != 2 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("memv expects 2 arguments, got {}", args.len()),
None,
)));
}
let obj = &args[0];
let mut current = &args[1];
loop {
match current {
Value::Nil => return Ok(Value::boolean(false)),
Value::Pair(car, cdr) => {
if values_eqv(obj, car) {
return Ok(current.clone());
}
current = cdr;
}
_ => {
return Err(Box::new(DiagnosticError::runtime_error(
"memv requires a proper list".to_string(),
None,
)));
}
}
}
}
fn primitive_assoc(args: &[Value]) -> Result<Value> {
if args.len() < 2 || args.len() > 3 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("assoc expects 2 or 3 arguments, got {}", args.len()),
None,
)));
}
let obj = &args[0];
let mut current = &args[1];
loop {
match current {
Value::Nil => return Ok(Value::boolean(false)),
Value::Pair(car, cdr) => {
match car.as_ref() {
Value::Pair(key, _) => {
if values_equal(obj, key) {
return Ok((**car).clone());
}
}
_ => {
return Err(Box::new(DiagnosticError::runtime_error(
"assoc requires a list of pairs".to_string(),
None,
)));
}
}
current = cdr;
}
_ => {
return Err(Box::new(DiagnosticError::runtime_error(
"assoc requires a proper list".to_string(),
None,
)));
}
}
}
}
fn primitive_assq(args: &[Value]) -> Result<Value> {
if args.len() != 2 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("assq expects 2 arguments, got {}", args.len()),
None,
)));
}
let obj = &args[0];
let mut current = &args[1];
loop {
match current {
Value::Nil => return Ok(Value::boolean(false)),
Value::Pair(car, cdr) => {
match car.as_ref() {
Value::Pair(key, _) => {
if values_eq(obj, key) {
return Ok((**car).clone());
}
}
_ => {
return Err(Box::new(DiagnosticError::runtime_error(
"assq requires a list of pairs".to_string(),
None,
)));
}
}
current = cdr;
}
_ => {
return Err(Box::new(DiagnosticError::runtime_error(
"assq requires a proper list".to_string(),
None,
)));
}
}
}
}
fn primitive_assv(args: &[Value]) -> Result<Value> {
if args.len() != 2 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("assv expects 2 arguments, got {}", args.len()),
None,
)));
}
let obj = &args[0];
let mut current = &args[1];
loop {
match current {
Value::Nil => return Ok(Value::boolean(false)),
Value::Pair(car, cdr) => {
match car.as_ref() {
Value::Pair(key, _) => {
if values_eqv(obj, key) {
return Ok((**car).clone());
}
}
_ => {
return Err(Box::new(DiagnosticError::runtime_error(
"assv requires a list of pairs".to_string(),
None,
)));
}
}
current = cdr;
}
_ => {
return Err(Box::new(DiagnosticError::runtime_error(
"assv requires a proper list".to_string(),
None,
)));
}
}
}
}
fn primitive_sort(args: &[Value]) -> Result<Value> {
if args.len() != 2 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("sort expects 2 arguments, got {}", args.len()),
None,
)));
}
Err(Box::new(DiagnosticError::runtime_error(
"sort requires evaluator integration (not yet implemented)".to_string(),
None,
)))
}
fn is_proper_list(value: &Value) -> bool {
let mut current = value;
loop {
match current {
Value::Nil => return true,
Value::Pair(_, cdr) => {
current = cdr;
}
_ => return false,
}
}
}
fn copy_list(value: &Value) -> Result<Value> {
match value {
Value::Nil => Ok(Value::Nil),
Value::Pair(car, cdr) => {
let copied_cdr = copy_list(cdr)?;
Ok(Value::pair((**car).clone(), copied_cdr))
}
_ => Err(Box::new(DiagnosticError::runtime_error(
"copy-list requires a list".to_string(),
None,
))),
}
}
fn values_equal(a: &Value, b: &Value) -> bool {
a == b }
fn values_eq(a: &Value, b: &Value) -> bool {
a == b }
fn values_eqv(a: &Value, b: &Value) -> bool {
a == b }
fn srfi1_take(args: &[Value]) -> Result<Value> {
if args.len() != 2 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("take expects 2 arguments, got {}", args.len()),
None,
)));
}
let list_arg = &args[0];
let n = args[1].as_integer().ok_or_else(|| {
DiagnosticError::runtime_error(
"take n must be a non-negative integer".to_string(),
None,
)
})?;
if n < 0 {
return Err(Box::new(DiagnosticError::runtime_error(
"take n must be non-negative".to_string(),
None,
)));
}
let mut current = list_arg;
let mut result = Vec::new();
let mut count = 0;
while count < n {
match current {
Value::Nil => break,
Value::Pair(car, cdr) => {
result.push((**car).clone());
current = cdr;
count += 1;
}
_ => {
return Err(Box::new(DiagnosticError::runtime_error(
"take requires a proper list".to_string(),
None,
)));
}
}
}
if count < n {
return Err(Box::new(DiagnosticError::runtime_error(
"take: list too short".to_string(),
None,
)));
}
Ok(Value::list(result))
}
fn srfi1_drop(args: &[Value]) -> Result<Value> {
if args.len() != 2 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("drop expects 2 arguments, got {}", args.len()),
None,
)));
}
let mut list_arg = args[0].clone();
let n = args[1].as_integer().ok_or_else(|| {
DiagnosticError::runtime_error(
"drop n must be a non-negative integer".to_string(),
None,
)
})?;
if n < 0 {
return Err(Box::new(DiagnosticError::runtime_error(
"drop n must be non-negative".to_string(),
None,
)));
}
let mut count = 0;
while count < n {
match list_arg {
Value::Nil => {
return Err(Box::new(DiagnosticError::runtime_error(
"drop: list too short".to_string(),
None,
)));
}
Value::Pair(_, cdr) => {
list_arg = cdr.as_ref().clone();
count += 1;
}
_ => {
return Err(Box::new(DiagnosticError::runtime_error(
"drop requires a proper list".to_string(),
None,
)));
}
}
}
Ok(list_arg)
}
fn srfi1_take_right(args: &[Value]) -> Result<Value> {
if args.len() != 2 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("take-right expects 2 arguments, got {}", args.len()),
None,
)));
}
let list_arg = &args[0];
let n = args[1].as_integer().ok_or_else(|| {
DiagnosticError::runtime_error(
"take-right n must be a non-negative integer".to_string(),
None,
)
})?;
if n < 0 {
return Err(Box::new(DiagnosticError::runtime_error(
"take-right n must be non-negative".to_string(),
None,
)));
}
let list = list_arg.as_list().ok_or_else(|| {
DiagnosticError::runtime_error(
"take-right requires a proper list".to_string(),
None,
)
})?;
let list_len = list.len();
if (n as usize) > list_len {
return Err(Box::new(DiagnosticError::runtime_error(
"take-right: n larger than list length".to_string(),
None,
)));
}
let start_idx = list_len - (n as usize);
let result = list[start_idx..].to_vec();
Ok(Value::list(result))
}
fn srfi1_drop_right(args: &[Value]) -> Result<Value> {
if args.len() != 2 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("drop-right expects 2 arguments, got {}", args.len()),
None,
)));
}
let list_arg = &args[0];
let n = args[1].as_integer().ok_or_else(|| {
DiagnosticError::runtime_error(
"drop-right n must be a non-negative integer".to_string(),
None,
)
})?;
if n < 0 {
return Err(Box::new(DiagnosticError::runtime_error(
"drop-right n must be non-negative".to_string(),
None,
)));
}
let list = list_arg.as_list().ok_or_else(|| {
DiagnosticError::runtime_error(
"drop-right requires a proper list".to_string(),
None,
)
})?;
let list_len = list.len();
if (n as usize) > list_len {
return Ok(Value::Nil);
}
let end_idx = list_len - (n as usize);
let result = list[..end_idx].to_vec();
Ok(Value::list(result))
}
fn srfi1_take_while(args: &[Value]) -> Result<Value> {
if args.len() != 2 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("take-while expects 2 arguments, got {}", args.len()),
None,
)));
}
let predicate = &args[0];
let list_arg = &args[1];
if !predicate.is_procedure() {
return Err(Box::new(DiagnosticError::runtime_error(
"take-while first argument must be a procedure".to_string(),
None,
)));
}
let list = list_arg.as_list().ok_or_else(|| {
DiagnosticError::runtime_error(
"take-while requires a proper list".to_string(),
None,
)
})?;
let mut result = Vec::new();
for element in list {
let proc_args = vec![element.clone()];
let should_take = match predicate {
Value::Primitive(prim) => {
let pred_result = match &prim.implementation {
PrimitiveImpl::RustFn(func) => func(&proc_args)?,
PrimitiveImpl::Native(func) => func(&proc_args)?,
PrimitiveImpl::EvaluatorIntegrated(_) => {
return Err(Box::new(DiagnosticError::runtime_error(
"take-while with evaluator-integrated functions requires evaluator access".to_string(),
None,
)));
}
PrimitiveImpl::ForeignFn { .. } => {
return Err(Box::new(DiagnosticError::runtime_error(
"take-while with foreign functions not yet implemented".to_string(),
None,
)));
}
};
pred_result.is_truthy()
},
_ => {
return Err(Box::new(DiagnosticError::runtime_error(
"take-while with user-defined procedures requires evaluator integration".to_string(),
None,
)));
}
};
if should_take {
result.push(element);
} else {
break;
}
}
Ok(Value::list(result))
}
fn srfi1_drop_while(args: &[Value]) -> Result<Value> {
if args.len() != 2 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("drop-while expects 2 arguments, got {}", args.len()),
None,
)));
}
let predicate = &args[0];
let mut current = &args[1];
if !predicate.is_procedure() {
return Err(Box::new(DiagnosticError::runtime_error(
"drop-while first argument must be a procedure".to_string(),
None,
)));
}
loop {
match current {
Value::Nil => return Ok(Value::Nil),
Value::Pair(car, cdr) => {
let proc_args = vec![(**car).clone()];
let should_drop = match predicate {
Value::Primitive(prim) => {
let pred_result = match &prim.implementation {
PrimitiveImpl::RustFn(func) => func(&proc_args)?,
PrimitiveImpl::Native(func) => func(&proc_args)?,
PrimitiveImpl::EvaluatorIntegrated(_) => {
return Err(Box::new(DiagnosticError::runtime_error(
"drop-while with evaluator-integrated functions requires evaluator access".to_string(),
None,
)));
}
PrimitiveImpl::ForeignFn { .. } => {
return Err(Box::new(DiagnosticError::runtime_error(
"drop-while with foreign functions not yet implemented".to_string(),
None,
)));
}
};
pred_result.is_truthy()
},
_ => {
return Err(Box::new(DiagnosticError::runtime_error(
"drop-while with user-defined procedures requires evaluator integration".to_string(),
None,
)));
}
};
if should_drop {
current = cdr;
} else {
return Ok(current.clone());
}
}
_ => {
return Err(Box::new(DiagnosticError::runtime_error(
"drop-while requires a proper list".to_string(),
None,
)));
}
}
}
}
fn srfi1_split_at(args: &[Value]) -> Result<Value> {
if args.len() != 2 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("split-at expects 2 arguments, got {}", args.len()),
None,
)));
}
let list_arg = &args[0];
let n = args[1].as_integer().ok_or_else(|| {
DiagnosticError::runtime_error(
"split-at n must be a non-negative integer".to_string(),
None,
)
})?;
if n < 0 {
return Err(Box::new(DiagnosticError::runtime_error(
"split-at n must be non-negative".to_string(),
None,
)));
}
let list = list_arg.as_list().ok_or_else(|| {
DiagnosticError::runtime_error(
"split-at requires a proper list".to_string(),
None,
)
})?;
let n_usize = n as usize;
if n_usize > list.len() {
return Err(Box::new(DiagnosticError::runtime_error(
"split-at: index out of bounds".to_string(),
None,
)));
}
let (prefix, suffix) = list.split_at(n_usize);
Ok(Value::pair(
Value::list(prefix.to_vec()),
Value::list(suffix.to_vec()),
))
}
fn srfi1_last(args: &[Value]) -> Result<Value> {
if args.len() != 1 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("last expects 1 argument, got {}", args.len()),
None,
)));
}
let list = args[0].as_list().ok_or_else(|| {
DiagnosticError::runtime_error(
"last requires a proper list".to_string(),
None,
)
})?;
if list.is_empty() {
return Err(Box::new(DiagnosticError::runtime_error(
"last: empty list".to_string(),
None,
)));
}
Ok(list[list.len() - 1].clone())
}
fn srfi1_last_pair(args: &[Value]) -> Result<Value> {
if args.len() != 1 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("last-pair expects 1 argument, got {}", args.len()),
None,
)));
}
let mut current = &args[0];
loop {
match current {
Value::Nil => {
return Err(Box::new(DiagnosticError::runtime_error(
"last-pair: empty list".to_string(),
None,
)));
}
Value::Pair(_, cdr) => {
let last_pair = current.clone();
if cdr.is_nil() {
return Ok(last_pair);
}
current = cdr;
}
_ => {
return Err(Box::new(DiagnosticError::runtime_error(
"last-pair requires a list".to_string(),
None,
)));
}
}
}
}
fn srfi1_fold(args: &[Value]) -> Result<Value> {
if args.len() < 3 {
return Err(Box::new(DiagnosticError::runtime_error(
"fold requires at least 3 arguments".to_string(),
None,
)));
}
let procedure = &args[0];
let mut accumulator = args[1].clone();
let lists = &args[2..];
if !procedure.is_procedure() {
return Err(Box::new(DiagnosticError::runtime_error(
"fold first argument must be a procedure".to_string(),
None,
)));
}
let mut list_data = Vec::new();
let mut min_length = usize::MAX;
for (i, list_arg) in lists.iter().enumerate() {
let list = list_arg.as_list().ok_or_else(|| {
DiagnosticError::runtime_error(
format!("fold argument {} must be a proper list", i + 3),
None,
)
})?;
min_length = min_length.min(list.len());
list_data.push(list);
}
if min_length == 0 || min_length == usize::MAX {
return Ok(accumulator);
}
for i in 0..min_length {
let mut proc_args = vec![accumulator];
for list in &list_data {
proc_args.push(list[i].clone());
}
match procedure {
Value::Primitive(prim) => {
accumulator = match &prim.implementation {
PrimitiveImpl::RustFn(func) => func(&proc_args)?,
PrimitiveImpl::Native(func) => func(&proc_args)?,
PrimitiveImpl::EvaluatorIntegrated(_) => {
return Err(Box::new(DiagnosticError::runtime_error(
"fold with evaluator-integrated functions requires evaluator access".to_string(),
None,
)));
}
PrimitiveImpl::ForeignFn { .. } => {
return Err(Box::new(DiagnosticError::runtime_error(
"fold with foreign functions not yet implemented".to_string(),
None,
)));
}
};
},
_ => {
return Err(Box::new(DiagnosticError::runtime_error(
"fold with user-defined procedures requires evaluator integration".to_string(),
None,
)));
}
}
}
Ok(accumulator)
}
fn srfi1_fold_right(args: &[Value]) -> Result<Value> {
if args.len() < 3 {
return Err(Box::new(DiagnosticError::runtime_error(
"fold-right requires at least 3 arguments".to_string(),
None,
)));
}
let procedure = &args[0];
let mut accumulator = args[1].clone();
let lists = &args[2..];
if !procedure.is_procedure() {
return Err(Box::new(DiagnosticError::runtime_error(
"fold-right first argument must be a procedure".to_string(),
None,
)));
}
let mut list_data = Vec::new();
let mut min_length = usize::MAX;
for (i, list_arg) in lists.iter().enumerate() {
let list = list_arg.as_list().ok_or_else(|| {
DiagnosticError::runtime_error(
format!("fold-right argument {} must be a proper list", i + 3),
None,
)
})?;
min_length = min_length.min(list.len());
list_data.push(list);
}
if min_length == 0 || min_length == usize::MAX {
return Ok(accumulator);
}
for i in (0..min_length).rev() {
let mut proc_args = Vec::new();
for list in &list_data {
proc_args.push(list[i].clone());
}
proc_args.push(accumulator);
match procedure {
Value::Primitive(prim) => {
accumulator = match &prim.implementation {
PrimitiveImpl::RustFn(func) => func(&proc_args)?,
PrimitiveImpl::Native(func) => func(&proc_args)?,
PrimitiveImpl::EvaluatorIntegrated(_) => {
return Err(Box::new(DiagnosticError::runtime_error(
"fold-right with evaluator-integrated functions requires evaluator access".to_string(),
None,
)));
}
PrimitiveImpl::ForeignFn { .. } => {
return Err(Box::new(DiagnosticError::runtime_error(
"fold-right with foreign functions not yet implemented".to_string(),
None,
)));
}
};
},
_ => {
return Err(Box::new(DiagnosticError::runtime_error(
"fold-right with user-defined procedures requires evaluator integration".to_string(),
None,
)));
}
}
}
Ok(accumulator)
}
fn srfi1_reduce(args: &[Value]) -> Result<Value> {
if args.len() != 3 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("reduce expects 3 arguments, got {}", args.len()),
None,
)));
}
let procedure = &args[0];
let ridentity = &args[1];
let list_arg = &args[2];
if !procedure.is_procedure() {
return Err(Box::new(DiagnosticError::runtime_error(
"reduce first argument must be a procedure".to_string(),
None,
)));
}
let list = list_arg.as_list().ok_or_else(|| {
DiagnosticError::runtime_error(
"reduce requires a proper list".to_string(),
None,
)
})?;
if list.is_empty() {
return Ok(ridentity.clone());
}
if list.len() == 1 {
return Ok(list[0].clone());
}
let mut accumulator = list[0].clone();
for element in &list[1..] {
let proc_args = vec![accumulator, element.clone()];
match procedure {
Value::Primitive(prim) => {
accumulator = match &prim.implementation {
PrimitiveImpl::RustFn(func) => func(&proc_args)?,
PrimitiveImpl::Native(func) => func(&proc_args)?,
PrimitiveImpl::EvaluatorIntegrated(_) => {
return Err(Box::new(DiagnosticError::runtime_error(
"reduce with evaluator-integrated functions requires evaluator access".to_string(),
None,
)));
}
PrimitiveImpl::ForeignFn { .. } => {
return Err(Box::new(DiagnosticError::runtime_error(
"reduce with foreign functions not yet implemented".to_string(),
None,
)));
}
};
},
_ => {
return Err(Box::new(DiagnosticError::runtime_error(
"reduce with user-defined procedures requires evaluator integration".to_string(),
None,
)));
}
}
}
Ok(accumulator)
}
fn srfi1_reduce_right(args: &[Value]) -> Result<Value> {
if args.len() != 3 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("reduce-right expects 3 arguments, got {}", args.len()),
None,
)));
}
let procedure = &args[0];
let ridentity = &args[1];
let list_arg = &args[2];
if !procedure.is_procedure() {
return Err(Box::new(DiagnosticError::runtime_error(
"reduce-right first argument must be a procedure".to_string(),
None,
)));
}
let list = list_arg.as_list().ok_or_else(|| {
DiagnosticError::runtime_error(
"reduce-right requires a proper list".to_string(),
None,
)
})?;
if list.is_empty() {
return Ok(ridentity.clone());
}
if list.len() == 1 {
return Ok(list[0].clone());
}
let mut accumulator = list[list.len() - 1].clone();
for element in list[..list.len() - 1].iter().rev() {
let proc_args = vec![element.clone(), accumulator];
match procedure {
Value::Primitive(prim) => {
accumulator = match &prim.implementation {
PrimitiveImpl::RustFn(func) => func(&proc_args)?,
PrimitiveImpl::Native(func) => func(&proc_args)?,
PrimitiveImpl::EvaluatorIntegrated(_) => {
return Err(Box::new(DiagnosticError::runtime_error(
"reduce-right with evaluator-integrated functions requires evaluator access".to_string(),
None,
)));
}
PrimitiveImpl::ForeignFn { .. } => {
return Err(Box::new(DiagnosticError::runtime_error(
"reduce-right with foreign functions not yet implemented".to_string(),
None,
)));
}
};
},
_ => {
return Err(Box::new(DiagnosticError::runtime_error(
"reduce-right with user-defined procedures requires evaluator integration".to_string(),
None,
)));
}
}
}
Ok(accumulator)
}
fn srfi1_unfold(args: &[Value]) -> Result<Value> {
if args.len() < 4 || args.len() > 5 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("unfold expects 4 or 5 arguments, got {}", args.len()),
None,
)));
}
Err(Box::new(DiagnosticError::runtime_error(
"unfold requires evaluator integration (not yet implemented)".to_string(),
None,
)))
}
fn srfi1_unfold_right(args: &[Value]) -> Result<Value> {
if args.len() < 4 || args.len() > 5 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("unfold-right expects 4 or 5 arguments, got {}", args.len()),
None,
)));
}
Err(Box::new(DiagnosticError::runtime_error(
"unfold-right requires evaluator integration (not yet implemented)".to_string(),
None,
)))
}
fn srfi1_alist_cons(args: &[Value]) -> Result<Value> {
if args.len() != 3 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("alist-cons expects 3 arguments, got {}", args.len()),
None,
)));
}
let key = &args[0];
let datum = &args[1];
let alist = &args[2];
let pair = Value::pair(key.clone(), datum.clone());
Ok(Value::pair(pair, alist.clone()))
}
fn srfi1_alist_copy(args: &[Value]) -> Result<Value> {
if args.len() != 1 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("alist-copy expects 1 argument, got {}", args.len()),
None,
)));
}
copy_alist(&args[0])
}
fn srfi1_alist_delete(args: &[Value]) -> Result<Value> {
if args.len() < 2 || args.len() > 3 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("alist-delete expects 2 or 3 arguments, got {}", args.len()),
None,
)));
}
let key = &args[0];
let alist = &args[1];
let mut result = Vec::new();
let mut current = alist;
loop {
match current {
Value::Nil => break,
Value::Pair(car, cdr) => {
match car.as_ref() {
Value::Pair(entry_key, entry_value) => {
if !values_equal(key, entry_key) {
let entry = Value::pair((**entry_key).clone(), (**entry_value).clone());
result.push(entry);
}
}
_ => {
return Err(Box::new(DiagnosticError::runtime_error(
"alist-delete requires a list of pairs".to_string(),
None,
)));
}
}
current = cdr;
}
_ => {
return Err(Box::new(DiagnosticError::runtime_error(
"alist-delete requires a proper list".to_string(),
None,
)));
}
}
}
Ok(Value::list(result))
}
fn srfi1_list_equal(args: &[Value]) -> Result<Value> {
if args.is_empty() {
return Err(Box::new(DiagnosticError::runtime_error(
"list= requires at least 1 argument".to_string(),
None,
)));
}
if args.len() == 1 {
return Ok(Value::boolean(true));
}
let (eq_pred, lists) = if args[0].is_procedure() {
(&args[0], &args[1..])
} else {
return Ok(Value::boolean(lists_equal_default(&args[0], &args[1..])));
};
if lists.len() < 2 {
return Ok(Value::boolean(true));
}
let mut list_data = Vec::new();
for (i, list_arg) in lists.iter().enumerate() {
let list = list_arg.as_list().ok_or_else(|| {
DiagnosticError::runtime_error(
format!("list= argument {} must be a proper list", i + 2),
None,
)
})?;
list_data.push(list);
}
let first_len = list_data[0].len();
for list in &list_data[1..] {
if list.len() != first_len {
return Ok(Value::boolean(false));
}
}
for i in 0..first_len {
for j in 1..list_data.len() {
let proc_args = vec![list_data[0][i].clone(), list_data[j][i].clone()];
let are_equal = match eq_pred {
Value::Primitive(prim) => {
let result = match &prim.implementation {
PrimitiveImpl::RustFn(func) => func(&proc_args)?,
PrimitiveImpl::Native(func) => func(&proc_args)?,
PrimitiveImpl::EvaluatorIntegrated(_) => {
return Err(Box::new(DiagnosticError::runtime_error(
"list= with evaluator-integrated functions requires evaluator access".to_string(),
None,
)));
}
PrimitiveImpl::ForeignFn { .. } => {
return Err(Box::new(DiagnosticError::runtime_error(
"list= with foreign functions not yet implemented".to_string(),
None,
)));
}
};
result.is_truthy()
},
_ => {
return Err(Box::new(DiagnosticError::runtime_error(
"list= with user-defined procedures requires evaluator integration".to_string(),
None,
)));
}
};
if !are_equal {
return Ok(Value::boolean(false));
}
}
}
Ok(Value::boolean(true))
}
fn is_circular_list(value: &Value) -> bool {
let mut slow = value;
let mut fast = value;
loop {
match fast {
Value::Pair(_, cdr1) => {
match cdr1.as_ref() {
Value::Pair(_, cdr2) => {
fast = cdr2;
}
Value::Nil => return false,
_ => return false, }
}
Value::Nil => return false,
_ => return false,
}
match slow {
Value::Pair(_, cdr) => {
slow = cdr;
}
Value::Nil => return false,
_ => return false,
}
if std::ptr::eq(slow, fast) {
return true;
}
}
}
fn is_dotted_list(value: &Value) -> bool {
let mut current = value;
loop {
match current {
Value::Nil => return false, Value::Pair(_, cdr) => {
current = cdr;
}
_ => return true, }
}
}
fn copy_alist(alist: &Value) -> Result<Value> {
let mut result = Vec::new();
let mut current = alist;
loop {
match current {
Value::Nil => break,
Value::Pair(car, cdr) => {
match car.as_ref() {
Value::Pair(key, value) => {
let copied_pair = Value::pair((**key).clone(), (**value).clone());
result.push(copied_pair);
}
_ => {
return Err(Box::new(DiagnosticError::runtime_error(
"alist-copy requires a list of pairs".to_string(),
None,
)));
}
}
current = cdr;
}
_ => {
return Err(Box::new(DiagnosticError::runtime_error(
"alist-copy requires a proper list".to_string(),
None,
)));
}
}
}
Ok(Value::list(result))
}
fn lists_equal_default(first: &Value, rest: &[Value]) -> bool {
let first_list = match first.as_list() {
Some(list) => list,
None => return false,
};
for other in rest {
let other_list = match other.as_list() {
Some(list) => list,
None => return false,
};
if first_list.len() != other_list.len() {
return false;
}
for (a, b) in first_list.iter().zip(other_list.iter()) {
if !values_equal(a, b) {
return false;
}
}
}
true
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cons_car_cdr() {
let args = vec![Value::integer(1), Value::integer(2)];
let pair = primitive_cons(&args).unwrap();
let car_result = primitive_car(&[pair.clone()]).unwrap();
assert_eq!(car_result, Value::integer(1));
let cdr_result = primitive_cdr(&[pair]).unwrap();
assert_eq!(cdr_result, Value::integer(2));
}
#[test]
fn test_list_construction() {
let args = vec![Value::integer(1), Value::integer(2), Value::integer(3)];
let list = primitive_list(&args).unwrap();
assert!(is_proper_list(&list));
let length = primitive_length(&[list]).unwrap();
assert_eq!(length, Value::integer(3));
}
#[test]
fn test_list_predicates() {
let pair = Value::pair(Value::integer(1), Value::integer(2));
assert_eq!(primitive_pair_p(&[pair]).unwrap(), Value::boolean(true));
let nil = Value::Nil;
assert_eq!(primitive_null_p(&[nil]).unwrap(), Value::boolean(true));
let proper_list = Value::list(vec![Value::integer(1), Value::integer(2)]);
assert_eq!(primitive_list_p(&[proper_list]).unwrap(), Value::boolean(true));
}
#[test]
fn test_list_ref() {
let list = Value::list(vec![
Value::string("a"),
Value::string("b"),
Value::string("c"),
]);
let args = vec![list, Value::integer(1)];
let result = primitive_list_ref(&args).unwrap();
assert_eq!(result, Value::string("b"));
}
#[test]
fn test_append() {
let list1 = Value::list(vec![Value::integer(1), Value::integer(2)]);
let list2 = Value::list(vec![Value::integer(3), Value::integer(4)]);
let args = vec![list1, list2];
let result = primitive_append(&args).unwrap();
let expected = Value::list(vec![
Value::integer(1),
Value::integer(2),
Value::integer(3),
Value::integer(4),
]);
assert_eq!(result, expected);
}
#[test]
fn test_reverse() {
let list = Value::list(vec![
Value::integer(1),
Value::integer(2),
Value::integer(3),
]);
let result = primitive_reverse(&[list]).unwrap();
let expected = Value::list(vec![
Value::integer(3),
Value::integer(2),
Value::integer(1),
]);
assert_eq!(result, expected);
}
#[test]
fn test_member() {
let list = Value::list(vec![
Value::string("a"),
Value::string("b"),
Value::string("c"),
]);
let args = vec![Value::string("b"), list];
let result = primitive_member(&args).unwrap();
assert!(result.is_pair());
}
#[test]
fn test_map_single_list() {
let double_proc = Arc::new(PrimitiveProcedure {
name: "double".to_string(),
arity_min: 1,
arity_max: Some(1),
implementation: PrimitiveImpl::RustFn(|args| {
if let Some(n) = args[0].as_number() {
Ok(Value::number(n * 2.0))
} else {
Ok(Value::Unspecified)
}
}),
effects: vec![Effect::Pure],
});
let list = Value::list(vec![Value::number(1.0), Value::number(2.0), Value::number(3.0)]);
let args = vec![Value::Primitive(double_proc), list];
let result = primitive_map(&args).unwrap();
let expected = Value::list(vec![Value::number(2.0), Value::number(4.0), Value::number(6.0)]);
assert_eq!(result, expected);
}
#[test]
fn test_map_multiple_lists() {
let add_proc = Arc::new(PrimitiveProcedure {
name: "+".to_string(),
arity_min: 0,
arity_max: None,
implementation: PrimitiveImpl::RustFn(|args| {
let sum = args.iter()
.filter_map(|v| v.as_number())
.fold(0.0, |acc, n| acc + n);
Ok(Value::number(sum))
}),
effects: vec![Effect::Pure],
});
let list1 = Value::list(vec![Value::number(1.0), Value::number(2.0), Value::number(3.0)]);
let list2 = Value::list(vec![Value::number(4.0), Value::number(5.0), Value::number(6.0)]);
let args = vec![Value::Primitive(add_proc), list1, list2];
let result = primitive_map(&args).unwrap();
let expected = Value::list(vec![Value::number(5.0), Value::number(7.0), Value::number(9.0)]);
assert_eq!(result, expected);
}
#[test]
fn test_map_different_lengths() {
let add_proc = Arc::new(PrimitiveProcedure {
name: "+".to_string(),
arity_min: 0,
arity_max: None,
implementation: PrimitiveImpl::RustFn(|args| {
let sum = args.iter()
.filter_map(|v| v.as_number())
.fold(0.0, |acc, n| acc + n);
Ok(Value::number(sum))
}),
effects: vec![Effect::Pure],
});
let list1 = Value::list(vec![Value::number(1.0), Value::number(2.0)]);
let list2 = Value::list(vec![Value::number(4.0), Value::number(5.0), Value::number(6.0)]);
let args = vec![Value::Primitive(add_proc), list1, list2];
let result = primitive_map(&args).unwrap();
let expected = Value::list(vec![Value::number(5.0), Value::number(7.0)]);
assert_eq!(result, expected);
}
#[test]
fn test_map_empty_list() {
let double_proc = Arc::new(PrimitiveProcedure {
name: "double".to_string(),
arity_min: 1,
arity_max: Some(1),
implementation: PrimitiveImpl::RustFn(|args| {
if let Some(n) = args[0].as_number() {
Ok(Value::number(n * 2.0))
} else {
Ok(Value::Unspecified)
}
}),
effects: vec![Effect::Pure],
});
let empty_list = Value::Nil;
let args = vec![Value::Primitive(double_proc), empty_list];
let result = primitive_map(&args).unwrap();
assert_eq!(result, Value::Nil);
}
#[test]
fn test_for_each_basic() {
let identity_proc = Arc::new(PrimitiveProcedure {
name: "identity".to_string(),
arity_min: 1,
arity_max: Some(1),
implementation: PrimitiveImpl::RustFn(|args| Ok(args[0].clone())),
effects: vec![Effect::Pure],
});
let list = Value::list(vec![Value::number(1.0), Value::number(2.0), Value::number(3.0)]);
let args = vec![Value::Primitive(identity_proc), list];
let result = primitive_for_each(&args).unwrap();
assert_eq!(result, Value::Unspecified);
}
#[test]
fn test_for_each_multiple_lists() {
let add_proc = Arc::new(PrimitiveProcedure {
name: "+".to_string(),
arity_min: 0,
arity_max: None,
implementation: PrimitiveImpl::RustFn(|args| {
let sum = args.iter()
.filter_map(|v| v.as_number())
.fold(0.0, |acc, n| acc + n);
Ok(Value::number(sum))
}),
effects: vec![Effect::Pure],
});
let list1 = Value::list(vec![Value::number(1.0), Value::number(2.0)]);
let list2 = Value::list(vec![Value::number(4.0), Value::number(5.0)]);
let args = vec![Value::Primitive(add_proc), list1, list2];
let result = primitive_for_each(&args).unwrap();
assert_eq!(result, Value::Unspecified);
}
#[test]
fn test_map_for_each_errors() {
let args = vec![Value::integer(42), Value::list(vec![Value::integer(1)])];
assert!(primitive_map(&args).is_err());
assert!(primitive_for_each(&args).is_err());
let proc = Arc::new(PrimitiveProcedure {
name: "test".to_string(),
arity_min: 1,
arity_max: Some(1),
implementation: PrimitiveImpl::RustFn(|args| Ok(args[0].clone())),
effects: vec![Effect::Pure],
});
let args = vec![Value::Primitive(proc.clone()), Value::integer(42)];
assert!(primitive_map(&args).is_err());
assert!(primitive_for_each(&args).is_err());
assert!(primitive_map(&[]).is_err());
assert!(primitive_for_each(&[]).is_err());
let args = vec![Value::Primitive(proc)];
assert!(primitive_map(&args).is_err());
assert!(primitive_for_each(&args).is_err());
}
#[test]
fn test_filter_basic() {
let even_pred = Arc::new(PrimitiveProcedure {
name: "even?".to_string(),
arity_min: 1,
arity_max: Some(1),
implementation: PrimitiveImpl::RustFn(|args| {
if let Some(n) = args[0].as_integer() {
Ok(Value::boolean(n % 2 == 0))
} else {
Ok(Value::boolean(false))
}
}),
effects: vec![Effect::Pure],
});
let list = Value::list(vec![
Value::integer(1), Value::integer(2), Value::integer(3),
Value::integer(4), Value::integer(5), Value::integer(6)
]);
let args = vec![Value::Primitive(even_pred), list];
let result = primitive_filter(&args).unwrap();
let expected = Value::list(vec![Value::integer(2), Value::integer(4), Value::integer(6)]);
assert_eq!(result, expected);
}
#[test]
fn test_filter_empty_result() {
let false_pred = Arc::new(PrimitiveProcedure {
name: "false".to_string(),
arity_min: 1,
arity_max: Some(1),
implementation: PrimitiveImpl::RustFn(|_args| Ok(Value::boolean(false))),
effects: vec![Effect::Pure],
});
let list = Value::list(vec![Value::integer(1), Value::integer(2), Value::integer(3)]);
let args = vec![Value::Primitive(false_pred), list];
let result = primitive_filter(&args).unwrap();
assert_eq!(result, Value::Nil);
}
#[test]
fn test_filter_all_kept() {
let true_pred = Arc::new(PrimitiveProcedure {
name: "true".to_string(),
arity_min: 1,
arity_max: Some(1),
implementation: PrimitiveImpl::RustFn(|_args| Ok(Value::boolean(true))),
effects: vec![Effect::Pure],
});
let list = Value::list(vec![Value::integer(1), Value::integer(2), Value::integer(3)]);
let args = vec![Value::Primitive(true_pred), list.clone()];
let result = primitive_filter(&args).unwrap();
assert_eq!(result, list);
}
#[test]
fn test_fold_left_basic() {
let add_proc = Arc::new(PrimitiveProcedure {
name: "+".to_string(),
arity_min: 0,
arity_max: None,
implementation: PrimitiveImpl::RustFn(|args| {
let sum = args.iter()
.filter_map(|v| v.as_number())
.fold(0.0, |acc, n| acc + n);
Ok(Value::number(sum))
}),
effects: vec![Effect::Pure],
});
let list = Value::list(vec![Value::number(1.0), Value::number(2.0), Value::number(3.0)]);
let args = vec![Value::Primitive(add_proc), Value::number(0.0), list];
let result = primitive_fold_left(&args).unwrap();
assert_eq!(result, Value::number(6.0));
}
#[test]
fn test_fold_left_with_strings() {
let concat_proc = Arc::new(PrimitiveProcedure {
name: "string-append".to_string(),
arity_min: 0,
arity_max: None,
implementation: PrimitiveImpl::RustFn(|args| {
let mut result = String::new();
for arg in args {
if let Some(s) = arg.as_string() {
result.push_str(s);
}
}
Ok(Value::string(result))
}),
effects: vec![Effect::Pure],
});
let list = Value::list(vec![Value::string("a"), Value::string("b"), Value::string("c")]);
let args = vec![Value::Primitive(concat_proc), Value::string(""), list];
let result = primitive_fold_left(&args).unwrap();
assert_eq!(result, Value::string("abc"));
}
#[test]
fn test_fold_right_basic() {
let sub_proc = Arc::new(PrimitiveProcedure {
name: "-".to_string(),
arity_min: 1,
arity_max: None,
implementation: PrimitiveImpl::RustFn(|args| {
if args.is_empty() {
return Ok(Value::number(0.0));
}
let first = args[0].as_number().unwrap_or(0.0);
if args.len() == 1 {
return Ok(Value::number(-first));
}
let result = args[1..].iter()
.filter_map(|v| v.as_number())
.fold(first, |acc, n| acc - n);
Ok(Value::number(result))
}),
effects: vec![Effect::Pure],
});
let list = Value::list(vec![Value::number(1.0), Value::number(2.0), Value::number(3.0)]);
let args = vec![Value::Primitive(sub_proc), Value::number(0.0), list];
let result = primitive_fold_right(&args).unwrap();
assert_eq!(result, Value::number(2.0));
}
#[test]
fn test_fold_empty_list() {
let add_proc = Arc::new(PrimitiveProcedure {
name: "+".to_string(),
arity_min: 0,
arity_max: None,
implementation: PrimitiveImpl::RustFn(|args| {
let sum = args.iter()
.filter_map(|v| v.as_number())
.fold(0.0, |acc, n| acc + n);
Ok(Value::number(sum))
}),
effects: vec![Effect::Pure],
});
let empty_list = Value::Nil;
let initial = Value::number(42.0);
let args = vec![Value::Primitive(add_proc.clone()), initial.clone(), empty_list.clone()];
let result_left = primitive_fold_left(&args).unwrap();
assert_eq!(result_left, initial);
let args = vec![Value::Primitive(add_proc), initial.clone(), empty_list];
let result_right = primitive_fold_right(&args).unwrap();
assert_eq!(result_right, initial);
}
#[test]
fn test_higher_order_errors() {
let args = vec![Value::integer(42), Value::list(vec![Value::integer(1)])];
assert!(primitive_filter(&args).is_err());
let args = vec![Value::integer(42), Value::integer(0), Value::list(vec![Value::integer(1)])];
assert!(primitive_fold_left(&args).is_err());
assert!(primitive_fold_right(&args).is_err());
let proc = Arc::new(PrimitiveProcedure {
name: "test".to_string(),
arity_min: 1,
arity_max: Some(1),
implementation: PrimitiveImpl::RustFn(|args| Ok(args[0].clone())),
effects: vec![Effect::Pure],
});
let args = vec![Value::Primitive(proc.clone()), Value::integer(42)];
assert!(primitive_filter(&args).is_err());
let args = vec![Value::Primitive(proc.clone()), Value::integer(0), Value::integer(42)];
assert!(primitive_fold_left(&args).is_err());
assert!(primitive_fold_right(&args).is_err());
assert!(primitive_filter(&[]).is_err());
assert!(primitive_fold_left(&[]).is_err());
assert!(primitive_fold_right(&[]).is_err());
let args = vec![Value::Primitive(proc.clone())];
assert!(primitive_filter(&args).is_err());
let args = vec![Value::Primitive(proc.clone()), Value::integer(0)];
assert!(primitive_fold_left(&args).is_err());
assert!(primitive_fold_right(&args).is_err());
}
#[test]
fn test_srfi1_take_drop() {
let list = Value::list(vec![Value::integer(1), Value::integer(2), Value::integer(3), Value::integer(4), Value::integer(5)]);
let args = vec![list.clone(), Value::integer(3)];
let result = srfi1_take(&args).unwrap();
let expected = Value::list(vec![Value::integer(1), Value::integer(2), Value::integer(3)]);
assert_eq!(result, expected);
let args = vec![list.clone(), Value::integer(2)];
let result = srfi1_drop(&args).unwrap();
let expected = Value::list(vec![Value::integer(3), Value::integer(4), Value::integer(5)]);
assert_eq!(result, expected);
let args = vec![list.clone(), Value::integer(2)];
let result = srfi1_take_right(&args).unwrap();
let expected = Value::list(vec![Value::integer(4), Value::integer(5)]);
assert_eq!(result, expected);
let args = vec![list, Value::integer(2)];
let result = srfi1_drop_right(&args).unwrap();
let expected = Value::list(vec![Value::integer(1), Value::integer(2), Value::integer(3)]);
assert_eq!(result, expected);
}
#[test]
fn test_srfi1_split_at() {
let list = Value::list(vec![Value::integer(1), Value::integer(2), Value::integer(3), Value::integer(4)]);
let args = vec![list, Value::integer(2)];
let result = srfi1_split_at(&args).unwrap();
match result {
Value::Pair(prefix, suffix) => {
let expected_prefix = Value::list(vec![Value::integer(1), Value::integer(2)]);
let expected_suffix = Value::list(vec![Value::integer(3), Value::integer(4)]);
assert_eq!(prefix.as_ref().clone(), expected_prefix);
assert_eq!(suffix.as_ref().clone(), expected_suffix);
},
_ => panic!("split-at should return a pair"),
}
}
#[test]
fn test_srfi1_last_last_pair() {
let list = Value::list(vec![Value::integer(1), Value::integer(2), Value::integer(3)]);
let result = srfi1_last(&[list.clone()]).unwrap();
assert_eq!(result, Value::integer(3));
let result = srfi1_last_pair(&[list]).unwrap();
match result {
Value::Pair(car, cdr) => {
assert_eq!(car.as_ref().clone(), Value::integer(3));
assert_eq!(cdr.as_ref().clone(), Value::Nil);
},
_ => panic!("last-pair should return a pair"),
}
}
#[test]
fn test_srfi1_fold_reduce() {
let add_proc = Arc::new(PrimitiveProcedure {
name: "+".to_string(),
arity_min: 0,
arity_max: None,
implementation: PrimitiveImpl::RustFn(|args| {
let sum = args.iter()
.filter_map(|v| v.as_number())
.fold(0.0, |acc, n| acc + n);
Ok(Value::number(sum))
}),
effects: vec![Effect::Pure],
});
let list = Value::list(vec![Value::number(1.0), Value::number(2.0), Value::number(3.0)]);
let args = vec![Value::Primitive(add_proc.clone()), Value::number(0.0), list.clone()];
let result = srfi1_fold(&args).unwrap();
assert_eq!(result, Value::number(6.0));
let args = vec![Value::Primitive(add_proc), Value::number(0.0), list];
let result = srfi1_reduce(&args).unwrap();
assert_eq!(result, Value::number(6.0));
}
#[test]
fn test_srfi1_alist_operations() {
let alist = Value::list(vec![
Value::pair(Value::string("a"), Value::integer(1)),
Value::pair(Value::string("b"), Value::integer(2)),
Value::pair(Value::string("c"), Value::integer(3)),
]);
let args = vec![Value::string("d"), Value::integer(4), alist.clone()];
let result = srfi1_alist_cons(&args).unwrap();
match result {
Value::Pair(car, cdr) => {
match car.as_ref() {
Value::Pair(key, value) => {
assert_eq!(key.as_ref().clone(), Value::string("d"));
assert_eq!(value.as_ref().clone(), Value::integer(4));
},
_ => panic!("Expected pair in alist-cons result"),
}
assert_eq!(cdr.as_ref().clone(), alist);
},
_ => panic!("alist-cons should return a pair"),
}
let result = srfi1_alist_copy(&[alist.clone()]).unwrap();
if let (Some(orig_list), Some(copied_list)) = (alist.as_list(), result.as_list()) {
assert_eq!(orig_list.len(), copied_list.len());
for (orig, copied) in orig_list.iter().zip(copied_list.iter()) {
assert_eq!(orig, copied);
}
} else {
panic!("Both should be proper lists");
}
let args = vec![Value::string("b"), alist];
let result = srfi1_alist_delete(&args).unwrap();
let expected = Value::list(vec![
Value::pair(Value::string("a"), Value::integer(1)),
Value::pair(Value::string("c"), Value::integer(3)),
]);
assert_eq!(result, expected);
}
}
fn apply_procedure_with_evaluator(
evaluator: &mut crate::eval::evaluator::Evaluator,
procedure: &Value,
args: &[Value],
) -> crate::diagnostics::Result<Value> {
use crate::eval::evaluator::EvalStep;
use crate::diagnostics::Error;
let mut step = evaluator.apply_procedure(procedure.clone(), args.to_vec(), None);
loop {
step = match step {
EvalStep::Return(value) => return Ok(value),
EvalStep::Error(error) => return Err(Box::new(error)),
EvalStep::Continue { expr, env } => {
evaluator.eval_step(&expr, env)
}
EvalStep::TailCall { procedure: proc, args: tail_args, location } => {
evaluator.apply_procedure(proc, tail_args, location)
}
EvalStep::CallContinuation { continuation, value } => {
evaluator.call_continuation(continuation, value)
}
EvalStep::NonLocalJump { value, target_stack_depth: _ } => {
return Ok(value);
}
};
}
}
fn evaluator_map(evaluator: &mut crate::eval::evaluator::Evaluator, args: &[Value]) -> crate::diagnostics::Result<Value> {
use crate::diagnostics::Error as DiagnosticError;
if args.len() < 2 {
return Err(Box::new(DiagnosticError::runtime_error(
"map requires at least 2 arguments".to_string(),
None,
)));
}
let procedure = &args[0];
if !procedure.is_procedure() {
return Err(Box::new(DiagnosticError::runtime_error(
"map first argument must be a procedure".to_string(),
None,
)));
}
let mut list_data = Vec::new();
let mut min_length = usize::MAX;
for (i, arg) in args.iter().enumerate().skip(1) {
if let Some(list_values) = arg.as_list() {
let length = list_values.len();
if length < min_length {
min_length = length;
}
list_data.push(list_values);
} else {
return Err(Box::new(DiagnosticError::runtime_error(
format!("map argument {} must be a list", i + 1),
None,
)));
}
}
if min_length == 0 || min_length == usize::MAX {
return Ok(Value::Nil);
}
let mut results = Vec::new();
for i in 0..min_length {
let mut proc_args = Vec::new();
for list in &list_data {
proc_args.push(list[i].clone());
}
let result = apply_procedure_with_evaluator(evaluator, procedure, &proc_args)?;
results.push(result);
}
Ok(Value::list(results))
}
fn evaluator_filter(evaluator: &mut crate::eval::evaluator::Evaluator, args: &[Value]) -> crate::diagnostics::Result<Value> {
use crate::diagnostics::Error as DiagnosticError;
if args.len() != 2 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("filter expects 2 arguments, got {}", args.len()),
None,
)));
}
let predicate = &args[0];
let list_arg = &args[1];
if !predicate.is_procedure() {
return Err(Box::new(DiagnosticError::runtime_error(
"filter first argument must be a procedure".to_string(),
None,
)));
}
let list_values = list_arg.as_list().ok_or_else(|| {
Box::new(DiagnosticError::runtime_error(
"filter requires a proper list".to_string(),
None,
))
})?;
let mut filtered = Vec::new();
for element in list_values {
let proc_args = vec![element.clone()];
let result = apply_procedure_with_evaluator(evaluator, predicate, &proc_args)?;
if result.is_truthy() {
filtered.push(element);
}
}
Ok(Value::list(filtered))
}
fn evaluator_fold_left(evaluator: &mut crate::eval::evaluator::Evaluator, args: &[Value]) -> crate::diagnostics::Result<Value> {
use crate::diagnostics::Error as DiagnosticError;
if args.len() < 3 {
return Err(Box::new(DiagnosticError::runtime_error(
"fold-left requires at least 3 arguments".to_string(),
None,
)));
}
let procedure = &args[0];
let mut accumulator = args[1].clone();
if !procedure.is_procedure() {
return Err(Box::new(DiagnosticError::runtime_error(
"fold-left first argument must be a procedure".to_string(),
None,
)));
}
let mut list_data = Vec::new();
let mut min_length = usize::MAX;
for (i, arg) in args.iter().enumerate().skip(2) {
if let Some(list_values) = arg.as_list() {
let length = list_values.len();
if length < min_length {
min_length = length;
}
list_data.push(list_values);
} else {
return Err(Box::new(DiagnosticError::runtime_error(
format!("fold-left argument {} must be a proper list", i + 1),
None,
)));
}
}
if min_length == 0 || min_length == usize::MAX {
return Ok(accumulator);
}
for i in 0..min_length {
let mut proc_args = vec![accumulator];
for list in &list_data {
proc_args.push(list[i].clone());
}
accumulator = apply_procedure_with_evaluator(evaluator, procedure, &proc_args)?;
}
Ok(accumulator)
}
fn evaluator_fold_right(evaluator: &mut crate::eval::evaluator::Evaluator, args: &[Value]) -> crate::diagnostics::Result<Value> {
use crate::diagnostics::Error as DiagnosticError;
if args.len() < 3 {
return Err(Box::new(DiagnosticError::runtime_error(
"fold-right requires at least 3 arguments".to_string(),
None,
)));
}
let procedure = &args[0];
let mut accumulator = args[1].clone();
if !procedure.is_procedure() {
return Err(Box::new(DiagnosticError::runtime_error(
"fold-right first argument must be a procedure".to_string(),
None,
)));
}
let mut list_data = Vec::new();
let mut min_length = usize::MAX;
for (i, arg) in args.iter().enumerate().skip(2) {
if let Some(list_values) = arg.as_list() {
let length = list_values.len();
if length < min_length {
min_length = length;
}
list_data.push(list_values);
} else {
return Err(Box::new(DiagnosticError::runtime_error(
format!("fold-right argument {} must be a proper list", i + 1),
None,
)));
}
}
if min_length == 0 || min_length == usize::MAX {
return Ok(accumulator);
}
for i in (0..min_length).rev() {
let mut proc_args = Vec::new();
for list in &list_data {
proc_args.push(list[i].clone());
}
proc_args.push(accumulator);
accumulator = apply_procedure_with_evaluator(evaluator, procedure, &proc_args)?;
}
Ok(accumulator)
}
fn evaluator_for_each(evaluator: &mut crate::eval::evaluator::Evaluator, args: &[Value]) -> crate::diagnostics::Result<Value> {
use crate::diagnostics::Error as DiagnosticError;
if args.len() < 2 {
return Err(Box::new(DiagnosticError::runtime_error(
"for-each requires at least 2 arguments".to_string(),
None,
)));
}
let procedure = &args[0];
if !procedure.is_procedure() {
return Err(Box::new(DiagnosticError::runtime_error(
"for-each first argument must be a procedure".to_string(),
None,
)));
}
let mut list_data = Vec::new();
let mut min_length = usize::MAX;
for (i, arg) in args.iter().enumerate().skip(1) {
if let Some(list_values) = arg.as_list() {
let length = list_values.len();
if length < min_length {
min_length = length;
}
list_data.push(list_values);
} else {
return Err(Box::new(DiagnosticError::runtime_error(
format!("for-each argument {} must be a list", i + 1),
None,
)));
}
}
if min_length == 0 || min_length == usize::MAX {
return Ok(Value::Unspecified);
}
for i in 0..min_length {
let mut proc_args = Vec::new();
for list in &list_data {
proc_args.push(list[i].clone());
}
let _result = apply_procedure_with_evaluator(evaluator, procedure, &proc_args)?;
}
Ok(Value::Unspecified)
}
#[cfg(test)]
mod mutable_pair_tests {
use super::*;
#[test]
fn test_mutable_pair_creation() {
let pair = Value::mutable_pair(Value::integer(1), Value::integer(2));
assert!(pair.is_pair());
assert_eq!(primitive_pair_p(&[pair]).unwrap(), Value::boolean(true));
}
#[test]
fn test_mutable_pair_car_cdr() {
let pair = Value::mutable_pair(Value::integer(42), Value::string("hello"));
let car_result = primitive_car(&[pair.clone()]).unwrap();
assert_eq!(car_result, Value::integer(42));
let cdr_result = primitive_cdr(&[pair]).unwrap();
assert_eq!(cdr_result, Value::string("hello"));
}
#[test]
fn test_set_car() {
let pair = Value::mutable_pair(Value::integer(1), Value::integer(2));
let result = primitive_set_car(&[pair.clone(), Value::string("new")]).unwrap();
assert_eq!(result, Value::Unspecified);
let car_result = primitive_car(&[pair]).unwrap();
assert_eq!(car_result, Value::string("new"));
}
#[test]
fn test_set_cdr() {
let pair = Value::mutable_pair(Value::integer(1), Value::integer(2));
let result = primitive_set_cdr(&[pair.clone(), Value::string("new")]).unwrap();
assert_eq!(result, Value::Unspecified);
let cdr_result = primitive_cdr(&[pair]).unwrap();
assert_eq!(cdr_result, Value::string("new"));
}
#[test]
fn test_list_set() {
let mut_list = Value::mutable_pair(
Value::string("a"),
Value::mutable_pair(
Value::string("b"),
Value::mutable_pair(
Value::string("c"),
Value::Nil
)
)
);
let result = primitive_list_set(&[mut_list.clone(), Value::integer(1), Value::string("modified")]).unwrap();
assert_eq!(result, Value::Unspecified);
let elem1 = primitive_list_ref(&[mut_list, Value::integer(1)]).unwrap();
assert_eq!(elem1, Value::string("modified"));
}
#[test]
fn test_mutable_list_length() {
let mut_list = Value::mutable_pair(
Value::integer(1),
Value::mutable_pair(
Value::integer(2),
Value::mutable_pair(
Value::integer(3),
Value::Nil
)
)
);
let length = primitive_length(&[mut_list]).unwrap();
assert_eq!(length, Value::integer(3));
}
#[test]
fn test_immutable_pair_mutation_errors() {
let immutable_pair = Value::pair(Value::integer(1), Value::integer(2));
assert!(primitive_set_car(&[immutable_pair.clone(), Value::string("new")]).is_err());
assert!(primitive_set_cdr(&[immutable_pair, Value::string("new")]).is_err());
}
#[test]
fn test_list_set_errors() {
let mut_list = Value::mutable_pair(Value::integer(1), Value::Nil);
assert!(primitive_list_set(&[mut_list.clone(), Value::integer(-1), Value::string("x")]).is_err());
assert!(primitive_list_set(&[mut_list, Value::integer(5), Value::string("x")]).is_err());
assert!(primitive_list_set(&[Value::Nil, Value::string("bad"), Value::string("x")]).is_err());
}
}
#[cfg(test)]
mod evaluator_integration_tests {
use super::*;
use crate::eval::evaluator::Evaluator;
use std::sync::Arc;
#[test]
fn test_evaluator_integration_compiles() {
let _map_func = evaluator_map as fn(&mut Evaluator, &[Value]) -> crate::diagnostics::Result<Value>;
let _filter_func = evaluator_filter as fn(&mut Evaluator, &[Value]) -> crate::diagnostics::Result<Value>;
let _fold_left_func = evaluator_fold_left as fn(&mut Evaluator, &[Value]) -> crate::diagnostics::Result<Value>;
let _fold_right_func = evaluator_fold_right as fn(&mut Evaluator, &[Value]) -> crate::diagnostics::Result<Value>;
let _for_each_func = evaluator_for_each as fn(&mut Evaluator, &[Value]) -> crate::diagnostics::Result<Value>;
assert!(true);
}
}