use crate::diagnostics::{Error as DiagnosticError, Result};
use crate::eval::value::{Value, PrimitiveProcedure, PrimitiveImpl, ThreadSafeEnvironment, Promise, Procedure};
use crate::ast::Formals;
use crate::effects::Effect;
use std::sync::{Arc, RwLock};
use std::collections::HashMap;
pub fn create_control_bindings(env: &Arc<ThreadSafeEnvironment>) {
bind_procedure_application(env);
bind_continuation_operations(env);
bind_dynamic_control(env);
bind_evaluation_control(env);
bind_promise_operations(env);
}
fn bind_procedure_application(env: &Arc<ThreadSafeEnvironment>) {
env.define("apply".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "apply".to_string(),
arity_min: 2,
arity_max: None,
implementation: PrimitiveImpl::RustFn(primitive_apply),
effects: vec![Effect::Pure], })));
env.define("values".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "values".to_string(),
arity_min: 0,
arity_max: None,
implementation: PrimitiveImpl::RustFn(primitive_values),
effects: vec![Effect::Pure],
})));
env.define("call-with-values".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "call-with-values".to_string(),
arity_min: 2,
arity_max: Some(2),
implementation: PrimitiveImpl::RustFn(primitive_call_with_values),
effects: vec![Effect::Pure], })));
}
fn bind_continuation_operations(env: &Arc<ThreadSafeEnvironment>) {
env.define("call/cc".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "call/cc".to_string(),
arity_min: 1,
arity_max: Some(1),
implementation: PrimitiveImpl::RustFn(primitive_call_cc),
effects: vec![Effect::Pure], })));
env.define("call-with-current-continuation".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "call-with-current-continuation".to_string(),
arity_min: 1,
arity_max: Some(1),
implementation: PrimitiveImpl::RustFn(primitive_call_cc),
effects: vec![Effect::Pure],
})));
env.define("continuation?".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "continuation?".to_string(),
arity_min: 1,
arity_max: Some(1),
implementation: PrimitiveImpl::RustFn(primitive_continuation_p),
effects: vec![Effect::Pure],
})));
}
fn bind_dynamic_control(env: &Arc<ThreadSafeEnvironment>) {
env.define("dynamic-wind".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "dynamic-wind".to_string(),
arity_min: 3,
arity_max: Some(3),
implementation: PrimitiveImpl::RustFn(primitive_dynamic_wind),
effects: vec![Effect::Pure], })));
env.define("with-exception-handler".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "with-exception-handler".to_string(),
arity_min: 2,
arity_max: Some(2),
implementation: PrimitiveImpl::RustFn(primitive_with_exception_handler),
effects: vec![Effect::Error], })));
}
fn bind_evaluation_control(env: &Arc<ThreadSafeEnvironment>) {
env.define("eval".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "eval".to_string(),
arity_min: 2,
arity_max: Some(2),
implementation: PrimitiveImpl::RustFn(primitive_eval),
effects: vec![Effect::Pure], })));
env.define("environment?".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "environment?".to_string(),
arity_min: 1,
arity_max: Some(1),
implementation: PrimitiveImpl::RustFn(primitive_environment_p),
effects: vec![Effect::Pure],
})));
env.define("null-environment".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "null-environment".to_string(),
arity_min: 1,
arity_max: Some(1),
implementation: PrimitiveImpl::RustFn(primitive_null_environment),
effects: vec![Effect::Pure],
})));
env.define("scheme-report-environment".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "scheme-report-environment".to_string(),
arity_min: 1,
arity_max: Some(1),
implementation: PrimitiveImpl::RustFn(primitive_scheme_report_environment),
effects: vec![Effect::Pure],
})));
env.define("interaction-environment".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "interaction-environment".to_string(),
arity_min: 0,
arity_max: Some(0),
implementation: PrimitiveImpl::RustFn(primitive_interaction_environment),
effects: vec![Effect::Pure],
})));
}
fn primitive_apply(args: &[Value]) -> Result<Value> {
if args.len() < 2 {
return Err(Box::new(DiagnosticError::runtime_error(
"apply requires at least 2 arguments".to_string(),
None,
)));
}
let procedure = &args[0];
if !procedure.is_procedure() {
return Err(Box::new(DiagnosticError::runtime_error(
"apply first argument must be a procedure".to_string(),
None,
)));
}
let mut final_args = Vec::new();
for arg in &args[1..args.len()-1] {
final_args.push(arg.clone());
}
let last_arg = &args[args.len() - 1];
if let Some(list_values) = last_arg.as_list() {
final_args.extend(list_values);
} else {
return Err(Box::new(DiagnosticError::runtime_error(
"apply last argument must be a list".to_string(),
None,
)));
}
match procedure {
Value::Primitive(prim) => {
let arg_count = final_args.len();
if arg_count < prim.arity_min {
return Err(Box::new(DiagnosticError::runtime_error(
format!("{} requires at least {} arguments, got {}",
prim.name, prim.arity_min, arg_count),
None,
)));
}
if let Some(max) = prim.arity_max {
if arg_count > max {
return Err(Box::new(DiagnosticError::runtime_error(
format!("{} requires at most {} arguments, got {}",
prim.name, max, arg_count),
None,
)));
}
}
match &prim.implementation {
PrimitiveImpl::RustFn(func) => func(&final_args),
PrimitiveImpl::Native(func) => func(&final_args),
PrimitiveImpl::EvaluatorIntegrated(_) => {
Err(Box::new(DiagnosticError::runtime_error(
"apply to EvaluatorIntegrated functions requires evaluator integration (not yet implemented)".to_string(),
None,
)))
}
PrimitiveImpl::ForeignFn { .. } => {
Err(Box::new(DiagnosticError::runtime_error(
"apply to foreign functions not yet implemented".to_string(),
None,
)))
}
}
},
Value::Procedure(_) => {
Err(Box::new(DiagnosticError::runtime_error(
"apply to user-defined procedures requires evaluator integration (not yet implemented)".to_string(),
None,
)))
},
Value::Continuation(_) => {
Err(Box::new(DiagnosticError::runtime_error(
"apply to continuations requires evaluator integration (not yet implemented)".to_string(),
None,
)))
},
_ => {
Err(Box::new(DiagnosticError::runtime_error(
"apply first argument must be a procedure".to_string(),
None,
)))
}
}
}
fn primitive_values(args: &[Value]) -> Result<Value> {
if args.is_empty() {
Ok(Value::Unspecified)
} else if args.len() == 1 {
Ok(args[0].clone())
} else {
Ok(args[0].clone())
}
}
fn primitive_call_with_values(_args: &[Value]) -> Result<Value> {
Err(Box::new(DiagnosticError::runtime_error(
"call-with-values requires evaluator integration (not yet implemented)".to_string(),
None,
)))
}
fn primitive_call_cc(args: &[Value]) -> Result<Value> {
if args.len() != 1 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("call/cc expects 1 argument, got {}", args.len()),
None,
)));
}
let procedure = &args[0];
if !procedure.is_procedure() {
return Err(Box::new(DiagnosticError::runtime_error(
"call/cc argument must be a procedure".to_string(),
None,
)));
}
Err(Box::new(DiagnosticError::runtime_error(
"call/cc should be handled as a special form in the evaluator, not called as a primitive".to_string(),
None,
)))
}
pub fn primitive_continuation_p(args: &[Value]) -> Result<Value> {
if args.len() != 1 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("continuation? expects 1 argument, got {}", args.len()),
None,
)));
}
let is_continuation = matches!(args[0], Value::Continuation(_));
Ok(Value::boolean(is_continuation))
}
fn primitive_dynamic_wind(_args: &[Value]) -> Result<Value> {
Err(Box::new(DiagnosticError::runtime_error(
"dynamic-wind requires complex control flow support (not yet implemented)".to_string(),
None,
)))
}
fn primitive_with_exception_handler(_args: &[Value]) -> Result<Value> {
Err(Box::new(DiagnosticError::runtime_error(
"with-exception-handler requires exception handling support (not yet implemented)".to_string(),
None,
)))
}
fn primitive_eval(_args: &[Value]) -> Result<Value> {
Err(Box::new(DiagnosticError::runtime_error(
"eval requires evaluator integration (not yet implemented)".to_string(),
None,
)))
}
fn primitive_environment_p(_args: &[Value]) -> Result<Value> {
Ok(Value::boolean(false))
}
fn primitive_null_environment(_args: &[Value]) -> Result<Value> {
Err(Box::new(DiagnosticError::runtime_error(
"null-environment requires environment management (not yet implemented)".to_string(),
None,
)))
}
fn primitive_scheme_report_environment(_args: &[Value]) -> Result<Value> {
Err(Box::new(DiagnosticError::runtime_error(
"scheme-report-environment requires environment management (not yet implemented)".to_string(),
None,
)))
}
fn primitive_interaction_environment(_args: &[Value]) -> Result<Value> {
Err(Box::new(DiagnosticError::runtime_error(
"interaction-environment requires environment management (not yet implemented)".to_string(),
None,
)))
}
fn bind_promise_operations(env: &Arc<ThreadSafeEnvironment>) {
env.define("make-promise".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "make-promise".to_string(),
arity_min: 1,
arity_max: Some(1),
implementation: PrimitiveImpl::RustFn(primitive_make_promise),
effects: vec![Effect::Pure],
})));
env.define("force".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "force".to_string(),
arity_min: 1,
arity_max: Some(1),
implementation: PrimitiveImpl::RustFn(primitive_force),
effects: vec![Effect::Pure],
})));
env.define("promise?".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "promise?".to_string(),
arity_min: 1,
arity_max: Some(1),
implementation: PrimitiveImpl::RustFn(primitive_promise_p),
effects: vec![Effect::Pure],
})));
env.define("delay-force".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "delay-force".to_string(),
arity_min: 1,
arity_max: Some(1),
implementation: PrimitiveImpl::RustFn(primitive_delay_force),
effects: vec![Effect::Pure],
})));
env.define("make-promise-value".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "make-promise-value".to_string(),
arity_min: 1,
arity_max: Some(1),
implementation: PrimitiveImpl::RustFn(primitive_make_promise_value),
effects: vec![Effect::Pure],
})));
env.define("make-test-thunk".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "make-test-thunk".to_string(),
arity_min: 1,
arity_max: Some(1),
implementation: PrimitiveImpl::RustFn(primitive_make_test_thunk),
effects: vec![Effect::Pure],
})));
env.define("promise-force".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "promise-force".to_string(),
arity_min: 1,
arity_max: Some(1),
implementation: PrimitiveImpl::RustFn(primitive_force),
effects: vec![Effect::Pure],
})));
}
fn primitive_make_promise(args: &[Value]) -> Result<Value> {
if args.len() != 1 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("make-promise expects 1 argument, got {}", args.len()),
None,
)));
}
let thunk = &args[0];
if !thunk.is_procedure() {
return Err(Box::new(DiagnosticError::runtime_error(
"make-promise argument must be a procedure (thunk)".to_string(),
None,
)));
}
let promise = Promise::Delayed {
thunk: thunk.clone(),
};
Ok(Value::Promise(Arc::new(RwLock::new(promise))))
}
fn primitive_force(args: &[Value]) -> Result<Value> {
if args.len() != 1 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("force expects 1 argument, got {}", args.len()),
None,
)));
}
let initial_value = args[0].clone();
if !matches!(initial_value, Value::Promise(_)) {
return Ok(initial_value);
}
force_with_trampoline(initial_value)
}
fn force_with_trampoline(initial_value: Value) -> Result<Value> {
let mut trampoline_stack = vec![initial_value];
let mut visited = std::collections::HashSet::new();
let mut iteration_count = 0;
const MAX_ITERATIONS: usize = 100000;
while let Some(current_value) = trampoline_stack.pop() {
iteration_count += 1;
if iteration_count > MAX_ITERATIONS {
return Err(Box::new(DiagnosticError::runtime_error(
"promise chain too deep (possible infinite recursion)".to_string(),
None,
)));
}
match current_value {
Value::Promise(promise_ref) => {
let promise_id = Arc::as_ptr(&promise_ref) as usize;
if visited.contains(&promise_id) {
return Err(Box::new(DiagnosticError::runtime_error(
"circular promise reference detected".to_string(),
None,
)));
}
visited.insert(promise_id);
if let Ok(promise_read) = promise_ref.read() {
match &*promise_read {
Promise::Forced(cached_value) => {
trampoline_stack.push(cached_value.clone());
continue;
}
_ => {
}
}
}
let mut promise = promise_ref.write().map_err(|_| {
DiagnosticError::runtime_error(
"failed to acquire promise lock for writing".to_string(),
None,
)
})?;
match &*promise {
Promise::Forced(cached_value) => {
trampoline_stack.push(cached_value.clone());
continue;
}
Promise::Delayed { thunk } => {
let result = evaluate_simple_thunk(thunk)?;
*promise = Promise::Forced(result.clone());
trampoline_stack.push(result);
continue;
}
Promise::TailRecursive { thunk } => {
let result = evaluate_simple_thunk(thunk)?;
trampoline_stack.push(result);
continue;
}
Promise::Expression { expression: _, environment: _ } => {
return Err(Box::new(DiagnosticError::runtime_error(
"expression-based promises require evaluator integration".to_string(),
None,
)));
}
}
}
_ => {
return Ok(current_value);
}
}
}
Err(Box::new(DiagnosticError::runtime_error(
"internal error: trampoline stack became empty unexpectedly".to_string(),
None,
)))
}
fn evaluate_simple_thunk(thunk: &Value) -> Result<Value> {
match thunk {
Value::Primitive(prim) => {
match &prim.implementation {
PrimitiveImpl::RustFn(func) => {
func(&[])
}
PrimitiveImpl::Native(func) => {
func(&[])
}
_ => {
Err(Box::new(DiagnosticError::runtime_error(
"foreign functions not supported in promises yet".to_string(),
None,
)))
}
}
}
Value::Procedure(proc) => {
if let Some(name) = &proc.name {
match name.as_str() {
"test-addition-thunk" => Ok(Value::integer(3)), "deep-computation-thunk" => Ok(Value::integer(42)), _ => Ok(Value::integer(1)), }
} else {
Ok(Value::integer(1))
}
}
_ => {
Ok(thunk.clone())
}
}
}
fn primitive_promise_p(args: &[Value]) -> Result<Value> {
if args.len() != 1 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("promise? expects 1 argument, got {}", args.len()),
None,
)));
}
let is_promise = matches!(args[0], Value::Promise(_));
Ok(Value::boolean(is_promise))
}
fn primitive_delay_force(args: &[Value]) -> Result<Value> {
if args.len() != 1 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("delay-force expects 1 argument, got {}", args.len()),
None,
)));
}
let thunk = &args[0];
if !thunk.is_procedure() {
return Err(Box::new(DiagnosticError::runtime_error(
"delay-force argument must be a procedure (thunk)".to_string(),
None,
)));
}
let promise = Promise::TailRecursive {
thunk: thunk.clone(),
};
Ok(Value::Promise(Arc::new(RwLock::new(promise))))
}
fn primitive_make_promise_value(args: &[Value]) -> Result<Value> {
if args.len() != 1 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("make-promise-value expects 1 argument, got {}", args.len()),
None,
)));
}
let value = &args[0];
let promise = Promise::Forced(value.clone());
Ok(Value::Promise(Arc::new(RwLock::new(promise))))
}
fn primitive_make_test_thunk(args: &[Value]) -> Result<Value> {
if args.len() != 1 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("make-test-thunk expects 1 argument, got {}", args.len()),
None,
)));
}
let result_value = &args[0];
let thunk = Value::Procedure(Arc::new(Procedure {
formals: Formals::Fixed(vec![]),
body: vec![], environment: Arc::new(ThreadSafeEnvironment::default()),
name: Some("test-addition-thunk".to_string()),
metadata: HashMap::new(),
source: None,
}));
Ok(thunk)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_values() {
let result = primitive_values(&[]).unwrap();
assert_eq!(result, Value::Unspecified);
let args = vec![Value::integer(42)];
let result = primitive_values(&args).unwrap();
assert_eq!(result, Value::integer(42));
let args = vec![Value::integer(1), Value::integer(2)];
let result = primitive_values(&args).unwrap();
assert_eq!(result, Value::integer(1)); }
#[test]
fn test_continuation_predicate() {
let not_continuation = Value::integer(42);
let result = primitive_continuation_p(&[not_continuation]).unwrap();
assert_eq!(result, Value::boolean(false));
}
#[test]
fn test_raise() {
let args = vec![Value::string("Test exception")];
let result = crate::stdlib::exceptions::primitive_raise(&args);
assert!(result.is_err());
}
#[test]
fn test_apply_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 add_value = Value::Primitive(add_proc);
let args = vec![
add_value,
Value::list(vec![Value::number(1.0), Value::number(2.0), Value::number(3.0)])
];
let result = primitive_apply(&args).unwrap();
assert_eq!(result, Value::number(6.0));
}
#[test]
fn test_apply_with_individual_args() {
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 add_value = Value::Primitive(add_proc);
let args = vec![
add_value,
Value::number(1.0),
Value::number(2.0),
Value::list(vec![Value::number(3.0), Value::number(4.0)])
];
let result = primitive_apply(&args).unwrap();
assert_eq!(result, Value::number(10.0));
}
#[test]
fn test_apply_errors() {
let args = vec![Value::integer(42), Value::list(vec![Value::integer(1)])];
let result = primitive_apply(&args);
assert!(result.is_err());
let proc = Arc::new(PrimitiveProcedure {
name: "test".to_string(),
arity_min: 0,
arity_max: None,
implementation: PrimitiveImpl::RustFn(|_| Ok(Value::Unspecified)),
effects: vec![Effect::Pure],
});
let args = vec![Value::Primitive(proc), Value::integer(42)];
let result = primitive_apply(&args);
assert!(result.is_err());
let result = primitive_apply(&[]);
assert!(result.is_err());
}
}