use crate::eval::{Value, Parameter};
use crate::diagnostics::Result;
use std::collections::HashMap;
pub fn make_parameter(args: &[Value]) -> Result<Value> {
match args.len() {
1 => {
let initial_value = args[0].clone();
let param = Parameter::new(initial_value, None);
Ok(Value::parameter(param))
}
2 => {
let initial_value = args[0].clone();
let converter = args[1].clone();
let param = Parameter::new(initial_value, Some(converter));
Ok(Value::parameter(param))
}
_ => Err(Box::new(crate::diagnostics::Error::runtime_error(
format!("make-parameter expects 1 or 2 arguments, got {}", args.len()),
None,
))),
}
}
pub fn is_parameter(args: &[Value]) -> Result<Value> {
if args.len() != 1 {
return Err(Box::new(crate::diagnostics::Error::runtime_error(
format!("parameter? expects 1 argument, got {}", args.len()),
None,
)));
}
Ok(Value::boolean(args[0].is_parameter()))
}
pub fn call_parameter(parameter: &Parameter, args: &[Value]) -> Result<Value> {
match args.len() {
0 => {
Ok(parameter.get())
}
1 => {
let new_value = args[0].clone();
parameter.set_global(new_value)?;
Ok(Value::Unspecified)
}
_ => Err(Box::new(crate::diagnostics::Error::runtime_error(
format!("parameter expects 0 or 1 arguments, got {}", args.len()),
None,
))),
}
}
pub fn process_parameter_bindings(
bindings: &[crate::ast::ParameterBinding],
mut evaluate_expr: impl FnMut(&crate::ast::Expr) -> Result<Value>,
) -> Result<HashMap<u64, Value>> {
let mut runtime_bindings = HashMap::new();
for binding in bindings {
let param_value = evaluate_expr(&binding.parameter.inner)?;
if let Value::Parameter(param) = param_value {
let value = evaluate_expr(&binding.value.inner)?;
let processed_value = param.apply_converter(value)?;
runtime_bindings.insert(param.id, processed_value);
} else {
return Err(Box::new(crate::diagnostics::Error::runtime_error(
"Expected parameter object in parameterize binding".to_string(),
None,
)));
}
}
Ok(runtime_bindings)
}
pub fn install_parameter_functions(env: &crate::eval::ThreadSafeEnvironment) {
use crate::eval::{PrimitiveProcedure, PrimitiveImpl};
use std::sync::Arc;
let make_param_proc = PrimitiveProcedure {
name: "make-parameter".to_string(),
arity_min: 1,
arity_max: Some(2),
implementation: PrimitiveImpl::RustFn(make_parameter),
effects: vec![],
};
env.define("make-parameter".to_string(), Value::Primitive(Arc::new(make_param_proc)));
let is_param_proc = PrimitiveProcedure {
name: "parameter?".to_string(),
arity_min: 1,
arity_max: Some(1),
implementation: PrimitiveImpl::RustFn(is_parameter),
effects: vec![],
};
env.define("parameter?".to_string(), Value::Primitive(Arc::new(is_param_proc)));
}
#[cfg(test)]
mod tests {
use super::*;
use crate::eval::{Value, Parameter};
#[test]
fn test_make_parameter_basic() {
let args = vec![Value::integer(42)];
let result = make_parameter(&args).unwrap();
assert!(result.is_parameter());
if let Value::Parameter(param) = result {
assert_eq!(param.get().as_integer(), Some(42));
}
}
#[test]
fn test_make_parameter_with_converter() {
let args = vec![
Value::integer(42),
Value::integer(0), ];
let result = make_parameter(&args).unwrap();
assert!(result.is_parameter());
if let Value::Parameter(param) = result {
assert!(param.has_converter());
}
}
#[test]
fn test_is_parameter() {
let param = Parameter::new(Value::integer(42), None);
let param_value = Value::parameter(param);
let args = vec![param_value];
let result = is_parameter(&args).unwrap();
assert_eq!(result, Value::boolean(true));
let args = vec![Value::integer(42)];
let result = is_parameter(&args).unwrap();
assert_eq!(result, Value::boolean(false));
}
#[test]
fn test_call_parameter() {
let param = Parameter::new(Value::integer(42), None);
let result = call_parameter(¶m, &[]).unwrap();
assert_eq!(result.as_integer(), Some(42));
let result = call_parameter(¶m, &[Value::integer(100)]).unwrap();
assert_eq!(result, Value::Unspecified);
let result = call_parameter(¶m, &[]).unwrap();
assert_eq!(result.as_integer(), Some(100));
}
#[test]
fn test_call_parameter_arity_error() {
let param = Parameter::new(Value::integer(42), None);
let result = call_parameter(¶m, &[Value::integer(1), Value::integer(2)]);
assert!(result.is_err());
}
}