use crate::frontend::ast::{Expr, Pattern};
use crate::runtime::eval_pattern::match_pattern;
use crate::runtime::{InterpreterError, Value};
use std::cell::{Cell, RefCell}; use std::collections::HashMap;
use std::rc::Rc; use std::sync::Arc;
thread_local! {
static CALL_DEPTH: Cell<usize> = const { Cell::new(0) };
static MAX_DEPTH: Cell<usize> = const { Cell::new(1000) };
}
pub fn set_max_recursion_depth(depth: usize) {
MAX_DEPTH.with(|max| max.set(depth));
}
pub fn get_current_depth() -> usize {
CALL_DEPTH.with(std::cell::Cell::get)
}
pub fn check_recursion_depth() -> Result<(), InterpreterError> {
CALL_DEPTH.with(|depth| {
let current = depth.get();
MAX_DEPTH.with(|max| {
let max_val = max.get();
if current >= max_val {
Err(InterpreterError::RecursionLimitExceeded(current, max_val))
} else {
depth.set(current + 1);
Ok(())
}
})
})
}
pub fn decrement_depth() {
CALL_DEPTH.with(|depth| {
depth.set(depth.get().saturating_sub(1));
});
}
#[derive(Debug, Clone)]
pub struct Closure {
pub params: Vec<Pattern>,
pub body: Expr,
pub captured_env: HashMap<String, Value>,
pub name: Option<String>,
}
impl Closure {
pub fn new(params: Vec<Pattern>, body: Expr, captured_env: HashMap<String, Value>) -> Self {
Self {
params,
body,
captured_env,
name: None,
}
}
pub fn named(
params: Vec<Pattern>,
body: Expr,
captured_env: HashMap<String, Value>,
name: String,
) -> Self {
Self {
params,
body,
captured_env,
name: Some(name),
}
}
}
pub fn eval_function_def<F>(
name: &str,
params: &[crate::frontend::ast::Param],
body: &Expr,
capture_environment: F,
) -> Result<Value, InterpreterError>
where
F: FnOnce() -> HashMap<String, Value>,
{
let captured_env = capture_environment();
let param_patterns: Vec<Pattern> = params.iter().map(|p| p.pattern.clone()).collect();
let closure = Closure::named(param_patterns, body.clone(), captured_env, name.to_string());
let params_with_defaults: Vec<(String, Option<Arc<Expr>>)> = params
.iter()
.map(|p| {
let name = match &p.pattern {
Pattern::Identifier(name) => name.clone(),
_ => "_".to_string(), };
let default = p
.default_value
.clone()
.map(|expr| Arc::new((*expr).clone()));
(name, default)
})
.collect();
Ok(Value::Closure {
params: params_with_defaults,
body: Arc::new(closure.body),
env: Rc::new(RefCell::new(closure.captured_env)), })
}
pub fn eval_lambda<F>(
params: &[crate::frontend::ast::Param],
body: &Expr,
capture_environment: F,
) -> Result<Value, InterpreterError>
where
F: FnOnce() -> HashMap<String, Value>,
{
let captured_env = capture_environment();
let param_patterns: Vec<Pattern> = params.iter().map(|p| p.pattern.clone()).collect();
let closure = Closure::new(param_patterns, body.clone(), captured_env);
let params_with_defaults: Vec<(String, Option<Arc<Expr>>)> = params
.iter()
.map(|p| {
let name = match &p.pattern {
Pattern::Identifier(name) => name.clone(),
_ => "_".to_string(), };
let default = p
.default_value
.clone()
.map(|expr| Arc::new((*expr).clone()));
(name, default)
})
.collect();
Ok(Value::Closure {
params: params_with_defaults,
body: Arc::new(closure.body),
env: Rc::new(RefCell::new(closure.captured_env)), })
}
pub fn eval_function_call<F1, F2>(
function: &Value,
args: &[Value],
eval_with_env: F1,
_eval_builtin: F2,
) -> Result<Value, InterpreterError>
where
F1: FnMut(&Expr, &HashMap<String, Value>) -> Result<Value, InterpreterError>,
F2: FnMut(&str, &[Value]) -> Result<Option<Value>, InterpreterError>,
{
match function {
Value::Closure { params, body, env } => {
eval_closure_call_direct(params, body, env, args, eval_with_env)
}
_ => Err(InterpreterError::TypeError(format!(
"Cannot call non-function value of type {}",
function.type_name()
))),
}
}
fn eval_closure_call_direct<F>(
params: &[(String, Option<Arc<Expr>>)], body: &Expr,
env: &Rc<RefCell<HashMap<String, Value>>>, args: &[Value],
mut eval_with_env: F,
) -> Result<Value, InterpreterError>
where
F: FnMut(&Expr, &HashMap<String, Value>) -> Result<Value, InterpreterError>,
{
check_recursion_depth()?;
let result = (|| {
let required_count = params
.iter()
.filter(|(_, default)| default.is_none())
.count();
let total_count = params.len();
if args.len() < required_count || args.len() > total_count {
return Err(InterpreterError::RuntimeError(format!(
"Function expects {}-{} arguments, got {}",
required_count,
total_count,
args.len()
)));
}
let mut call_env = env.borrow().clone();
for (i, (param_name, default_value)) in params.iter().enumerate() {
let value = if i < args.len() {
args[i].clone()
} else if let Some(default_expr) = default_value {
eval_with_env(default_expr, &call_env)?
} else {
unreachable!("Missing required parameter");
};
call_env.insert(param_name.clone(), value);
}
match eval_with_env(body, &call_env) {
Err(InterpreterError::Return(val)) => Ok(val),
other => other,
}
})();
decrement_depth();
result
}
fn eval_closure_call<F>(
closure: &Closure,
args: &[Value],
mut eval_with_env: F,
) -> Result<Value, InterpreterError>
where
F: FnMut(&Expr, &HashMap<String, Value>) -> Result<Value, InterpreterError>,
{
if args.len() != closure.params.len() {
return Err(InterpreterError::RuntimeError(format!(
"Function expects {} arguments, got {}",
closure.params.len(),
args.len()
)));
}
let mut call_env = closure.captured_env.clone();
if let Some(ref name) = closure.name {
let closure_value = Value::Closure {
params: closure
.params
.iter()
.map(|p| match p {
Pattern::Identifier(name) => (name.clone(), None),
_ => ("_".to_string(), None),
})
.collect(),
body: Arc::new(closure.body.clone()),
env: Rc::new(RefCell::new(closure.captured_env.clone())), };
call_env.insert(name.clone(), closure_value);
}
for (param, arg) in closure.params.iter().zip(args.iter()) {
bind_parameter(param, arg, &mut call_env)?;
}
match eval_with_env(&closure.body, &call_env) {
Err(InterpreterError::Return(val)) => Ok(val),
other => other,
}
}
fn bind_parameter(
pattern: &Pattern,
value: &Value,
env: &mut HashMap<String, Value>,
) -> Result<(), InterpreterError> {
let match_result = match_pattern(pattern, value)?;
if !match_result.matches {
return Err(InterpreterError::RuntimeError(
"Parameter pattern does not match argument value".to_string(),
));
}
env.extend(match_result.bindings);
Ok(())
}
pub fn eval_method_call_value<F1, F2>(
receiver: &Value,
method: &str,
args: &[Value],
eval_function_call_value: F1,
mut eval_method_dispatch: F2,
) -> Result<Value, InterpreterError>
where
F1: FnMut(&Value, &[Value]) -> Result<Value, InterpreterError>,
F2: FnMut(&Value, &str, &[Value], bool) -> Result<Value, InterpreterError>,
{
match eval_method_dispatch(receiver, method, args, args.is_empty()) {
Ok(result) => Ok(result),
Err(InterpreterError::RuntimeError(msg)) if msg.contains("not found") => {
try_method_as_function(receiver, method, args, eval_function_call_value)
}
Err(e) => Err(e),
}
}
fn try_method_as_function<F>(
receiver: &Value,
method: &str,
args: &[Value],
mut eval_function_call_value: F,
) -> Result<Value, InterpreterError>
where
F: FnMut(&Value, &[Value]) -> Result<Value, InterpreterError>,
{
if let Ok(function_value) = get_function_from_env(method) {
let mut all_args = vec![receiver.clone()];
all_args.extend_from_slice(args);
eval_function_call_value(&function_value, &all_args)
} else {
Err(InterpreterError::RuntimeError(format!(
"Method '{}' not found for type {}",
method,
receiver.type_name()
)))
}
}
fn get_function_from_env(name: &str) -> Result<Value, InterpreterError> {
Err(InterpreterError::RuntimeError(format!(
"Function '{name}' not found in environment"
)))
}
pub fn create_partial_application(
function: &Value,
partial_args: &[Value],
) -> Result<Value, InterpreterError> {
match function {
Value::Closure { params, body, env } => {
let closure = Closure {
params: params
.iter()
.map(|(param_name, _default_value)| Pattern::Identifier(param_name.clone()))
.collect(),
body: body.as_ref().clone(),
captured_env: env.borrow().clone(), name: None,
};
if partial_args.len() >= closure.params.len() {
return Err(InterpreterError::RuntimeError(
"Cannot partially apply more arguments than function parameters".to_string(),
));
}
let remaining_params = closure.params[partial_args.len()..].to_vec();
let mut new_captured_env = closure.captured_env.clone();
for (param, arg) in closure.params.iter().zip(partial_args.iter()) {
bind_parameter(param, arg, &mut new_captured_env)?;
}
let partial_closure =
Closure::new(remaining_params, closure.body.clone(), new_captured_env);
Ok(Value::Closure {
params: partial_closure
.params
.iter()
.map(|p| match p {
Pattern::Identifier(name) => (name.clone(), None),
_ => ("_".to_string(), None),
})
.collect(),
body: Arc::new(partial_closure.body),
env: Rc::new(RefCell::new(partial_closure.captured_env)), })
}
_ => Err(InterpreterError::TypeError(
"Cannot create partial application of non-function".to_string(),
)),
}
}
pub fn eval_function_composition(f: &Value, g: &Value) -> Result<Value, InterpreterError> {
match (f, g) {
(
Value::Closure {
params: f_params,
body: _f_body,
env: _f_env,
},
Value::Closure {
params: g_params,
body: g_body,
env: g_env,
},
) => {
if g_params.len() != 1 {
return Err(InterpreterError::RuntimeError(
"Function composition requires second function to have exactly one parameter"
.to_string(),
));
}
if f_params.len() != 1 {
return Err(InterpreterError::RuntimeError(
"Function composition requires first function to have exactly one parameter"
.to_string(),
));
}
Ok(Value::Closure {
params: g_params.clone(),
body: g_body.clone(), env: g_env.clone(),
})
}
_ => Err(InterpreterError::TypeError(
"Function composition requires two functions".to_string(),
)),
}
}
fn create_composition_closure(f: &Closure, g: &Closure) -> Result<Closure, InterpreterError> {
let param = g.params[0].clone();
let composed_body = Expr::new(
crate::frontend::ast::ExprKind::Literal(crate::frontend::ast::Literal::Unit),
crate::frontend::ast::Span::new(0, 0),
);
let mut combined_env = g.captured_env.clone();
combined_env.extend(f.captured_env.clone());
Ok(Closure::new(vec![param], composed_body, combined_env))
}
pub fn is_callable(value: &Value) -> bool {
matches!(value, Value::Closure { .. })
}
pub fn get_arity(value: &Value) -> Result<usize, InterpreterError> {
match value {
Value::Closure { params, .. } => Ok(params.len()),
_ => Err(InterpreterError::TypeError(
"Cannot get arity of non-callable value".to_string(),
)),
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::frontend::ast::{ExprKind, Literal, Span};
fn lit_expr(val: i64) -> Expr {
Expr::new(
ExprKind::Literal(Literal::Integer(val, None)),
Span::new(0, 0),
)
}
fn unit_expr() -> Expr {
Expr::new(ExprKind::Literal(Literal::Unit), Span::new(0, 0))
}
#[test]
fn test_set_max_recursion_depth() {
set_max_recursion_depth(500);
set_max_recursion_depth(1000); }
#[test]
fn test_get_current_depth_initially_zero() {
CALL_DEPTH.with(|d| d.set(0));
assert_eq!(get_current_depth(), 0);
}
#[test]
fn test_check_recursion_depth_increments() {
CALL_DEPTH.with(|d| d.set(0));
MAX_DEPTH.with(|m| m.set(1000));
check_recursion_depth().unwrap();
assert_eq!(get_current_depth(), 1);
check_recursion_depth().unwrap();
assert_eq!(get_current_depth(), 2);
decrement_depth();
decrement_depth();
}
#[test]
fn test_check_recursion_depth_limit_exceeded() {
CALL_DEPTH.with(|d| d.set(5));
MAX_DEPTH.with(|m| m.set(5));
let result = check_recursion_depth();
assert!(matches!(
result,
Err(InterpreterError::RecursionLimitExceeded(5, 5))
));
CALL_DEPTH.with(|d| d.set(0));
MAX_DEPTH.with(|m| m.set(1000));
}
#[test]
fn test_decrement_depth() {
CALL_DEPTH.with(|d| d.set(5));
decrement_depth();
assert_eq!(get_current_depth(), 4);
decrement_depth();
assert_eq!(get_current_depth(), 3);
CALL_DEPTH.with(|d| d.set(0));
}
#[test]
fn test_decrement_depth_saturating() {
CALL_DEPTH.with(|d| d.set(0));
decrement_depth();
assert_eq!(get_current_depth(), 0);
}
#[test]
fn test_closure_creation() {
let params = vec![Pattern::Identifier("x".to_string())];
let body = Expr::new(
ExprKind::Literal(Literal::Integer(42, None)),
Span::new(3, 5),
);
let env = HashMap::new();
let closure = Closure::new(params, body, env);
assert_eq!(closure.params.len(), 1);
assert!(closure.name.is_none());
}
#[test]
fn test_named_closure_creation() {
let params = vec![Pattern::Identifier("x".to_string())];
let body = Expr::new(
ExprKind::Literal(Literal::Integer(42, None)),
Span::new(3, 5),
);
let env = HashMap::new();
let closure = Closure::named(params, body, env, "factorial".to_string());
assert_eq!(closure.params.len(), 1);
assert_eq!(closure.name, Some("factorial".to_string()));
}
#[test]
fn test_closure_with_captured_env() {
let params = vec![Pattern::Identifier("x".to_string())];
let body = unit_expr();
let mut env = HashMap::new();
env.insert("captured_var".to_string(), Value::Integer(100));
let closure = Closure::new(params, body, env);
assert_eq!(
closure.captured_env.get("captured_var"),
Some(&Value::Integer(100))
);
}
#[test]
fn test_parameter_binding() {
let pattern = Pattern::Identifier("x".to_string());
let value = Value::Integer(42);
let mut env = HashMap::new();
bind_parameter(&pattern, &value, &mut env).unwrap();
assert_eq!(env.get("x"), Some(&Value::Integer(42)));
}
#[test]
fn test_parameter_binding_string_value() {
let pattern = Pattern::Identifier("name".to_string());
let value = Value::String("hello".into());
let mut env = HashMap::new();
bind_parameter(&pattern, &value, &mut env).unwrap();
assert_eq!(env.get("name"), Some(&Value::String("hello".into())));
}
#[test]
fn test_parameter_binding_multiple() {
let mut env = HashMap::new();
bind_parameter(
&Pattern::Identifier("a".to_string()),
&Value::Integer(1),
&mut env,
)
.unwrap();
bind_parameter(
&Pattern::Identifier("b".to_string()),
&Value::Integer(2),
&mut env,
)
.unwrap();
bind_parameter(
&Pattern::Identifier("c".to_string()),
&Value::Integer(3),
&mut env,
)
.unwrap();
assert_eq!(env.len(), 3);
assert_eq!(env.get("a"), Some(&Value::Integer(1)));
assert_eq!(env.get("b"), Some(&Value::Integer(2)));
assert_eq!(env.get("c"), Some(&Value::Integer(3)));
}
#[test]
fn test_is_callable() {
let _closure = Closure::new(
vec![],
Expr::new(ExprKind::Literal(Literal::Unit), Span::new(0, 0)),
HashMap::new(),
);
let function_value = Value::Closure {
params: vec![],
body: Arc::new(Expr::new(ExprKind::Literal(Literal::Unit), Span::new(0, 0))),
env: Rc::new(RefCell::new(HashMap::new())),
};
assert!(is_callable(&function_value));
let non_callable = Value::Integer(42);
assert!(!is_callable(&non_callable));
}
#[test]
fn test_is_callable_string_not_callable() {
assert!(!is_callable(&Value::String("hello".into())));
}
#[test]
fn test_is_callable_bool_not_callable() {
assert!(!is_callable(&Value::Bool(true)));
}
#[test]
fn test_is_callable_nil_not_callable() {
assert!(!is_callable(&Value::Nil));
}
#[test]
fn test_get_arity() {
let _params = [
Pattern::Identifier("x".to_string()),
Pattern::Identifier("y".to_string()),
];
let function_value = Value::Closure {
params: vec![("x".to_string(), None), ("y".to_string(), None)],
body: Arc::new(Expr::new(ExprKind::Literal(Literal::Unit), Span::new(0, 0))),
env: Rc::new(RefCell::new(HashMap::new())),
};
assert_eq!(get_arity(&function_value).unwrap(), 2);
let non_callable = Value::Integer(42);
assert!(get_arity(&non_callable).is_err());
}
#[test]
fn test_get_arity_zero_params() {
let function_value = Value::Closure {
params: vec![],
body: Arc::new(unit_expr()),
env: Rc::new(RefCell::new(HashMap::new())),
};
assert_eq!(get_arity(&function_value).unwrap(), 0);
}
#[test]
fn test_get_arity_three_params() {
let function_value = Value::Closure {
params: vec![
("a".to_string(), None),
("b".to_string(), None),
("c".to_string(), None),
],
body: Arc::new(unit_expr()),
env: Rc::new(RefCell::new(HashMap::new())),
};
assert_eq!(get_arity(&function_value).unwrap(), 3);
}
#[test]
fn test_get_arity_error_on_string() {
let result = get_arity(&Value::String("not a function".into()));
assert!(result.is_err());
}
#[test]
fn test_eval_function_call_non_function() {
let non_function = Value::Integer(42);
let result = eval_function_call(&non_function, &[], |_, _| Ok(Value::Nil), |_, _| Ok(None));
assert!(matches!(result, Err(InterpreterError::TypeError(_))));
}
#[test]
fn test_eval_function_call_closure() {
CALL_DEPTH.with(|d| d.set(0));
MAX_DEPTH.with(|m| m.set(1000));
let closure = Value::Closure {
params: vec![("x".to_string(), None)],
body: Arc::new(lit_expr(42)),
env: Rc::new(RefCell::new(HashMap::new())),
};
let result = eval_function_call(
&closure,
&[Value::Integer(10)],
|_body, _env| Ok(Value::Integer(42)),
|_, _| Ok(None),
);
assert!(result.is_ok());
assert_eq!(result.unwrap(), Value::Integer(42));
}
#[test]
fn test_create_partial_application_non_function() {
let non_function = Value::Integer(42);
let result = create_partial_application(&non_function, &[Value::Integer(1)]);
assert!(matches!(result, Err(InterpreterError::TypeError(_))));
}
#[test]
fn test_create_partial_application_too_many_args() {
let closure = Value::Closure {
params: vec![("x".to_string(), None)],
body: Arc::new(lit_expr(42)),
env: Rc::new(RefCell::new(HashMap::new())),
};
let result = create_partial_application(&closure, &[Value::Integer(1), Value::Integer(2)]);
assert!(matches!(result, Err(InterpreterError::RuntimeError(_))));
}
#[test]
fn test_create_partial_application_success() {
let closure = Value::Closure {
params: vec![("x".to_string(), None), ("y".to_string(), None)],
body: Arc::new(lit_expr(42)),
env: Rc::new(RefCell::new(HashMap::new())),
};
let result = create_partial_application(&closure, &[Value::Integer(1)]);
assert!(result.is_ok());
if let Ok(Value::Closure { params, .. }) = result {
assert_eq!(params.len(), 1);
} else {
panic!("Expected closure");
}
}
#[test]
fn test_eval_function_composition_non_functions() {
let non_function = Value::Integer(42);
let result = eval_function_composition(&non_function, &non_function);
assert!(matches!(result, Err(InterpreterError::TypeError(_))));
}
#[test]
fn test_eval_function_composition_wrong_arity_first() {
let f = Value::Closure {
params: vec![("a".to_string(), None), ("b".to_string(), None)],
body: Arc::new(lit_expr(1)),
env: Rc::new(RefCell::new(HashMap::new())),
};
let g = Value::Closure {
params: vec![("x".to_string(), None)],
body: Arc::new(lit_expr(2)),
env: Rc::new(RefCell::new(HashMap::new())),
};
let result = eval_function_composition(&f, &g);
assert!(matches!(result, Err(InterpreterError::RuntimeError(_))));
}
#[test]
fn test_eval_function_composition_wrong_arity_second() {
let f = Value::Closure {
params: vec![("a".to_string(), None)],
body: Arc::new(lit_expr(1)),
env: Rc::new(RefCell::new(HashMap::new())),
};
let g = Value::Closure {
params: vec![("x".to_string(), None), ("y".to_string(), None)],
body: Arc::new(lit_expr(2)),
env: Rc::new(RefCell::new(HashMap::new())),
};
let result = eval_function_composition(&f, &g);
assert!(matches!(result, Err(InterpreterError::RuntimeError(_))));
}
#[test]
fn test_eval_function_composition_success() {
let f = Value::Closure {
params: vec![("a".to_string(), None)],
body: Arc::new(lit_expr(1)),
env: Rc::new(RefCell::new(HashMap::new())),
};
let g = Value::Closure {
params: vec![("x".to_string(), None)],
body: Arc::new(lit_expr(2)),
env: Rc::new(RefCell::new(HashMap::new())),
};
let result = eval_function_composition(&f, &g);
assert!(result.is_ok());
}
#[test]
fn test_get_function_from_env_not_found() {
let result = get_function_from_env("nonexistent");
assert!(result.is_err());
}
}
#[cfg(test)]
mod round_130_tests {
use super::*;
use crate::frontend::ast::{ExprKind, Literal, Span};
use std::cell::RefCell;
use std::rc::Rc;
fn make_lit_expr(val: i64) -> Expr {
Expr::new(
ExprKind::Literal(Literal::Integer(val, None)),
Span::new(0, 0),
)
}
fn make_unit_expr() -> Expr {
Expr::new(ExprKind::Literal(Literal::Unit), Span::new(0, 0))
}
#[test]
fn test_set_max_recursion_depth_r130() {
set_max_recursion_depth(500);
let depth = MAX_DEPTH.with(|m| m.get());
assert_eq!(depth, 500);
set_max_recursion_depth(1000); }
#[test]
fn test_get_current_depth_r130() {
CALL_DEPTH.with(|d| d.set(5));
let depth = get_current_depth();
assert_eq!(depth, 5);
CALL_DEPTH.with(|d| d.set(0)); }
#[test]
fn test_check_recursion_depth_success_r130() {
CALL_DEPTH.with(|d| d.set(0));
MAX_DEPTH.with(|m| m.set(1000));
let result = check_recursion_depth();
assert!(result.is_ok());
let depth = get_current_depth();
assert_eq!(depth, 1);
CALL_DEPTH.with(|d| d.set(0)); }
#[test]
fn test_check_recursion_depth_exceeded_r130() {
CALL_DEPTH.with(|d| d.set(100));
MAX_DEPTH.with(|m| m.set(100));
let result = check_recursion_depth();
assert!(result.is_err());
CALL_DEPTH.with(|d| d.set(0)); MAX_DEPTH.with(|m| m.set(1000)); }
#[test]
fn test_decrement_depth_r130() {
CALL_DEPTH.with(|d| d.set(5));
decrement_depth();
let depth = get_current_depth();
assert_eq!(depth, 4);
CALL_DEPTH.with(|d| d.set(0)); }
#[test]
fn test_decrement_depth_at_zero_r130() {
CALL_DEPTH.with(|d| d.set(0));
decrement_depth();
let depth = get_current_depth();
assert_eq!(depth, 0); }
#[test]
fn test_closure_new_r130() {
let params = vec![Pattern::Identifier("x".to_string())];
let body = make_lit_expr(42);
let env = HashMap::new();
let closure = Closure::new(params.clone(), body.clone(), env);
assert_eq!(closure.params.len(), 1);
assert!(closure.name.is_none());
}
#[test]
fn test_closure_with_name_r130() {
let params = vec![Pattern::Identifier("x".to_string())];
let body = make_lit_expr(42);
let env = HashMap::new();
let mut closure = Closure::new(params, body, env);
closure.name = Some("my_func".to_string());
assert_eq!(closure.name, Some("my_func".to_string()));
}
#[test]
fn test_closure_named_r130() {
let params = vec![Pattern::Identifier("x".to_string())];
let body = make_lit_expr(42);
let env = HashMap::new();
let closure = Closure::named(params, body, env, "test_fn".to_string());
assert_eq!(closure.name, Some("test_fn".to_string()));
}
#[test]
fn test_closure_empty_params_r130() {
let params: Vec<Pattern> = vec![];
let body = make_unit_expr();
let env = HashMap::new();
let closure = Closure::new(params, body, env);
assert_eq!(closure.params.len(), 0);
}
#[test]
fn test_closure_with_captured_env_r130() {
let params = vec![Pattern::Identifier("x".to_string())];
let body = make_lit_expr(0);
let mut env = HashMap::new();
env.insert("outer_var".to_string(), Value::Integer(100));
let closure = Closure::new(params, body, env.clone());
assert!(closure.captured_env.contains_key("outer_var"));
assert_eq!(
closure.captured_env.get("outer_var"),
Some(&Value::Integer(100))
);
}
#[test]
fn test_closure_multiple_params_r130() {
let params = vec![
Pattern::Identifier("a".to_string()),
Pattern::Identifier("b".to_string()),
Pattern::Identifier("c".to_string()),
];
let body = make_unit_expr();
let closure = Closure::new(params, body, HashMap::new());
assert_eq!(closure.params.len(), 3);
}
#[test]
fn test_is_callable_closure_r130() {
let value = Value::Closure {
params: vec![("x".to_string(), None)],
body: Arc::new(make_lit_expr(42)),
env: Rc::new(RefCell::new(HashMap::new())),
};
assert!(is_callable(&value));
}
#[test]
fn test_is_callable_integer_r130() {
assert!(!is_callable(&Value::Integer(42)));
}
#[test]
fn test_is_callable_string_r130() {
assert!(!is_callable(&Value::String(Arc::from("hello"))));
}
#[test]
fn test_is_callable_nil_r130() {
assert!(!is_callable(&Value::Nil));
}
#[test]
fn test_is_callable_bool_r130() {
assert!(!is_callable(&Value::Bool(true)));
assert!(!is_callable(&Value::Bool(false)));
}
#[test]
fn test_is_callable_float_r130() {
assert!(!is_callable(&Value::Float(3.14)));
}
#[test]
fn test_is_callable_array_r130() {
let arr = Value::from_array(vec![Value::Integer(1)]);
assert!(!is_callable(&arr));
}
#[test]
fn test_is_callable_builtin_r130() {
let builtin = Value::BuiltinFunction("print".to_string());
assert!(!is_callable(&builtin)); }
#[test]
fn test_get_arity_closure_r130() {
let value = Value::Closure {
params: vec![("x".to_string(), None), ("y".to_string(), None)],
body: Arc::new(make_lit_expr(42)),
env: Rc::new(RefCell::new(HashMap::new())),
};
let result = get_arity(&value);
assert!(result.is_ok());
assert_eq!(result.unwrap(), 2);
}
#[test]
fn test_get_arity_nil_r130() {
let result = get_arity(&Value::Nil);
assert!(result.is_err());
}
#[test]
fn test_get_arity_bool_r130() {
let result = get_arity(&Value::Bool(true));
assert!(result.is_err());
}
#[test]
fn test_get_arity_float_r130() {
let result = get_arity(&Value::Float(3.14));
assert!(result.is_err());
}
#[test]
fn test_get_arity_array_r130() {
let result = get_arity(&Value::from_array(vec![Value::Integer(1)]));
assert!(result.is_err());
}
#[test]
fn test_get_arity_string_r130() {
let result = get_arity(&Value::String(Arc::from("hello")));
assert!(result.is_err());
}
#[test]
fn test_get_arity_object_r130() {
let obj = Value::Object(Arc::new(HashMap::new()));
let result = get_arity(&obj);
assert!(result.is_err());
}
#[test]
fn test_get_arity_zero_params_r130() {
let value = Value::Closure {
params: vec![],
body: Arc::new(make_unit_expr()),
env: Rc::new(RefCell::new(HashMap::new())),
};
let result = get_arity(&value);
assert!(result.is_ok());
assert_eq!(result.unwrap(), 0);
}
#[test]
fn test_max_recursion_depth_boundary_r130() {
CALL_DEPTH.with(|d| d.set(999));
MAX_DEPTH.with(|m| m.set(1000));
let result = check_recursion_depth();
assert!(result.is_ok());
let result2 = check_recursion_depth();
assert!(result2.is_err());
CALL_DEPTH.with(|d| d.set(0));
}
#[test]
fn test_decrement_depth_multiple_r130() {
CALL_DEPTH.with(|d| d.set(10));
decrement_depth();
assert_eq!(get_current_depth(), 9);
decrement_depth();
assert_eq!(get_current_depth(), 8);
decrement_depth();
assert_eq!(get_current_depth(), 7);
CALL_DEPTH.with(|d| d.set(0));
}
#[test]
fn test_eval_function_call_non_callable_r130() {
CALL_DEPTH.with(|d| d.set(0));
MAX_DEPTH.with(|m| m.set(1000));
let non_callable = Value::Integer(42);
let result = eval_function_call(&non_callable, &[], |_, _| Ok(Value::Nil), |_, _| Ok(None));
assert!(result.is_err());
CALL_DEPTH.with(|d| d.set(0));
}
#[test]
fn test_eval_function_call_closure_r130() {
CALL_DEPTH.with(|d| d.set(0));
MAX_DEPTH.with(|m| m.set(1000));
let closure = Value::Closure {
params: vec![("x".to_string(), None)],
body: Arc::new(make_lit_expr(42)),
env: Rc::new(RefCell::new(HashMap::new())),
};
let result = eval_function_call(
&closure,
&[Value::Integer(1)],
|_expr, _env| Ok(Value::Integer(42)),
|_, _| Ok(None),
);
assert!(result.is_ok());
CALL_DEPTH.with(|d| d.set(0));
}
#[test]
fn test_eval_function_call_depth_exceeded_r130() {
CALL_DEPTH.with(|d| d.set(100));
MAX_DEPTH.with(|m| m.set(100));
let closure = Value::Closure {
params: vec![],
body: Arc::new(make_unit_expr()),
env: Rc::new(RefCell::new(HashMap::new())),
};
let result = eval_function_call(&closure, &[], |_, _| Ok(Value::Nil), |_, _| Ok(None));
assert!(result.is_err());
CALL_DEPTH.with(|d| d.set(0));
MAX_DEPTH.with(|m| m.set(1000));
}
#[test]
fn test_bind_parameter_identifier_r130() {
let pattern = Pattern::Identifier("x".to_string());
let value = Value::Integer(42);
let mut env = HashMap::new();
let result = bind_parameter(&pattern, &value, &mut env);
assert!(result.is_ok());
assert_eq!(env.get("x"), Some(&Value::Integer(42)));
}
#[test]
fn test_bind_parameter_wildcard_r130() {
let pattern = Pattern::Wildcard;
let value = Value::Integer(42);
let mut env = HashMap::new();
let result = bind_parameter(&pattern, &value, &mut env);
assert!(result.is_ok());
assert!(env.is_empty());
}
#[test]
fn test_bind_parameter_tuple_r130() {
let pattern = Pattern::Tuple(vec![
Pattern::Identifier("a".to_string()),
Pattern::Identifier("b".to_string()),
]);
let value = Value::Tuple(Arc::new([Value::Integer(1), Value::Integer(2)]));
let mut env = HashMap::new();
let result = bind_parameter(&pattern, &value, &mut env);
assert!(result.is_ok());
assert_eq!(env.get("a"), Some(&Value::Integer(1)));
assert_eq!(env.get("b"), Some(&Value::Integer(2)));
}
#[test]
fn test_bind_parameter_literal_match_r130() {
let pattern = Pattern::Literal(Literal::Integer(42, None));
let value = Value::Integer(42);
let mut env = HashMap::new();
let result = bind_parameter(&pattern, &value, &mut env);
assert!(result.is_ok());
}
#[test]
fn test_bind_parameter_literal_no_match_r130() {
let pattern = Pattern::Literal(Literal::Integer(42, None));
let value = Value::Integer(99);
let mut env = HashMap::new();
let result = bind_parameter(&pattern, &value, &mut env);
assert!(result.is_err());
}
#[test]
fn test_bind_parameter_nested_tuple_r130() {
let pattern = Pattern::Tuple(vec![
Pattern::Identifier("a".to_string()),
Pattern::Tuple(vec![
Pattern::Identifier("b".to_string()),
Pattern::Identifier("c".to_string()),
]),
]);
let value = Value::Tuple(Arc::new([
Value::Integer(1),
Value::Tuple(Arc::new([Value::Integer(2), Value::Integer(3)])),
]));
let mut env = HashMap::new();
let result = bind_parameter(&pattern, &value, &mut env);
assert!(result.is_ok());
assert_eq!(env.get("a"), Some(&Value::Integer(1)));
assert_eq!(env.get("b"), Some(&Value::Integer(2)));
assert_eq!(env.get("c"), Some(&Value::Integer(3)));
}
#[test]
fn test_bind_parameter_multiple_r130() {
let mut env = HashMap::new();
bind_parameter(
&Pattern::Identifier("a".to_string()),
&Value::Integer(1),
&mut env,
)
.unwrap();
bind_parameter(
&Pattern::Identifier("b".to_string()),
&Value::Float(2.5),
&mut env,
)
.unwrap();
bind_parameter(
&Pattern::Identifier("c".to_string()),
&Value::String(Arc::from("test")),
&mut env,
)
.unwrap();
assert_eq!(env.len(), 3);
assert_eq!(env.get("a"), Some(&Value::Integer(1)));
assert_eq!(env.get("b"), Some(&Value::Float(2.5)));
assert_eq!(env.get("c"), Some(&Value::String(Arc::from("test"))));
}
#[test]
fn test_create_partial_application_r130() {
let closure = Value::Closure {
params: vec![("x".to_string(), None), ("y".to_string(), None)],
body: Arc::new(make_lit_expr(42)),
env: Rc::new(RefCell::new(HashMap::new())),
};
let result = create_partial_application(&closure, &[Value::Integer(1)]);
assert!(result.is_ok());
if let Ok(Value::Closure { params, .. }) = result {
assert_eq!(params.len(), 1);
} else {
panic!("Expected Closure result");
}
}
#[test]
fn test_create_partial_application_non_closure_r130() {
let non_closure = Value::Integer(42);
let result = create_partial_application(&non_closure, &[Value::Integer(1)]);
assert!(result.is_err());
}
}
#[cfg(test)]
mod comprehensive_coverage_tests {
use super::*;
use crate::frontend::ast::{ExprKind, Literal, Param, Span};
use std::cell::RefCell;
use std::rc::Rc;
use std::sync::Arc;
fn make_int_expr(val: i64) -> Expr {
Expr::new(
ExprKind::Literal(Literal::Integer(val, None)),
Span::new(0, 0),
)
}
fn make_unit_expr() -> Expr {
Expr::new(ExprKind::Literal(Literal::Unit), Span::new(0, 0))
}
fn make_param(name: &str) -> Param {
Param {
pattern: Pattern::Identifier(name.to_string()),
ty: crate::frontend::ast::Type {
kind: crate::frontend::ast::TypeKind::Named("Any".to_string()),
span: Span::new(0, 0),
},
span: Span::new(0, 0),
is_mutable: false,
default_value: None,
}
}
fn make_param_with_default(name: &str, default: Expr) -> Param {
Param {
pattern: Pattern::Identifier(name.to_string()),
ty: crate::frontend::ast::Type {
kind: crate::frontend::ast::TypeKind::Named("Any".to_string()),
span: Span::new(0, 0),
},
span: Span::new(0, 0),
is_mutable: false,
default_value: Some(Box::new(default)),
}
}
#[test]
fn test_eval_function_def_basic() {
let params = vec![make_param("x")];
let body = make_int_expr(42);
let result = eval_function_def("test_fn", ¶ms, &body, HashMap::new);
assert!(result.is_ok());
if let Ok(Value::Closure {
params: closure_params,
..
}) = result
{
assert_eq!(closure_params.len(), 1);
assert_eq!(closure_params[0].0, "x");
assert!(closure_params[0].1.is_none());
} else {
panic!("Expected Closure");
}
}
#[test]
fn test_eval_function_def_multiple_params() {
let params = vec![make_param("a"), make_param("b"), make_param("c")];
let body = make_unit_expr();
let result = eval_function_def("multi_param_fn", ¶ms, &body, HashMap::new);
assert!(result.is_ok());
if let Ok(Value::Closure {
params: closure_params,
..
}) = result
{
assert_eq!(closure_params.len(), 3);
assert_eq!(closure_params[0].0, "a");
assert_eq!(closure_params[1].0, "b");
assert_eq!(closure_params[2].0, "c");
} else {
panic!("Expected Closure");
}
}
#[test]
fn test_eval_function_def_with_default_params() {
let params = vec![
make_param("x"),
make_param_with_default("y", make_int_expr(10)),
];
let body = make_int_expr(0);
let result = eval_function_def("fn_with_defaults", ¶ms, &body, HashMap::new);
assert!(result.is_ok());
if let Ok(Value::Closure {
params: closure_params,
..
}) = result
{
assert_eq!(closure_params.len(), 2);
assert!(closure_params[0].1.is_none()); assert!(closure_params[1].1.is_some()); } else {
panic!("Expected Closure");
}
}
#[test]
fn test_eval_function_def_captures_environment() {
let params = vec![make_param("x")];
let body = make_unit_expr();
let mut captured = HashMap::new();
captured.insert("outer_var".to_string(), Value::Integer(100));
let result = eval_function_def("capturing_fn", ¶ms, &body, || captured.clone());
assert!(result.is_ok());
if let Ok(Value::Closure { env, .. }) = result {
assert!(env.borrow().contains_key("outer_var"));
} else {
panic!("Expected Closure");
}
}
#[test]
fn test_eval_function_def_no_params() {
let params = vec![];
let body = make_int_expr(42);
let result = eval_function_def("no_params_fn", ¶ms, &body, HashMap::new);
assert!(result.is_ok());
if let Ok(Value::Closure {
params: closure_params,
..
}) = result
{
assert_eq!(closure_params.len(), 0);
} else {
panic!("Expected Closure");
}
}
#[test]
fn test_eval_function_def_with_wildcard_pattern() {
let param = Param {
pattern: Pattern::Wildcard,
ty: crate::frontend::ast::Type {
kind: crate::frontend::ast::TypeKind::Named("Any".to_string()),
span: Span::new(0, 0),
},
span: Span::new(0, 0),
is_mutable: false,
default_value: None,
};
let params = vec![param];
let body = make_unit_expr();
let result = eval_function_def("wildcard_fn", ¶ms, &body, HashMap::new);
assert!(result.is_ok());
if let Ok(Value::Closure {
params: closure_params,
..
}) = result
{
assert_eq!(closure_params.len(), 1);
assert_eq!(closure_params[0].0, "_");
} else {
panic!("Expected Closure");
}
}
#[test]
fn test_eval_lambda_basic() {
let params = vec![make_param("x")];
let body = make_int_expr(42);
let result = eval_lambda(¶ms, &body, HashMap::new);
assert!(result.is_ok());
if let Ok(Value::Closure {
params: closure_params,
..
}) = result
{
assert_eq!(closure_params.len(), 1);
} else {
panic!("Expected Closure");
}
}
#[test]
fn test_eval_lambda_multiple_params() {
let params = vec![make_param("a"), make_param("b")];
let body = make_unit_expr();
let result = eval_lambda(¶ms, &body, HashMap::new);
assert!(result.is_ok());
if let Ok(Value::Closure {
params: closure_params,
..
}) = result
{
assert_eq!(closure_params.len(), 2);
} else {
panic!("Expected Closure");
}
}
#[test]
fn test_eval_lambda_with_default_params() {
let params = vec![
make_param("x"),
make_param_with_default("y", make_int_expr(5)),
];
let body = make_int_expr(0);
let result = eval_lambda(¶ms, &body, HashMap::new);
assert!(result.is_ok());
if let Ok(Value::Closure {
params: closure_params,
..
}) = result
{
assert_eq!(closure_params.len(), 2);
assert!(closure_params[0].1.is_none());
assert!(closure_params[1].1.is_some());
} else {
panic!("Expected Closure");
}
}
#[test]
fn test_eval_lambda_captures_environment() {
let params = vec![make_param("x")];
let body = make_unit_expr();
let mut captured = HashMap::new();
captured.insert(
"captured_val".to_string(),
Value::String(Arc::from("hello")),
);
let result = eval_lambda(¶ms, &body, || captured.clone());
assert!(result.is_ok());
if let Ok(Value::Closure { env, .. }) = result {
assert!(env.borrow().contains_key("captured_val"));
} else {
panic!("Expected Closure");
}
}
#[test]
fn test_eval_lambda_empty_params() {
let params = vec![];
let body = make_int_expr(99);
let result = eval_lambda(¶ms, &body, HashMap::new);
assert!(result.is_ok());
if let Ok(Value::Closure {
params: closure_params,
..
}) = result
{
assert_eq!(closure_params.len(), 0);
} else {
panic!("Expected Closure");
}
}
#[test]
fn test_eval_lambda_with_wildcard_pattern() {
let param = Param {
pattern: Pattern::Wildcard,
ty: crate::frontend::ast::Type {
kind: crate::frontend::ast::TypeKind::Named("Any".to_string()),
span: Span::new(0, 0),
},
span: Span::new(0, 0),
is_mutable: false,
default_value: None,
};
let params = vec![param];
let body = make_unit_expr();
let result = eval_lambda(¶ms, &body, HashMap::new);
assert!(result.is_ok());
if let Ok(Value::Closure {
params: closure_params,
..
}) = result
{
assert_eq!(closure_params[0].0, "_");
} else {
panic!("Expected Closure");
}
}
#[test]
fn test_eval_function_call_string_value_error() {
let non_function = Value::String(Arc::from("not a function"));
let result = eval_function_call(&non_function, &[], |_, _| Ok(Value::Nil), |_, _| Ok(None));
assert!(result.is_err());
if let Err(InterpreterError::TypeError(msg)) = result {
assert!(msg.contains("Cannot call non-function"));
}
}
#[test]
fn test_eval_function_call_array_error() {
let non_function = Value::from_array(vec![Value::Integer(1), Value::Integer(2)]);
let result = eval_function_call(&non_function, &[], |_, _| Ok(Value::Nil), |_, _| Ok(None));
assert!(result.is_err());
}
#[test]
fn test_eval_function_call_nil_error() {
let non_function = Value::Nil;
let result = eval_function_call(&non_function, &[], |_, _| Ok(Value::Nil), |_, _| Ok(None));
assert!(result.is_err());
}
#[test]
fn test_eval_function_call_closure_returns_value() {
CALL_DEPTH.with(|d| d.set(0));
MAX_DEPTH.with(|m| m.set(1000));
let closure = Value::Closure {
params: vec![("x".to_string(), None)],
body: Arc::new(make_int_expr(100)),
env: Rc::new(RefCell::new(HashMap::new())),
};
let result = eval_function_call(
&closure,
&[Value::Integer(5)],
|_body, _env| Ok(Value::Integer(100)),
|_, _| Ok(None),
);
assert!(result.is_ok());
assert_eq!(result.unwrap(), Value::Integer(100));
CALL_DEPTH.with(|d| d.set(0));
}
#[test]
fn test_eval_function_call_wrong_arg_count_too_few() {
CALL_DEPTH.with(|d| d.set(0));
MAX_DEPTH.with(|m| m.set(1000));
let closure = Value::Closure {
params: vec![("a".to_string(), None), ("b".to_string(), None)],
body: Arc::new(make_unit_expr()),
env: Rc::new(RefCell::new(HashMap::new())),
};
let result = eval_function_call(&closure, &[], |_, _| Ok(Value::Nil), |_, _| Ok(None));
assert!(result.is_err());
CALL_DEPTH.with(|d| d.set(0));
}
#[test]
fn test_eval_function_call_wrong_arg_count_too_many() {
CALL_DEPTH.with(|d| d.set(0));
MAX_DEPTH.with(|m| m.set(1000));
let closure = Value::Closure {
params: vec![("x".to_string(), None)],
body: Arc::new(make_unit_expr()),
env: Rc::new(RefCell::new(HashMap::new())),
};
let result = eval_function_call(
&closure,
&[Value::Integer(1), Value::Integer(2), Value::Integer(3)],
|_, _| Ok(Value::Nil),
|_, _| Ok(None),
);
assert!(result.is_err());
CALL_DEPTH.with(|d| d.set(0));
}
#[test]
fn test_eval_function_call_handles_return_value() {
CALL_DEPTH.with(|d| d.set(0));
MAX_DEPTH.with(|m| m.set(1000));
let closure = Value::Closure {
params: vec![],
body: Arc::new(make_unit_expr()),
env: Rc::new(RefCell::new(HashMap::new())),
};
let result = eval_function_call(
&closure,
&[],
|_, _| Err(InterpreterError::Return(Value::Integer(42))),
|_, _| Ok(None),
);
assert!(result.is_ok());
assert_eq!(result.unwrap(), Value::Integer(42));
CALL_DEPTH.with(|d| d.set(0));
}
#[test]
fn test_eval_function_call_with_default_params() {
CALL_DEPTH.with(|d| d.set(0));
MAX_DEPTH.with(|m| m.set(1000));
let closure = Value::Closure {
params: vec![
("x".to_string(), None), ("y".to_string(), Some(Arc::new(make_int_expr(10)))), ],
body: Arc::new(make_unit_expr()),
env: Rc::new(RefCell::new(HashMap::new())),
};
let result = eval_function_call(
&closure,
&[Value::Integer(5)],
|_, env| {
assert_eq!(env.get("x"), Some(&Value::Integer(5)));
Ok(Value::Integer(15))
},
|_, _| Ok(None),
);
assert!(result.is_ok());
CALL_DEPTH.with(|d| d.set(0));
}
#[test]
fn test_eval_method_call_value_success() {
let receiver = Value::Integer(42);
let result = eval_method_call_value(
&receiver,
"to_string",
&[],
|_, _| Ok(Value::Nil),
|_, _, _, _| Ok(Value::String(Arc::from("42"))),
);
assert!(result.is_ok());
assert_eq!(result.unwrap(), Value::String(Arc::from("42")));
}
#[test]
fn test_eval_method_call_value_fallback_to_function() {
let receiver = Value::Integer(42);
let result = eval_method_call_value(
&receiver,
"nonexistent_method",
&[],
|_, _| Ok(Value::Nil),
|_, _, _, _| {
Err(InterpreterError::RuntimeError(
"Method not found".to_string(),
))
},
);
assert!(result.is_err());
}
#[test]
fn test_eval_method_call_value_other_error() {
let receiver = Value::Integer(42);
let result = eval_method_call_value(
&receiver,
"test_method",
&[],
|_, _| Ok(Value::Nil),
|_, _, _, _| Err(InterpreterError::TypeError("Type error".to_string())),
);
assert!(matches!(result, Err(InterpreterError::TypeError(_))));
}
#[test]
fn test_eval_method_call_value_with_args() {
let receiver = Value::String(Arc::from("hello"));
let result = eval_method_call_value(
&receiver,
"replace",
&[Value::String(Arc::from("l")), Value::String(Arc::from("L"))],
|_, _| Ok(Value::Nil),
|_, method, args, _| {
assert_eq!(method, "replace");
assert_eq!(args.len(), 2);
Ok(Value::String(Arc::from("heLLo")))
},
);
assert!(result.is_ok());
}
#[test]
fn test_create_partial_application_with_captured_env() {
let mut env = HashMap::new();
env.insert("captured".to_string(), Value::Integer(100));
let closure = Value::Closure {
params: vec![("x".to_string(), None), ("y".to_string(), None)],
body: Arc::new(make_int_expr(0)),
env: Rc::new(RefCell::new(env)),
};
let result = create_partial_application(&closure, &[Value::Integer(5)]);
assert!(result.is_ok());
if let Ok(Value::Closure {
env: new_env,
params,
..
}) = result
{
assert_eq!(params.len(), 1); assert!(new_env.borrow().contains_key("x"));
assert_eq!(new_env.borrow().get("x"), Some(&Value::Integer(5)));
assert!(new_env.borrow().contains_key("captured"));
} else {
panic!("Expected Closure");
}
}
#[test]
fn test_create_partial_application_exact_args_error() {
let closure = Value::Closure {
params: vec![("x".to_string(), None)],
body: Arc::new(make_unit_expr()),
env: Rc::new(RefCell::new(HashMap::new())),
};
let result = create_partial_application(&closure, &[Value::Integer(1)]);
assert!(result.is_err());
if let Err(InterpreterError::RuntimeError(msg)) = result {
assert!(msg.contains("Cannot partially apply"));
}
}
#[test]
fn test_create_partial_application_multiple_args() {
let closure = Value::Closure {
params: vec![
("a".to_string(), None),
("b".to_string(), None),
("c".to_string(), None),
],
body: Arc::new(make_unit_expr()),
env: Rc::new(RefCell::new(HashMap::new())),
};
let result = create_partial_application(&closure, &[Value::Integer(1), Value::Integer(2)]);
assert!(result.is_ok());
if let Ok(Value::Closure { params, .. }) = result {
assert_eq!(params.len(), 1); assert_eq!(params[0].0, "c");
} else {
panic!("Expected Closure");
}
}
#[test]
fn test_create_partial_application_bool_value() {
let result = create_partial_application(&Value::Bool(true), &[Value::Integer(1)]);
assert!(matches!(result, Err(InterpreterError::TypeError(_))));
}
#[test]
fn test_create_partial_application_float_value() {
let result = create_partial_application(&Value::Float(3.14), &[Value::Integer(1)]);
assert!(matches!(result, Err(InterpreterError::TypeError(_))));
}
#[test]
fn test_eval_function_composition_both_single_param() {
let f = Value::Closure {
params: vec![("x".to_string(), None)],
body: Arc::new(make_int_expr(1)),
env: Rc::new(RefCell::new(HashMap::new())),
};
let g = Value::Closure {
params: vec![("y".to_string(), None)],
body: Arc::new(make_int_expr(2)),
env: Rc::new(RefCell::new(HashMap::new())),
};
let result = eval_function_composition(&f, &g);
assert!(result.is_ok());
if let Ok(Value::Closure { params, .. }) = result {
assert_eq!(params.len(), 1);
} else {
panic!("Expected Closure");
}
}
#[test]
fn test_eval_function_composition_g_zero_params() {
let f = Value::Closure {
params: vec![("x".to_string(), None)],
body: Arc::new(make_int_expr(1)),
env: Rc::new(RefCell::new(HashMap::new())),
};
let g = Value::Closure {
params: vec![],
body: Arc::new(make_int_expr(2)),
env: Rc::new(RefCell::new(HashMap::new())),
};
let result = eval_function_composition(&f, &g);
assert!(result.is_err());
if let Err(InterpreterError::RuntimeError(msg)) = result {
assert!(msg.contains("second function"));
}
}
#[test]
fn test_eval_function_composition_f_zero_params() {
let f = Value::Closure {
params: vec![],
body: Arc::new(make_int_expr(1)),
env: Rc::new(RefCell::new(HashMap::new())),
};
let g = Value::Closure {
params: vec![("x".to_string(), None)],
body: Arc::new(make_int_expr(2)),
env: Rc::new(RefCell::new(HashMap::new())),
};
let result = eval_function_composition(&f, &g);
assert!(result.is_err());
if let Err(InterpreterError::RuntimeError(msg)) = result {
assert!(msg.contains("first function"));
}
}
#[test]
fn test_eval_function_composition_first_non_closure() {
let f = Value::Integer(42);
let g = Value::Closure {
params: vec![("x".to_string(), None)],
body: Arc::new(make_int_expr(2)),
env: Rc::new(RefCell::new(HashMap::new())),
};
let result = eval_function_composition(&f, &g);
assert!(matches!(result, Err(InterpreterError::TypeError(_))));
}
#[test]
fn test_eval_function_composition_second_non_closure() {
let f = Value::Closure {
params: vec![("x".to_string(), None)],
body: Arc::new(make_int_expr(1)),
env: Rc::new(RefCell::new(HashMap::new())),
};
let g = Value::String(Arc::from("not a function"));
let result = eval_function_composition(&f, &g);
assert!(matches!(result, Err(InterpreterError::TypeError(_))));
}
#[test]
fn test_eval_function_composition_both_non_closure() {
let f = Value::Bool(true);
let g = Value::Float(3.14);
let result = eval_function_composition(&f, &g);
assert!(matches!(result, Err(InterpreterError::TypeError(_))));
}
#[test]
fn test_create_composition_closure_basic() {
let f = Closure::new(
vec![Pattern::Identifier("x".to_string())],
make_int_expr(1),
HashMap::new(),
);
let g = Closure::new(
vec![Pattern::Identifier("y".to_string())],
make_int_expr(2),
HashMap::new(),
);
let result = create_composition_closure(&f, &g);
assert!(result.is_ok());
let composed = result.unwrap();
assert_eq!(composed.params.len(), 1);
}
#[test]
fn test_create_composition_closure_merges_envs() {
let mut f_env = HashMap::new();
f_env.insert("f_var".to_string(), Value::Integer(1));
let mut g_env = HashMap::new();
g_env.insert("g_var".to_string(), Value::Integer(2));
let f = Closure::new(
vec![Pattern::Identifier("x".to_string())],
make_int_expr(1),
f_env,
);
let g = Closure::new(
vec![Pattern::Identifier("y".to_string())],
make_int_expr(2),
g_env,
);
let result = create_composition_closure(&f, &g);
assert!(result.is_ok());
let composed = result.unwrap();
assert!(composed.captured_env.contains_key("f_var"));
assert!(composed.captured_env.contains_key("g_var"));
}
#[test]
fn test_eval_closure_call_basic() {
let closure = Closure::new(
vec![Pattern::Identifier("x".to_string())],
make_int_expr(42),
HashMap::new(),
);
let result = eval_closure_call(&closure, &[Value::Integer(5)], |_, env| {
assert!(env.contains_key("x"));
assert_eq!(env.get("x"), Some(&Value::Integer(5)));
Ok(Value::Integer(47))
});
assert!(result.is_ok());
assert_eq!(result.unwrap(), Value::Integer(47));
}
#[test]
fn test_eval_closure_call_wrong_arg_count() {
let closure = Closure::new(
vec![
Pattern::Identifier("a".to_string()),
Pattern::Identifier("b".to_string()),
],
make_unit_expr(),
HashMap::new(),
);
let result = eval_closure_call(&closure, &[Value::Integer(1)], |_, _| Ok(Value::Nil));
assert!(result.is_err());
if let Err(InterpreterError::RuntimeError(msg)) = result {
assert!(msg.contains("expects"));
assert!(msg.contains("arguments"));
}
}
#[test]
fn test_eval_closure_call_named_recursive() {
let closure = Closure::named(
vec![Pattern::Identifier("n".to_string())],
make_int_expr(1),
HashMap::new(),
"factorial".to_string(),
);
let result = eval_closure_call(&closure, &[Value::Integer(5)], |_, env| {
assert!(env.contains_key("factorial"));
Ok(Value::Integer(120))
});
assert!(result.is_ok());
}
#[test]
fn test_eval_closure_call_handles_return() {
let closure = Closure::new(
vec![Pattern::Identifier("x".to_string())],
make_unit_expr(),
HashMap::new(),
);
let result = eval_closure_call(&closure, &[Value::Integer(1)], |_, _| {
Err(InterpreterError::Return(Value::String(Arc::from(
"early return",
))))
});
assert!(result.is_ok());
assert_eq!(result.unwrap(), Value::String(Arc::from("early return")));
}
#[test]
fn test_eval_closure_call_propagates_error() {
let closure = Closure::new(
vec![Pattern::Identifier("x".to_string())],
make_unit_expr(),
HashMap::new(),
);
let result = eval_closure_call(&closure, &[Value::Integer(1)], |_, _| {
Err(InterpreterError::TypeError("test error".to_string()))
});
assert!(matches!(result, Err(InterpreterError::TypeError(_))));
}
#[test]
fn test_eval_closure_call_with_wildcard_pattern() {
let closure = Closure::new(vec![Pattern::Wildcard], make_int_expr(99), HashMap::new());
let result = eval_closure_call(&closure, &[Value::Integer(1)], |_, _| {
Ok(Value::Integer(99))
});
assert!(result.is_ok());
}
#[test]
fn test_eval_closure_call_with_tuple_pattern() {
let closure = Closure::new(
vec![Pattern::Tuple(vec![
Pattern::Identifier("a".to_string()),
Pattern::Identifier("b".to_string()),
])],
make_unit_expr(),
HashMap::new(),
);
let tuple_arg = Value::Tuple(Arc::new([Value::Integer(1), Value::Integer(2)]));
let result = eval_closure_call(&closure, &[tuple_arg], |_, env| {
assert_eq!(env.get("a"), Some(&Value::Integer(1)));
assert_eq!(env.get("b"), Some(&Value::Integer(2)));
Ok(Value::Integer(3))
});
assert!(result.is_ok());
}
#[test]
fn test_eval_closure_call_pattern_mismatch() {
let closure = Closure::new(
vec![Pattern::Literal(Literal::Integer(42, None))],
make_unit_expr(),
HashMap::new(),
);
let result = eval_closure_call(&closure, &[Value::Integer(99)], |_, _| Ok(Value::Nil));
assert!(result.is_err());
if let Err(InterpreterError::RuntimeError(msg)) = result {
assert!(msg.contains("pattern does not match"));
}
}
#[test]
fn test_eval_closure_call_with_captured_env() {
let mut env = HashMap::new();
env.insert("outer".to_string(), Value::Integer(100));
let closure = Closure::new(
vec![Pattern::Identifier("x".to_string())],
make_unit_expr(),
env,
);
let result = eval_closure_call(&closure, &[Value::Integer(5)], |_, env| {
assert_eq!(env.get("outer"), Some(&Value::Integer(100)));
assert_eq!(env.get("x"), Some(&Value::Integer(5)));
Ok(Value::Integer(105))
});
assert!(result.is_ok());
}
#[test]
fn test_is_callable_with_tuple() {
let tuple = Value::Tuple(Arc::new([Value::Integer(1)]));
assert!(!is_callable(&tuple));
}
#[test]
fn test_is_callable_with_object() {
let obj = Value::Object(Arc::new(HashMap::new()));
assert!(!is_callable(&obj));
}
#[test]
fn test_get_arity_with_tuple() {
let tuple = Value::Tuple(Arc::new([Value::Integer(1), Value::Integer(2)]));
assert!(get_arity(&tuple).is_err());
}
#[test]
fn test_get_arity_many_params() {
let closure = Value::Closure {
params: vec![
("a".to_string(), None),
("b".to_string(), None),
("c".to_string(), None),
("d".to_string(), None),
("e".to_string(), None),
],
body: Arc::new(make_unit_expr()),
env: Rc::new(RefCell::new(HashMap::new())),
};
assert_eq!(get_arity(&closure).unwrap(), 5);
}
#[test]
fn test_bind_parameter_or_pattern() {
let pattern = Pattern::Identifier("x".to_string());
let value = Value::Float(3.14);
let mut env = HashMap::new();
let result = bind_parameter(&pattern, &value, &mut env);
assert!(result.is_ok());
assert_eq!(env.get("x"), Some(&Value::Float(3.14)));
}
#[test]
fn test_bind_parameter_list_pattern() {
let pattern = Pattern::List(vec![
Pattern::Identifier("first".to_string()),
Pattern::Identifier("second".to_string()),
]);
let value = Value::from_array(vec![Value::Integer(1), Value::Integer(2)]);
let mut env = HashMap::new();
let result = bind_parameter(&pattern, &value, &mut env);
let _ = result;
}
#[test]
fn test_closure_clone() {
let closure = Closure::new(
vec![Pattern::Identifier("x".to_string())],
make_int_expr(42),
HashMap::new(),
);
let cloned = closure.clone();
assert_eq!(cloned.params.len(), closure.params.len());
assert!(cloned.name.is_none());
}
#[test]
fn test_closure_debug() {
let closure = Closure::new(
vec![Pattern::Identifier("x".to_string())],
make_int_expr(42),
HashMap::new(),
);
let _ = format!("{:?}", closure);
}
}