#![allow(clippy::unused_self)]
#![allow(clippy::only_used_in_recursion)]
#![allow(clippy::uninlined_format_args)]
#![allow(clippy::cast_precision_loss)]
#![allow(clippy::expect_used)]
#![allow(clippy::cast_possible_truncation)]
use super::eval_func;
use crate::frontend::ast::{Expr, ExprKind};
use crate::frontend::Param;
use crate::runtime::interpreter::Interpreter;
use crate::runtime::{InterpreterError, Value};
use std::collections::HashMap;
use std::sync::Arc;
impl Interpreter {
pub(crate) fn eval_function(
&mut self,
name: &str,
params: &[Param],
body: &Expr,
) -> Result<Value, InterpreterError> {
let params_with_defaults: Vec<(String, Option<Arc<Expr>>)> = params
.iter()
.map(|p| {
(
p.name(),
p.default_value
.clone()
.map(|expr| Arc::new((*expr).clone())),
)
})
.collect();
let closure = Value::Closure {
params: params_with_defaults,
body: Arc::new(body.clone()),
env: self.current_env().clone(), };
self.env_set(name.to_string(), closure.clone());
Ok(closure)
}
pub(crate) fn eval_lambda(
&mut self,
params: &[Param],
body: &Expr,
) -> Result<Value, InterpreterError> {
eval_func::eval_lambda(params, body, self.current_env())
}
pub(crate) fn reorder_named_args<'a>(
&self,
args: &'a [Expr],
param_names: &[String],
) -> Vec<&'a Expr> {
let mut named_args: Vec<(Option<String>, &Expr)> = Vec::new();
for arg in args {
if let ExprKind::Assign { target, value } = &arg.kind {
if let ExprKind::Identifier(name) = &target.kind {
named_args.push((Some(name.clone()), value.as_ref()));
} else {
named_args.push((None, arg));
}
} else {
named_args.push((None, arg));
}
}
let has_named = named_args.iter().any(|(name, _)| name.is_some());
if !has_named {
return args.iter().collect();
}
let mut result: Vec<Option<&Expr>> = vec![None; param_names.len()];
let mut positional_idx = 0;
for (name, expr) in &named_args {
if let Some(param_name) = name {
if let Some(pos) = param_names.iter().position(|p| p == param_name) {
result[pos] = Some(*expr);
} else {
if positional_idx < result.len() {
while positional_idx < result.len() && result[positional_idx].is_some() {
positional_idx += 1;
}
if positional_idx < result.len() {
result[positional_idx] = Some(*expr);
}
}
}
} else {
while positional_idx < result.len() && result[positional_idx].is_some() {
positional_idx += 1;
}
if positional_idx < result.len() {
result[positional_idx] = Some(*expr);
positional_idx += 1;
}
}
}
let provided_count = named_args.len();
result.into_iter().take(provided_count).flatten().collect()
}
pub(crate) fn eval_function_call(
&mut self,
func: &Expr,
args: &[Expr],
) -> Result<Value, InterpreterError> {
if let ExprKind::FieldAccess { object, field } = &func.kind {
if let ExprKind::Identifier(type_name) = &object.kind {
if type_name == "Box" && field == "new" {
if args.len() != 1 {
return Err(InterpreterError::RuntimeError(format!(
"Box::new() requires exactly 1 argument, got {}",
args.len()
)));
}
return self.eval_expr(&args[0]);
}
if type_name == "Vec" && field == "new" {
if !args.is_empty() {
return Err(InterpreterError::RuntimeError(format!(
"Vec::new() takes no arguments, got {}",
args.len()
)));
}
return Ok(Value::Array(Arc::from([])));
}
if type_name == "JSON" {
if field == "parse" {
if args.len() != 1 {
return Err(InterpreterError::RuntimeError(format!(
"JSON.parse() requires exactly 1 argument, got {}",
args.len()
)));
}
let json_str_val = self.eval_expr(&args[0])?;
let json_str = json_str_val.to_string();
return self.json_parse(&json_str);
} else if field == "stringify" {
if args.len() != 1 {
return Err(InterpreterError::RuntimeError(format!(
"JSON.stringify() requires exactly 1 argument, got {}",
args.len()
)));
}
let value = self.eval_expr(&args[0])?;
return self.json_stringify(&value);
}
}
if type_name == "File" && field == "open" {
if args.len() != 1 {
return Err(InterpreterError::RuntimeError(format!(
"File.open() requires exactly 1 argument, got {}",
args.len()
)));
}
let path_val = self.eval_expr(&args[0])?;
return crate::runtime::eval_builtin::eval_builtin_function(
"File_open",
&[path_val],
)?
.ok_or_else(|| {
InterpreterError::RuntimeError("File_open builtin not found".to_string())
});
}
let qualified_method = format!("{}::{}", type_name, field);
if let Ok(method_value) = self.lookup_variable(&qualified_method) {
let arg_vals: Result<Vec<Value>, InterpreterError> =
args.iter().map(|arg| self.eval_expr(arg)).collect();
let arg_vals = arg_vals?;
return self.call_function(method_value, &arg_vals);
}
}
}
let has_named_args = args.iter().any(|arg| {
matches!(
&arg.kind,
ExprKind::Assign { target, .. } if matches!(&target.kind, ExprKind::Identifier(_))
)
});
let reordered_args: Vec<&Expr> = if has_named_args {
if let ExprKind::Identifier(func_name) = &func.kind {
if let Ok(func_val) = self.lookup_variable(func_name) {
if let Value::Closure { params, .. } = &func_val {
let param_names: Vec<String> =
params.iter().map(|(name, _)| name.clone()).collect();
self.reorder_named_args(args, ¶m_names)
} else {
args.iter().collect()
}
} else {
args.iter().collect()
}
} else {
args.iter().collect()
}
} else {
args.iter().collect()
};
let arg_vals: Vec<Value> = reordered_args
.iter()
.map(|arg| self.eval_expr(arg))
.collect::<Result<Vec<_>, _>>()?;
if let ExprKind::Identifier(name) = &func.kind {
let builtin_name = format!("__builtin_{}__", name);
match crate::runtime::eval_builtin::eval_builtin_function(&builtin_name, &arg_vals) {
Ok(Some(result)) => return Ok(result),
Ok(None) => {} Err(e) => return Err(e), }
}
let func_val_result = self.eval_expr(func);
let func_val = match func_val_result {
Ok(val) => val,
Err(InterpreterError::RuntimeError(msg)) if msg.starts_with("Undefined variable:") => {
if let ExprKind::Identifier(name) = &func.kind {
let mut message = HashMap::new();
message.insert(
"__type".to_string(),
Value::from_string("Message".to_string()),
);
message.insert("type".to_string(), Value::from_string(name.clone()));
message.insert("data".to_string(), Value::Array(Arc::from(arg_vals)));
return Ok(Value::Object(Arc::new(message)));
}
return Err(InterpreterError::RuntimeError(msg));
}
Err(e) => return Err(e),
};
if let Value::EnumVariant {
enum_name,
variant_name,
data: _,
} = func_val
{
return Ok(Value::EnumVariant {
enum_name,
variant_name,
data: Some(arg_vals),
});
}
let func_name = match &func.kind {
ExprKind::Identifier(name) => name.clone(),
_ => "anonymous".to_string(),
};
let trace_enabled = std::env::var("RUCHY_TRACE").is_ok();
if trace_enabled {
let args_str = arg_vals
.iter()
.map(|v| {
let value_str = match v {
Value::String(s) => format!("\"{}\"", s),
other => other.to_string(),
};
format!("{}: {}", value_str, v.type_name())
})
.collect::<Vec<_>>()
.join(", ");
println!("TRACE: → {}({})", func_name, args_str);
}
let result = self.call_function(func_val, &arg_vals)?;
if trace_enabled {
let result_str = match &result {
Value::String(s) => format!("\"{}\"", s),
other => other.to_string(),
};
println!(
"TRACE: ← {} = {}: {}",
func_name,
result_str,
result.type_name()
);
}
let site_id = func.span.start; self.record_function_call_feedback(site_id, &func_name, &arg_vals, &result);
Ok(result)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::frontend::ast::{Literal, Pattern, Span, Type, TypeKind};
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
fn make_interpreter() -> Interpreter {
Interpreter::new()
}
fn make_expr(kind: ExprKind) -> Expr {
Expr {
kind,
span: Span::default(),
attributes: vec![],
leading_comments: vec![],
trailing_comment: None,
}
}
fn make_type(name: &str) -> Type {
Type {
kind: TypeKind::Named(name.to_string()),
span: Span::default(),
}
}
fn make_param(name: &str) -> Param {
Param {
pattern: Pattern::Identifier(name.to_string()),
ty: make_type("Any"),
span: Span::default(),
is_mutable: false,
default_value: None,
}
}
#[test]
fn test_eval_function_simple() {
let mut interp = make_interpreter();
let params = vec![make_param("x")];
let body = make_expr(ExprKind::Identifier("x".to_string()));
let result = interp.eval_function("identity", ¶ms, &body).unwrap();
assert!(matches!(result, Value::Closure { .. }));
}
#[test]
fn test_eval_function_no_params() {
let mut interp = make_interpreter();
let body = make_expr(ExprKind::Literal(Literal::Integer(42, None)));
let result = interp.eval_function("constant", &[], &body).unwrap();
assert!(matches!(result, Value::Closure { .. }));
}
#[test]
fn test_eval_function_multiple_params() {
let mut interp = make_interpreter();
let params = vec![make_param("a"), make_param("b"), make_param("c")];
let body = make_expr(ExprKind::Identifier("a".to_string()));
let result = interp.eval_function("multi", ¶ms, &body).unwrap();
if let Value::Closure { params: p, .. } = result {
assert_eq!(p.len(), 3);
} else {
panic!("Expected Closure");
}
}
#[test]
fn test_eval_function_registered_in_env() {
let mut interp = make_interpreter();
let params = vec![make_param("x")];
let body = make_expr(ExprKind::Identifier("x".to_string()));
interp.eval_function("my_func", ¶ms, &body).unwrap();
let lookup = interp.lookup_variable("my_func");
assert!(lookup.is_ok());
assert!(matches!(lookup.unwrap(), Value::Closure { .. }));
}
#[test]
fn test_eval_lambda_simple() {
let mut interp = make_interpreter();
let params = vec![make_param("x")];
let body = make_expr(ExprKind::Identifier("x".to_string()));
let result = interp.eval_lambda(¶ms, &body).unwrap();
assert!(matches!(result, Value::Closure { .. }));
}
#[test]
fn test_eval_lambda_no_params() {
let mut interp = make_interpreter();
let body = make_expr(ExprKind::Literal(Literal::Integer(100, None)));
let result = interp.eval_lambda(&[], &body).unwrap();
assert!(matches!(result, Value::Closure { .. }));
}
#[test]
fn test_reorder_named_args_no_named() {
let interp = make_interpreter();
let args = vec![
make_expr(ExprKind::Literal(Literal::Integer(1, None))),
make_expr(ExprKind::Literal(Literal::Integer(2, None))),
];
let param_names = vec!["a".to_string(), "b".to_string()];
let result = interp.reorder_named_args(&args, ¶m_names);
assert_eq!(result.len(), 2);
}
#[test]
fn test_reorder_named_args_with_named() {
let interp = make_interpreter();
let args = vec![
make_expr(ExprKind::Assign {
target: Box::new(make_expr(ExprKind::Identifier("b".to_string()))),
value: Box::new(make_expr(ExprKind::Literal(Literal::Integer(2, None)))),
}),
make_expr(ExprKind::Assign {
target: Box::new(make_expr(ExprKind::Identifier("a".to_string()))),
value: Box::new(make_expr(ExprKind::Literal(Literal::Integer(1, None)))),
}),
];
let param_names = vec!["a".to_string(), "b".to_string()];
let result = interp.reorder_named_args(&args, ¶m_names);
assert_eq!(result.len(), 2);
}
#[test]
fn test_reorder_named_args_mixed() {
let interp = make_interpreter();
let args = vec![
make_expr(ExprKind::Literal(Literal::Integer(1, None))),
make_expr(ExprKind::Assign {
target: Box::new(make_expr(ExprKind::Identifier("b".to_string()))),
value: Box::new(make_expr(ExprKind::Literal(Literal::Integer(2, None)))),
}),
];
let param_names = vec!["a".to_string(), "b".to_string()];
let result = interp.reorder_named_args(&args, ¶m_names);
assert_eq!(result.len(), 2);
}
#[test]
fn test_reorder_named_args_unknown_param() {
let interp = make_interpreter();
let args = vec![make_expr(ExprKind::Assign {
target: Box::new(make_expr(ExprKind::Identifier("unknown".to_string()))),
value: Box::new(make_expr(ExprKind::Literal(Literal::Integer(1, None)))),
})];
let param_names = vec!["a".to_string(), "b".to_string()];
let result = interp.reorder_named_args(&args, ¶m_names);
assert_eq!(result.len(), 1);
}
#[test]
fn test_reorder_named_args_assign_to_non_identifier() {
let interp = make_interpreter();
let args = vec![make_expr(ExprKind::Assign {
target: Box::new(make_expr(ExprKind::IndexAccess {
object: Box::new(make_expr(ExprKind::Literal(Literal::Integer(1, None)))),
index: Box::new(make_expr(ExprKind::Literal(Literal::Integer(0, None)))),
})),
value: Box::new(make_expr(ExprKind::Literal(Literal::Integer(2, None)))),
})];
let param_names = vec!["a".to_string()];
let result = interp.reorder_named_args(&args, ¶m_names);
assert_eq!(result.len(), 1);
}
#[test]
fn test_eval_function_call_box_new() {
let mut interp = make_interpreter();
let func = make_expr(ExprKind::FieldAccess {
object: Box::new(make_expr(ExprKind::Identifier("Box".to_string()))),
field: "new".to_string(),
});
let args = vec![make_expr(ExprKind::Literal(Literal::Integer(42, None)))];
let result = interp.eval_function_call(&func, &args).unwrap();
assert_eq!(result, Value::Integer(42));
}
#[test]
fn test_eval_function_call_box_new_wrong_args() {
let mut interp = make_interpreter();
let func = make_expr(ExprKind::FieldAccess {
object: Box::new(make_expr(ExprKind::Identifier("Box".to_string()))),
field: "new".to_string(),
});
let args = vec![];
let result = interp.eval_function_call(&func, &args);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("requires exactly 1"));
}
#[test]
fn test_eval_function_call_vec_new() {
let mut interp = make_interpreter();
let func = make_expr(ExprKind::FieldAccess {
object: Box::new(make_expr(ExprKind::Identifier("Vec".to_string()))),
field: "new".to_string(),
});
let args = vec![];
let result = interp.eval_function_call(&func, &args).unwrap();
if let Value::Array(arr) = result {
assert!(arr.is_empty());
} else {
panic!("Expected Array");
}
}
#[test]
fn test_eval_function_call_vec_new_wrong_args() {
let mut interp = make_interpreter();
let func = make_expr(ExprKind::FieldAccess {
object: Box::new(make_expr(ExprKind::Identifier("Vec".to_string()))),
field: "new".to_string(),
});
let args = vec![make_expr(ExprKind::Literal(Literal::Integer(1, None)))];
let result = interp.eval_function_call(&func, &args);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("takes no arguments"));
}
#[test]
fn test_eval_function_call_json_parse() {
let interp = make_interpreter();
let result = interp.json_parse(r#"{"a": 1}"#).unwrap();
assert!(matches!(result, Value::Object(_)));
}
#[test]
fn test_eval_function_call_json_static_parse_wrong_args() {
let mut interp = make_interpreter();
let func = make_expr(ExprKind::FieldAccess {
object: Box::new(make_expr(ExprKind::Identifier("JSON".to_string()))),
field: "parse".to_string(),
});
let args = vec![
make_expr(ExprKind::Literal(Literal::String("{}".to_string()))),
make_expr(ExprKind::Literal(Literal::String("{}".to_string()))),
];
let result = interp.eval_function_call(&func, &args);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("requires exactly 1"));
}
#[test]
fn test_eval_function_call_json_parse_wrong_args() {
let mut interp = make_interpreter();
let func = make_expr(ExprKind::FieldAccess {
object: Box::new(make_expr(ExprKind::Identifier("JSON".to_string()))),
field: "parse".to_string(),
});
let args = vec![];
let result = interp.eval_function_call(&func, &args);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("requires exactly 1"));
}
#[test]
fn test_eval_function_call_json_stringify() {
let mut interp = make_interpreter();
let func = make_expr(ExprKind::FieldAccess {
object: Box::new(make_expr(ExprKind::Identifier("JSON".to_string()))),
field: "stringify".to_string(),
});
let args = vec![make_expr(ExprKind::Literal(Literal::Integer(42, None)))];
let result = interp.eval_function_call(&func, &args).unwrap();
if let Value::String(s) = result {
assert_eq!(s.as_ref(), "42");
} else {
panic!("Expected String");
}
}
#[test]
fn test_eval_function_call_json_stringify_wrong_args() {
let mut interp = make_interpreter();
let func = make_expr(ExprKind::FieldAccess {
object: Box::new(make_expr(ExprKind::Identifier("JSON".to_string()))),
field: "stringify".to_string(),
});
let args = vec![];
let result = interp.eval_function_call(&func, &args);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("requires exactly 1"));
}
#[test]
fn test_eval_function_call_file_open_wrong_args() {
let mut interp = make_interpreter();
let func = make_expr(ExprKind::FieldAccess {
object: Box::new(make_expr(ExprKind::Identifier("File".to_string()))),
field: "open".to_string(),
});
let args = vec![];
let result = interp.eval_function_call(&func, &args);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("requires exactly 1"));
}
#[test]
fn test_eval_function_call_message_constructor() {
let mut interp = make_interpreter();
let func = make_expr(ExprKind::Identifier("CustomMessage".to_string()));
let args = vec![make_expr(ExprKind::Literal(Literal::Integer(42, None)))];
let result = interp.eval_function_call(&func, &args).unwrap();
if let Value::Object(obj) = result {
assert_eq!(
obj.get("__type"),
Some(&Value::from_string("Message".to_string()))
);
assert_eq!(
obj.get("type"),
Some(&Value::from_string("CustomMessage".to_string()))
);
} else {
panic!("Expected Object");
}
}
#[test]
fn test_eval_function_call_closure() {
let mut interp = make_interpreter();
let params = vec![make_param("x")];
let body = make_expr(ExprKind::Identifier("x".to_string()));
interp.eval_function("identity", ¶ms, &body).unwrap();
let func = make_expr(ExprKind::Identifier("identity".to_string()));
let args = vec![make_expr(ExprKind::Literal(Literal::Integer(42, None)))];
let result = interp.eval_function_call(&func, &args).unwrap();
assert_eq!(result, Value::Integer(42));
}
#[test]
fn test_eval_function_call_enum_variant() {
let mut interp = make_interpreter();
interp.set_variable(
"Result_Error",
Value::EnumVariant {
enum_name: "Result".to_string(),
variant_name: "Error".to_string(),
data: None,
},
);
let func = make_expr(ExprKind::Identifier("Result_Error".to_string()));
let args = vec![make_expr(ExprKind::Literal(Literal::String(
"error message".to_string(),
)))];
let result = interp.eval_function_call(&func, &args).unwrap();
if let Value::EnumVariant {
enum_name,
variant_name,
data,
} = result
{
assert_eq!(enum_name, "Result");
assert_eq!(variant_name, "Error");
assert!(data.is_some());
} else {
panic!("Expected EnumVariant");
}
}
#[test]
fn test_eval_function_call_impl_method() {
let mut interp = make_interpreter();
let env = Rc::new(RefCell::new(HashMap::new()));
interp.set_variable(
"Point::origin",
Value::Closure {
params: vec![],
body: Arc::new(make_expr(ExprKind::Literal(Literal::Integer(0, None)))),
env,
},
);
let func = make_expr(ExprKind::FieldAccess {
object: Box::new(make_expr(ExprKind::Identifier("Point".to_string()))),
field: "origin".to_string(),
});
let args = vec![];
let result = interp.eval_function_call(&func, &args).unwrap();
assert_eq!(result, Value::Integer(0));
}
#[test]
fn test_eval_function_call_with_named_args() {
let mut interp = make_interpreter();
let params = vec![make_param("a"), make_param("b")];
let body = make_expr(ExprKind::Identifier("a".to_string()));
interp.eval_function("test_func", ¶ms, &body).unwrap();
let func = make_expr(ExprKind::Identifier("test_func".to_string()));
let args = vec![
make_expr(ExprKind::Assign {
target: Box::new(make_expr(ExprKind::Identifier("b".to_string()))),
value: Box::new(make_expr(ExprKind::Literal(Literal::Integer(2, None)))),
}),
make_expr(ExprKind::Assign {
target: Box::new(make_expr(ExprKind::Identifier("a".to_string()))),
value: Box::new(make_expr(ExprKind::Literal(Literal::Integer(1, None)))),
}),
];
let result = interp.eval_function_call(&func, &args).unwrap();
assert_eq!(result, Value::Integer(1));
}
#[test]
fn test_eval_function_call_builtin_parse_int() {
let mut interp = make_interpreter();
let func = make_expr(ExprKind::Identifier("parse_int".to_string()));
let args = vec![make_expr(ExprKind::Literal(Literal::String(
"42".to_string(),
)))];
let result = interp.eval_function_call(&func, &args);
assert!(result.is_ok() || result.is_err());
}
#[test]
fn test_eval_function_call_named_args_non_closure() {
let mut interp = make_interpreter();
interp.set_variable("not_a_func", Value::Integer(42));
let func = make_expr(ExprKind::Identifier("not_a_func".to_string()));
let args = vec![make_expr(ExprKind::Assign {
target: Box::new(make_expr(ExprKind::Identifier("x".to_string()))),
value: Box::new(make_expr(ExprKind::Literal(Literal::Integer(1, None)))),
})];
let result = interp.eval_function_call(&func, &args);
assert!(result.is_err());
}
#[test]
fn test_eval_function_call_named_args_lookup_fail() {
let mut interp = make_interpreter();
let func = make_expr(ExprKind::Identifier("UndefinedFunc".to_string()));
let args = vec![make_expr(ExprKind::Assign {
target: Box::new(make_expr(ExprKind::Identifier("x".to_string()))),
value: Box::new(make_expr(ExprKind::Literal(Literal::Integer(1, None)))),
})];
let result = interp.eval_function_call(&func, &args);
assert!(result.is_ok());
}
#[test]
fn test_eval_function_call_field_access_non_identifier() {
let mut interp = make_interpreter();
let func = make_expr(ExprKind::FieldAccess {
object: Box::new(make_expr(ExprKind::Literal(Literal::Integer(1, None)))),
field: "method".to_string(),
});
let result = interp.eval_function_call(&func, &[]);
assert!(result.is_err());
}
#[test]
fn test_eval_function_call_closure_with_default_param() {
let mut interp = make_interpreter();
let default_expr = make_expr(ExprKind::Literal(Literal::String("Hello".to_string())));
let params = vec![
Param {
pattern: Pattern::Identifier("name".to_string()),
ty: make_type("Any"),
span: Span::default(),
is_mutable: false,
default_value: None,
},
Param {
pattern: Pattern::Identifier("greeting".to_string()),
ty: make_type("Any"),
span: Span::default(),
is_mutable: false,
default_value: Some(Box::new(default_expr)),
},
];
let body = make_expr(ExprKind::Identifier("greeting".to_string()));
interp.eval_function("greet", ¶ms, &body).unwrap();
let func = make_expr(ExprKind::Identifier("greet".to_string()));
let args = vec![make_expr(ExprKind::Literal(Literal::String(
"Alice".to_string(),
)))];
let result = interp.eval_function_call(&func, &args).unwrap();
assert_eq!(result, Value::from_string("Hello".to_string()));
}
#[test]
fn test_eval_function_call_closure_override_default() {
let mut interp = make_interpreter();
let default_expr = make_expr(ExprKind::Literal(Literal::String("Hello".to_string())));
let params = vec![
Param {
pattern: Pattern::Identifier("name".to_string()),
ty: make_type("Any"),
span: Span::default(),
is_mutable: false,
default_value: None,
},
Param {
pattern: Pattern::Identifier("greeting".to_string()),
ty: make_type("Any"),
span: Span::default(),
is_mutable: false,
default_value: Some(Box::new(default_expr)),
},
];
let body = make_expr(ExprKind::Identifier("greeting".to_string()));
interp.eval_function("greet", ¶ms, &body).unwrap();
let func = make_expr(ExprKind::Identifier("greet".to_string()));
let args = vec![
make_expr(ExprKind::Literal(Literal::String("Alice".to_string()))),
make_expr(ExprKind::Literal(Literal::String("Hi".to_string()))),
];
let result = interp.eval_function_call(&func, &args).unwrap();
assert_eq!(result, Value::from_string("Hi".to_string()));
}
#[test]
fn test_eval_function_call_recursive() {
let mut interp = make_interpreter();
interp.eval_string("fun fact(n) { if n <= 1 { 1 } else { n * fact(n - 1) } }").unwrap();
let result = interp.eval_string("fact(5)").unwrap();
assert_eq!(result, Value::Integer(120));
}
#[test]
fn test_eval_function_call_struct_impl_method() {
let mut interp = make_interpreter();
let body = make_expr(ExprKind::Literal(Literal::Integer(0, None)));
let closure = Value::Closure {
params: vec![],
body: Arc::new(body),
env: Rc::new(RefCell::new(HashMap::new())),
};
interp.set_variable("Point::origin", closure);
let func = make_expr(ExprKind::FieldAccess {
object: Box::new(make_expr(ExprKind::Identifier("Point".to_string()))),
field: "origin".to_string(),
});
let result = interp.eval_function_call(&func, &[]).unwrap();
assert_eq!(result, Value::Integer(0));
}
#[test]
fn test_eval_function_call_vec_new_coverage() {
let mut interp = make_interpreter();
let func = make_expr(ExprKind::FieldAccess {
object: Box::new(make_expr(ExprKind::Identifier("Vec".to_string()))),
field: "new".to_string(),
});
let result = interp.eval_function_call(&func, &[]).unwrap();
assert!(matches!(result, Value::Array(_)));
if let Value::Array(arr) = result {
assert_eq!(arr.len(), 0);
}
}
#[test]
fn test_eval_function_call_box_new_coverage() {
let mut interp = make_interpreter();
let func = make_expr(ExprKind::FieldAccess {
object: Box::new(make_expr(ExprKind::Identifier("Box".to_string()))),
field: "new".to_string(),
});
let args = vec![make_expr(ExprKind::Literal(Literal::Integer(42, None)))];
let result = interp.eval_function_call(&func, &args).unwrap();
assert_eq!(result, Value::Integer(42));
}
#[test]
fn test_eval_function_call_json_parse_coverage() {
let mut interp = make_interpreter();
let func = make_expr(ExprKind::FieldAccess {
object: Box::new(make_expr(ExprKind::Identifier("JSON".to_string()))),
field: "parse".to_string(),
});
let args = vec![make_expr(ExprKind::Literal(Literal::String(
r#"{"x": 42}"#.to_string(),
)))];
let result = interp.eval_function_call(&func, &args);
assert!(result.is_ok() || result.is_err());
}
#[test]
fn test_eval_function_call_json_stringify_coverage() {
let mut interp = make_interpreter();
let func = make_expr(ExprKind::FieldAccess {
object: Box::new(make_expr(ExprKind::Identifier("JSON".to_string()))),
field: "stringify".to_string(),
});
let args = vec![make_expr(ExprKind::Literal(Literal::Integer(42, None)))];
let result = interp.eval_function_call(&func, &args);
assert!(result.is_ok() || result.is_err());
}
}