#![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)]
#![allow(clippy::rc_buffer)]
use crate::frontend::ast::{Expr, ExprKind};
use crate::runtime::interpreter::Interpreter;
use crate::runtime::{InterpreterError, Value};
use std::collections::HashMap;
use std::sync::Arc;
impl Interpreter {
pub(crate) fn eval_method_call(
&mut self,
receiver: &Expr,
method: &str,
args: &[Expr],
) -> Result<Value, InterpreterError> {
if let ExprKind::Identifier(namespace) = &receiver.kind {
let namespace_method = format!("{namespace}_{method}");
let arg_values: Result<Vec<_>, _> =
args.iter().map(|arg| self.eval_expr(arg)).collect();
let arg_values = arg_values?;
if let Ok(Some(result)) =
crate::runtime::eval_builtin::eval_builtin_function(&namespace_method, &arg_values)
{
return Ok(result);
}
}
if let ExprKind::Identifier(var_name) = &receiver.kind {
if method == "push" && args.len() == 1 {
if let Ok(Value::Array(arr)) = self.lookup_variable(var_name) {
let arg_value = self.eval_expr(&args[0])?;
let mut new_arr = arr.to_vec();
new_arr.push(arg_value);
self.env_set_mut(var_name.clone(), Value::Array(Arc::from(new_arr)));
return Ok(Value::Nil); }
} else if method == "pop" && args.is_empty() {
if let Ok(Value::Array(arr)) = self.lookup_variable(var_name) {
let mut new_arr = arr.to_vec();
let popped_value = new_arr.pop().unwrap_or(Value::Nil);
self.env_set_mut(var_name.clone(), Value::Array(Arc::from(new_arr)));
return Ok(popped_value); }
}
}
if let ExprKind::FieldAccess { object, field } = &receiver.kind {
if let Ok(object_value) = self.eval_expr(object) {
if let Value::ObjectMut(cell_rc) = object_value {
if method == "push" && args.len() == 1 {
let arg_value = self.eval_expr(&args[0])?;
let mut obj = cell_rc
.lock()
.expect("Mutex poisoned: object lock is corrupted");
if let Some(field_value) = obj.get(field) {
if let Value::Array(arr) = field_value {
let mut new_arr = arr.to_vec();
new_arr.push(arg_value);
obj.insert(field.clone(), Value::Array(Arc::from(new_arr)));
return Ok(Value::Nil); }
}
}
}
}
}
let receiver_value = self.eval_expr(receiver)?;
if let Value::Object(ref obj) = receiver_value {
if let Some(Value::String(type_name)) = obj.get("__type") {
if type_name.as_ref() == "Module" {
let func_value = obj.get(method).ok_or_else(|| {
InterpreterError::RuntimeError(format!(
"Module has no function named '{}'",
method
))
})?;
let arg_values: Result<Vec<_>, _> =
args.iter().map(|arg| self.eval_expr(arg)).collect();
let arg_values = arg_values?;
return self.call_function(func_value.clone(), &arg_values);
}
}
}
if matches!(receiver_value, Value::DataFrame { .. }) {
match method {
"filter" => return self.eval_dataframe_filter_method(&receiver_value, args),
"with_column" => {
return self.eval_dataframe_with_column_method(&receiver_value, args)
}
"transform" => return self.eval_dataframe_transform_method(&receiver_value, args),
_ => {}
}
}
if (method == "send" || method == "ask") && args.len() == 1 {
let is_actor = match &receiver_value {
Value::Object(ref obj) => obj.contains_key("__actor"),
Value::ObjectMut(ref cell) => cell
.lock()
.expect("Mutex poisoned: object lock is corrupted")
.contains_key("__actor"),
_ => false,
};
if is_actor {
let arg_value = match &args[0].kind {
ExprKind::Identifier(name) => {
if let Ok(val) = self.lookup_variable(name) {
val
} else {
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(vec![])));
Value::Object(Arc::new(message))
}
}
_ => self.eval_expr(&args[0])?,
};
return self.dispatch_method_call(&receiver_value, method, &[arg_value], false);
}
}
let arg_values: Result<Vec<_>, _> = args.iter().map(|arg| self.eval_expr(arg)).collect();
let arg_values = arg_values?;
if let ExprKind::Identifier(var_name) = &receiver.kind {
if matches!(receiver_value, Value::ObjectMut(_)) {
let result = self.dispatch_method_call(
&receiver_value,
method,
&arg_values,
args.is_empty(),
)?;
self.set_variable(var_name, receiver_value);
return Ok(result);
}
if let Value::Struct { name, fields } = &receiver_value {
let qualified_method_name = format!("{}::{}", name, method);
if self.lookup_variable(&qualified_method_name).is_ok() {
let (result, modified_fields_opt) = self
.eval_struct_instance_method_with_self_capture(
fields,
name,
method,
&arg_values,
)?;
if let Some(modified_fields) = modified_fields_opt {
let new_struct = Value::Struct {
name: name.clone(),
fields: modified_fields,
};
self.set_variable(var_name, new_struct);
}
return Ok(result);
}
}
}
self.dispatch_method_call(&receiver_value, method, &arg_values, args.is_empty())
}
pub(crate) fn eval_message_expr(&mut self, message: &Expr) -> Result<Value, InterpreterError> {
match &message.kind {
ExprKind::Identifier(name) => {
if let Ok(val) = self.lookup_variable(name) {
Ok(val)
} else {
let mut msg_obj = HashMap::new();
msg_obj.insert(
"__type".to_string(),
Value::from_string("Message".to_string()),
);
msg_obj.insert("type".to_string(), Value::from_string(name.clone()));
msg_obj.insert("data".to_string(), Value::Array(Arc::from(vec![])));
Ok(Value::Object(Arc::new(msg_obj)))
}
}
_ => self.eval_expr(message),
}
}
pub(crate) fn dispatch_method_call(
&mut self,
receiver: &Value,
method: &str,
arg_values: &[Value],
args_empty: bool,
) -> Result<Value, InterpreterError> {
let base_method = if let Some(pos) = method.find("::") {
&method[..pos]
} else {
method
};
match receiver {
Value::String(s) => self.eval_string_method(s, base_method, arg_values),
Value::Array(arr) => self.eval_array_method(arr, base_method, arg_values),
Value::Float(f) => self.eval_float_method(*f, base_method, args_empty),
Value::Integer(n) => self.eval_integer_method(*n, base_method, arg_values),
Value::DataFrame { columns } => {
self.eval_dataframe_method(columns, base_method, arg_values)
}
Value::Object(obj) => {
if let Some(Value::String(actor_name)) = obj.get("__actor") {
self.eval_actor_instance_method(
obj,
actor_name.as_ref(),
base_method,
arg_values,
)
}
else if let Some(Value::String(class_name)) = obj.get("__class") {
self.eval_class_instance_method(
obj,
class_name.as_ref(),
base_method,
arg_values,
)
}
else if let Some(Value::String(struct_name)) =
obj.get("__struct_type").or_else(|| obj.get("__struct"))
{
self.eval_struct_instance_method(
obj,
struct_name.as_ref(),
base_method,
arg_values,
)
}
else if let Some(Value::String(type_str)) = obj.get("__type") {
if type_str.as_ref() == "DataFrameBuilder" {
self.eval_dataframe_builder_method(obj, base_method, arg_values)
} else {
self.eval_object_method(obj, base_method, arg_values, args_empty)
}
} else {
self.eval_object_method(obj, base_method, arg_values, args_empty)
}
}
Value::ObjectMut(cell_rc) => {
let obj = cell_rc
.lock()
.expect("Mutex poisoned: object lock is corrupted");
if let Some(Value::String(actor_name)) = obj.get("__actor") {
let actor_name = actor_name.clone();
drop(obj); self.eval_actor_instance_method_mut(
cell_rc,
actor_name.as_ref(),
base_method,
arg_values,
)
}
else if let Some(Value::String(class_name)) = obj.get("__class") {
let class_name = class_name.clone();
drop(obj); self.eval_class_instance_method_mut(
cell_rc,
class_name.as_ref(),
base_method,
arg_values,
)
}
else if let Some(Value::String(struct_name)) =
obj.get("__struct_type").or_else(|| obj.get("__struct"))
{
let struct_name = struct_name.clone();
drop(obj); self.eval_struct_instance_method_mut(
cell_rc,
struct_name.as_ref(),
base_method,
arg_values,
)
}
else if let Some(Value::String(type_name)) = obj.get("__type") {
if type_name.as_ref() == "File" {
drop(obj); return self.eval_file_method_mut(cell_rc, base_method, arg_values);
}
drop(obj); self.eval_object_method_mut(cell_rc, base_method, arg_values, args_empty)
} else {
drop(obj); self.eval_object_method_mut(cell_rc, base_method, arg_values, args_empty)
}
}
Value::Struct { name, fields } => {
self.eval_struct_instance_method(fields, name, base_method, arg_values)
}
Value::Class {
class_name,
fields,
methods,
} => {
self.eval_class_instance_method_on_class(
class_name,
fields,
methods,
base_method,
arg_values,
)
}
#[cfg(not(target_arch = "wasm32"))]
Value::HtmlDocument(doc) => {
self.eval_html_document_method(doc, base_method, arg_values)
}
#[cfg(not(target_arch = "wasm32"))]
Value::HtmlElement(elem) => {
self.eval_html_element_method(elem, base_method, arg_values)
}
_ => self.eval_generic_method(receiver, base_method, args_empty),
}
}
pub(crate) fn eval_float_method(
&self,
f: f64,
method: &str,
args_empty: bool,
) -> Result<Value, InterpreterError> {
super::eval_method::eval_float_method(f, method, args_empty)
}
pub(crate) fn eval_integer_method(
&self,
n: i64,
method: &str,
arg_values: &[Value],
) -> Result<Value, InterpreterError> {
super::eval_method::eval_integer_method(n, method, arg_values)
}
pub(crate) fn eval_generic_method(
&self,
receiver: &Value,
method: &str,
args_empty: bool,
) -> Result<Value, InterpreterError> {
super::eval_method::eval_generic_method(receiver, method, args_empty)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::frontend::ast::Span;
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,
}
}
#[test]
fn test_dispatch_strips_turbofish() {
let mut interp = make_interpreter();
let s = Value::from_string("42".to_string());
let result = interp.dispatch_method_call(&s, "parse::<i32>", &[], false);
assert!(result.is_ok());
assert_eq!(result.unwrap(), Value::Integer(42));
}
#[test]
fn test_dispatch_to_string_method() {
let mut interp = make_interpreter();
let s = Value::from_string("hello".to_string());
let result = interp.dispatch_method_call(&s, "len", &[], false).unwrap();
assert_eq!(result, Value::Integer(5));
}
#[test]
fn test_dispatch_to_array_method() {
let mut interp = make_interpreter();
let arr = Value::Array(Arc::from(vec![Value::Integer(1), Value::Integer(2)]));
let result = interp
.dispatch_method_call(&arr, "len", &[], false)
.unwrap();
assert_eq!(result, Value::Integer(2));
}
#[test]
fn test_dispatch_to_float_method() {
let mut interp = make_interpreter();
let f = Value::Float(3.7);
let result = interp.dispatch_method_call(&f, "round", &[], true).unwrap();
assert_eq!(result, Value::Float(4.0));
}
#[test]
fn test_dispatch_to_integer_method() {
let mut interp = make_interpreter();
let n = Value::Integer(-5);
let result = interp.dispatch_method_call(&n, "abs", &[], false).unwrap();
assert_eq!(result, Value::Integer(5));
}
#[test]
fn test_eval_message_expr_undefined_identifier() {
let mut interp = make_interpreter();
let expr = make_expr(ExprKind::Identifier("Increment".to_string()));
let result = interp.eval_message_expr(&expr).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("Increment".to_string()))
);
} else {
panic!("Expected Object");
}
}
#[test]
fn test_eval_message_expr_defined_variable() {
let mut interp = make_interpreter();
interp.set_variable("x", Value::Integer(42));
let expr = make_expr(ExprKind::Identifier("x".to_string()));
let result = interp.eval_message_expr(&expr).unwrap();
assert_eq!(result, Value::Integer(42));
}
#[test]
fn test_eval_message_expr_literal() {
let mut interp = make_interpreter();
let expr = make_expr(ExprKind::Literal(crate::frontend::ast::Literal::Integer(
100, None,
)));
let result = interp.eval_message_expr(&expr).unwrap();
assert_eq!(result, Value::Integer(100));
}
#[test]
fn test_dispatch_to_object_missing_type() {
let mut interp = make_interpreter();
let obj_map = HashMap::new();
let obj = Value::Object(Arc::new(obj_map));
let result = interp.dispatch_method_call(&obj, "test", &[], true);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("missing __type marker"));
}
#[test]
fn test_dispatch_to_object_unknown_type() {
let mut interp = make_interpreter();
let mut obj_map = HashMap::new();
obj_map.insert(
"__type".to_string(),
Value::from_string("UnknownType".to_string()),
);
let obj = Value::Object(Arc::new(obj_map));
let result = interp.dispatch_method_call(&obj, "test", &[], true);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("Unknown object type"));
}
#[test]
fn test_eval_generic_method() {
let interp = make_interpreter();
let v = Value::Integer(42);
let result = interp.eval_generic_method(&v, "to_string", false);
assert!(result.is_ok() || result.is_err());
}
#[test]
fn test_eval_float_method_ceil() {
let interp = make_interpreter();
let result = interp.eval_float_method(3.2, "ceil", true).unwrap();
assert_eq!(result, Value::Float(4.0));
}
#[test]
fn test_eval_float_method_floor() {
let interp = make_interpreter();
let result = interp.eval_float_method(3.8, "floor", true).unwrap();
assert_eq!(result, Value::Float(3.0));
}
#[test]
fn test_eval_integer_method_abs() {
let interp = make_interpreter();
let result = interp.eval_integer_method(-10, "abs", &[]).unwrap();
assert_eq!(result, Value::Integer(10));
}
#[test]
fn test_eval_integer_method_positive_abs() {
let interp = make_interpreter();
let result = interp.eval_integer_method(10, "abs", &[]).unwrap();
assert_eq!(result, Value::Integer(10));
}
#[test]
fn test_eval_method_call_push_on_array() {
let mut interp = make_interpreter();
interp.set_variable("arr", Value::Array(Arc::from(vec![Value::Integer(1)])));
let receiver = make_expr(ExprKind::Identifier("arr".to_string()));
let arg = make_expr(ExprKind::Literal(crate::frontend::ast::Literal::Integer(
2, None,
)));
let result = interp.eval_method_call(&receiver, "push", &[arg]).unwrap();
assert_eq!(result, Value::Nil);
let arr = interp.lookup_variable("arr").unwrap();
if let Value::Array(values) = arr {
assert_eq!(values.len(), 2);
assert_eq!(values[1], Value::Integer(2));
} else {
panic!("Expected Array");
}
}
#[test]
fn test_eval_method_call_pop_on_array() {
let mut interp = make_interpreter();
interp.set_variable(
"arr",
Value::Array(Arc::from(vec![Value::Integer(1), Value::Integer(2)])),
);
let receiver = make_expr(ExprKind::Identifier("arr".to_string()));
let result = interp.eval_method_call(&receiver, "pop", &[]).unwrap();
assert_eq!(result, Value::Integer(2));
let arr = interp.lookup_variable("arr").unwrap();
if let Value::Array(values) = arr {
assert_eq!(values.len(), 1);
} else {
panic!("Expected Array");
}
}
#[test]
fn test_eval_method_call_pop_empty_array() {
let mut interp = make_interpreter();
interp.set_variable("arr", Value::Array(Arc::from(vec![])));
let receiver = make_expr(ExprKind::Identifier("arr".to_string()));
let result = interp.eval_method_call(&receiver, "pop", &[]).unwrap();
assert_eq!(result, Value::Nil);
}
#[test]
fn test_eval_method_call_push_on_objectmut_field() {
use std::sync::Mutex;
let mut interp = make_interpreter();
let mut obj = HashMap::new();
obj.insert(
"items".to_string(),
Value::Array(Arc::from(vec![Value::Integer(1)])),
);
let obj_mut = Value::ObjectMut(Arc::new(Mutex::new(obj)));
interp.set_variable("self", obj_mut);
let object = Box::new(make_expr(ExprKind::Identifier("self".to_string())));
let receiver = make_expr(ExprKind::FieldAccess {
object,
field: "items".to_string(),
});
let arg = make_expr(ExprKind::Literal(crate::frontend::ast::Literal::Integer(
2, None,
)));
let result = interp.eval_method_call(&receiver, "push", &[arg]).unwrap();
assert_eq!(result, Value::Nil);
}
#[test]
fn test_eval_method_call_module() {
use crate::frontend::ast::Literal;
use std::cell::RefCell;
use std::rc::Rc;
let mut interp = make_interpreter();
let mut module = HashMap::new();
module.insert(
"__type".to_string(),
Value::from_string("Module".to_string()),
);
let body = make_expr(ExprKind::Literal(Literal::Integer(42, None)));
let closure = Value::Closure {
params: vec![],
body: Arc::new(body),
env: Rc::new(RefCell::new(HashMap::new())),
};
module.insert("get_answer".to_string(), closure);
interp.set_variable("math", Value::Object(Arc::new(module)));
let receiver = make_expr(ExprKind::Identifier("math".to_string()));
let result = interp
.eval_method_call(&receiver, "get_answer", &[])
.unwrap();
assert_eq!(result, Value::Integer(42));
}
#[test]
fn test_eval_method_call_module_not_found() {
let mut interp = make_interpreter();
let mut module = HashMap::new();
module.insert(
"__type".to_string(),
Value::from_string("Module".to_string()),
);
interp.set_variable("math", Value::Object(Arc::new(module)));
let receiver = make_expr(ExprKind::Identifier("math".to_string()));
let result = interp.eval_method_call(&receiver, "nonexistent", &[]);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("no function named"));
}
#[test]
fn test_dispatch_to_actor_object() {
let mut interp = make_interpreter();
let mut actor = HashMap::new();
actor.insert(
"__actor".to_string(),
Value::from_string("Counter".to_string()),
);
let obj = Value::Object(Arc::new(actor));
let result = interp
.dispatch_method_call(&obj, "stop", &[], true)
.unwrap();
assert_eq!(result, Value::Bool(true));
}
#[test]
fn test_dispatch_to_class_object() {
let mut interp = make_interpreter();
let mut class_obj = HashMap::new();
class_obj.insert(
"__class".to_string(),
Value::from_string("MyClass".to_string()),
);
let obj = Value::Object(Arc::new(class_obj));
let result = interp.dispatch_method_call(&obj, "unknown_method", &[], true);
assert!(result.is_err());
}
#[test]
fn test_dispatch_to_struct_object() {
let mut interp = make_interpreter();
let mut struct_obj = HashMap::new();
struct_obj.insert(
"__struct".to_string(),
Value::from_string("Point".to_string()),
);
let obj = Value::Object(Arc::new(struct_obj));
let result = interp.dispatch_method_call(&obj, "unknown_method", &[], true);
assert!(result.is_err());
}
#[test]
fn test_dispatch_to_struct_type_object() {
let mut interp = make_interpreter();
let mut struct_obj = HashMap::new();
struct_obj.insert(
"__struct_type".to_string(),
Value::from_string("Point".to_string()),
);
let obj = Value::Object(Arc::new(struct_obj));
let result = interp.dispatch_method_call(&obj, "unknown_method", &[], true);
assert!(result.is_err());
}
#[test]
fn test_dispatch_to_dataframe_builder() {
let mut interp = make_interpreter();
let mut builder = HashMap::new();
builder.insert(
"__type".to_string(),
Value::from_string("DataFrameBuilder".to_string()),
);
let obj = Value::Object(Arc::new(builder));
let result = interp.dispatch_method_call(&obj, "unknown", &[], true);
assert!(result.is_err());
}
#[test]
fn test_dispatch_to_actor_objectmut() {
use std::sync::Mutex;
let mut interp = make_interpreter();
let mut actor = HashMap::new();
actor.insert(
"__actor".to_string(),
Value::from_string("Counter".to_string()),
);
let obj = Value::ObjectMut(Arc::new(Mutex::new(actor)));
let result = interp
.dispatch_method_call(&obj, "stop", &[], true)
.unwrap();
assert_eq!(result, Value::Bool(true));
}
#[test]
fn test_dispatch_to_class_objectmut() {
use std::sync::Mutex;
let mut interp = make_interpreter();
let mut class_obj = HashMap::new();
class_obj.insert(
"__class".to_string(),
Value::from_string("MyClass".to_string()),
);
let obj = Value::ObjectMut(Arc::new(Mutex::new(class_obj)));
let result = interp.dispatch_method_call(&obj, "unknown_method", &[], true);
assert!(result.is_err());
}
#[test]
fn test_dispatch_to_struct_objectmut() {
use std::sync::Mutex;
let mut interp = make_interpreter();
let mut struct_obj = HashMap::new();
struct_obj.insert(
"__struct".to_string(),
Value::from_string("Point".to_string()),
);
let obj = Value::ObjectMut(Arc::new(Mutex::new(struct_obj)));
let result = interp.dispatch_method_call(&obj, "unknown_method", &[], true);
assert!(result.is_err());
}
#[test]
fn test_dispatch_to_file_objectmut() {
use std::sync::Mutex;
let mut interp = make_interpreter();
let mut file_obj = HashMap::new();
file_obj.insert("__type".to_string(), Value::from_string("File".to_string()));
let obj = Value::ObjectMut(Arc::new(Mutex::new(file_obj)));
let result = interp.dispatch_method_call(&obj, "close", &[], true);
assert!(result.is_ok() || result.is_err());
}
#[test]
fn test_dispatch_to_generic_objectmut() {
use std::sync::Mutex;
let mut interp = make_interpreter();
let mut obj_map = HashMap::new();
obj_map.insert(
"__type".to_string(),
Value::from_string("GenericType".to_string()),
);
let obj = Value::ObjectMut(Arc::new(Mutex::new(obj_map)));
let result = interp.dispatch_method_call(&obj, "test", &[], true);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("Unknown object type"));
}
#[test]
fn test_dispatch_to_value_struct() {
let mut interp = make_interpreter();
let fields: HashMap<String, Value> = HashMap::new();
let v = Value::Struct {
name: "Point".to_string(),
fields: Arc::new(fields),
};
let result = interp.dispatch_method_call(&v, "unknown_method", &[], true);
assert!(result.is_err());
}
#[test]
fn test_dispatch_to_value_class() {
use std::sync::RwLock;
let mut interp = make_interpreter();
let v = Value::Class {
class_name: "Person".to_string(),
fields: Arc::new(RwLock::new(HashMap::new())),
methods: Arc::new(HashMap::new()),
};
let result = interp.dispatch_method_call(&v, "unknown_method", &[], true);
assert!(result.is_err());
}
#[test]
fn test_dispatch_to_bool() {
let mut interp = make_interpreter();
let v = Value::Bool(true);
let result = interp.dispatch_method_call(&v, "to_string", &[], true);
assert!(result.is_ok() || result.is_err());
}
#[test]
fn test_method_call_actor_send_undefined_message() {
let mut interp = make_interpreter();
let mut actor = HashMap::new();
actor.insert(
"__actor".to_string(),
Value::from_string("Counter".to_string()),
);
interp.set_variable("counter", Value::Object(Arc::new(actor)));
let receiver = make_expr(ExprKind::Identifier("counter".to_string()));
let arg = make_expr(ExprKind::Identifier("Increment".to_string()));
let result = interp.eval_method_call(&receiver, "send", &[arg]);
assert!(result.is_err());
}
#[test]
fn test_method_call_actor_ask_defined_variable() {
let mut interp = make_interpreter();
let mut actor = HashMap::new();
actor.insert(
"__actor".to_string(),
Value::from_string("Echo".to_string()),
);
interp.set_variable("echo", Value::Object(Arc::new(actor)));
interp.set_variable("msg", Value::Integer(42));
let receiver = make_expr(ExprKind::Identifier("echo".to_string()));
let arg = make_expr(ExprKind::Identifier("msg".to_string()));
let result = interp.eval_method_call(&receiver, "ask", &[arg]).unwrap();
assert_eq!(result, Value::Integer(42));
}
#[test]
fn test_method_call_objectmut_actor_send() {
use std::sync::Mutex;
let mut interp = make_interpreter();
let mut actor = HashMap::new();
actor.insert(
"__actor".to_string(),
Value::from_string("Counter".to_string()),
);
interp.set_variable("counter", Value::ObjectMut(Arc::new(Mutex::new(actor))));
let receiver = make_expr(ExprKind::Identifier("counter".to_string()));
let arg = make_expr(ExprKind::Identifier("Increment".to_string()));
let result = interp.eval_method_call(&receiver, "send", &[arg]);
assert!(result.is_err());
}
#[test]
fn test_method_call_non_actor_send() {
let mut interp = make_interpreter();
let obj = HashMap::new();
interp.set_variable("obj", Value::Object(Arc::new(obj)));
let receiver = make_expr(ExprKind::Identifier("obj".to_string()));
let arg = make_expr(ExprKind::Literal(crate::frontend::ast::Literal::Integer(
1, None,
)));
let result = interp.eval_method_call(&receiver, "send", &[arg]);
assert!(result.is_err());
}
#[test]
fn test_method_call_objectmut_identifier() {
use std::sync::Mutex;
let mut interp = make_interpreter();
let mut obj = HashMap::new();
obj.insert(
"__type".to_string(),
Value::from_string("SomeType".to_string()),
);
interp.set_variable("obj", Value::ObjectMut(Arc::new(Mutex::new(obj))));
let receiver = make_expr(ExprKind::Identifier("obj".to_string()));
let result = interp.eval_method_call(&receiver, "test", &[]);
assert!(result.is_err());
}
#[test]
fn test_method_call_struct_identifier_with_impl() {
use crate::frontend::ast::Literal;
use std::cell::RefCell;
use std::rc::Rc;
let mut interp = make_interpreter();
let body = make_expr(ExprKind::Literal(Literal::Integer(99, None)));
let closure = Value::Closure {
params: vec![("self".to_string(), None)],
body: Arc::new(body),
env: Rc::new(RefCell::new(HashMap::new())),
};
interp.set_variable("Point::get_x", closure);
let fields: HashMap<String, Value> = HashMap::new();
let struct_val = Value::Struct {
name: "Point".to_string(),
fields: Arc::new(fields),
};
interp.set_variable("p", struct_val);
let receiver = make_expr(ExprKind::Identifier("p".to_string()));
let result = interp.eval_method_call(&receiver, "get_x", &[]).unwrap();
assert_eq!(result, Value::Integer(99));
}
}