#![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 crate::frontend::ast::{Expr, ExprKind};
use crate::runtime::interpreter::Interpreter;
use crate::runtime::{DataFrameColumn, InterpreterError, Value};
use std::collections::HashMap;
use std::sync::Arc;
impl Interpreter {
pub(crate) fn eval_index_access(
&mut self,
object: &Expr,
index: &Expr,
) -> Result<Value, InterpreterError> {
let object_value = self.eval_expr(object)?;
let index_value = self.eval_expr(index)?;
match (&object_value, &index_value) {
(Value::Array(ref array), Value::Integer(idx)) => Self::index_array(array, *idx),
(
Value::Array(ref array),
Value::Range {
start,
end,
inclusive,
},
) => Self::slice_array(array, start, end, *inclusive),
(Value::String(ref s), Value::Integer(idx)) => Self::index_string(s, *idx),
(
Value::String(ref s),
Value::Range {
start,
end,
inclusive,
},
) => Self::slice_string(s, start, end, *inclusive),
(Value::Tuple(ref tuple), Value::Integer(idx)) => Self::index_tuple(tuple, *idx),
(Value::Object(ref fields), Value::String(ref key)) => Self::index_object(fields, key),
(Value::Object(ref fields), Value::Atom(ref key)) => {
let atom_key = format!(":{}", key);
Self::index_object(fields, &atom_key)
}
(Value::ObjectMut(ref cell), Value::String(ref key)) => {
Self::index_object_mut(cell, key)
}
(Value::ObjectMut(ref cell), Value::Atom(ref key)) => {
let atom_key = format!(":{}", key);
Self::index_object_mut(cell, &atom_key)
}
(Value::DataFrame { columns }, Value::Integer(idx)) => {
Self::index_dataframe_row(columns, *idx)
}
(Value::DataFrame { columns }, Value::String(ref col_name)) => {
Self::index_dataframe_column(columns, col_name)
}
_ => Err(InterpreterError::RuntimeError(format!(
"Cannot index {} with {}",
object_value.type_name(),
index_value.type_name()
))),
}
}
pub(crate) fn index_array(array: &[Value], idx: i64) -> Result<Value, InterpreterError> {
crate::runtime::eval_index::index_array(array, idx)
}
pub(crate) fn index_string(s: &str, idx: i64) -> Result<Value, InterpreterError> {
crate::runtime::eval_index::index_string(s, idx)
}
pub(crate) fn slice_string(
s: &str,
start: &Value,
end: &Value,
inclusive: bool,
) -> Result<Value, InterpreterError> {
crate::runtime::eval_index::slice_string(s, start, end, inclusive)
}
pub(crate) fn slice_array(
array: &[Value],
start: &Value,
end: &Value,
inclusive: bool,
) -> Result<Value, InterpreterError> {
crate::runtime::eval_index::slice_array(array, start, end, inclusive)
}
pub(crate) fn index_tuple(tuple: &[Value], idx: i64) -> Result<Value, InterpreterError> {
crate::runtime::eval_index::index_tuple(tuple, idx)
}
pub(crate) fn index_object(
fields: &HashMap<String, Value>,
key: &str,
) -> Result<Value, InterpreterError> {
crate::runtime::eval_index::index_object(fields, key)
}
pub(crate) fn index_object_mut(
cell: &Arc<std::sync::Mutex<HashMap<String, Value>>>,
key: &str,
) -> Result<Value, InterpreterError> {
crate::runtime::eval_index::index_object_mut(cell, key)
}
pub(crate) fn index_dataframe_row(
columns: &[DataFrameColumn],
row_idx: i64,
) -> Result<Value, InterpreterError> {
crate::runtime::eval_index::index_dataframe_row(columns, row_idx)
}
pub(crate) fn index_dataframe_column(
columns: &[DataFrameColumn],
col_name: &str,
) -> Result<Value, InterpreterError> {
crate::runtime::eval_index::index_dataframe_column(columns, col_name)
}
pub(crate) fn check_field_visibility(
&self,
struct_name: &str,
field: &str,
) -> Result<(), InterpreterError> {
let struct_type = self.lookup_variable(struct_name).ok();
if let Some(Value::Object(struct_obj)) = struct_type {
if let Some(Value::Object(fields)) = struct_obj.get("__fields") {
if let Some(Value::Object(field_info)) = fields.get(field) {
if let Some(Value::String(visibility)) = field_info.get("visibility") {
if visibility.as_ref() == "private" {
return Err(InterpreterError::RuntimeError(format!(
"Field '{}' is private and cannot be accessed outside the struct",
field
)));
}
}
}
}
}
Ok(())
}
pub(crate) fn eval_field_access(
&mut self,
object: &Expr,
field: &str,
) -> Result<Value, InterpreterError> {
let object_value = self.eval_expr(object)?;
match object_value {
Value::Object(ref object_map) => {
if let Some(Value::String(type_str)) = object_map.get("__type") {
if type_str.as_ref() == "Enum" {
let enum_name = if let ExprKind::Identifier(name) = &object.kind {
name.clone()
} else {
"UnknownEnum".to_string()
};
return Ok(Value::EnumVariant {
enum_name,
variant_name: field.to_string(),
data: None, });
}
}
self.access_object_field(object_map, field)
}
Value::ObjectMut(ref cell) => self.access_object_mut_field(cell, field),
Value::Struct {
ref name,
ref fields,
} => {
fields.get(field).cloned().ok_or_else(|| {
InterpreterError::RuntimeError(format!(
"Field '{field}' not found in struct {name}"
))
})
}
Value::Class {
ref class_name,
ref fields,
..
} => {
let fields_read = fields
.read()
.expect("RwLock poisoned: class fields lock is corrupted");
fields_read.get(field).cloned().ok_or_else(|| {
InterpreterError::RuntimeError(format!(
"Field '{field}' not found in class {class_name}"
))
})
}
Value::Tuple(ref elements) => {
crate::runtime::eval_data_structures::eval_tuple_field_access(elements, field)
}
Value::DataFrame { ref columns } => {
Self::index_dataframe_column(columns, field)
}
_ => Err(InterpreterError::RuntimeError(format!(
"Cannot access field '{}' on type {}",
field,
object_value.type_name()
))),
}
}
pub(crate) fn access_object_field(
&self,
object_map: &HashMap<String, Value>,
field: &str,
) -> Result<Value, InterpreterError> {
if let Some(constructor) = Self::check_constructor_access(object_map, field) {
return Ok(constructor);
}
if let Some(actor_field) = Self::check_actor_field_access(object_map, field)? {
return Ok(actor_field);
}
self.check_struct_visibility(object_map, field)?;
Self::get_object_field(object_map, field)
}
pub(crate) fn access_object_mut_field(
&self,
cell: &Arc<std::sync::Mutex<HashMap<String, Value>>>,
field: &str,
) -> Result<Value, InterpreterError> {
let object_map = cell
.lock()
.expect("Mutex poisoned: object map lock is corrupted");
if let Some(actor_field) = Self::check_actor_field_access(&object_map, field)? {
return Ok(actor_field);
}
self.check_struct_visibility(&object_map, field)?;
Self::get_object_field(&object_map, field)
}
pub(crate) fn check_constructor_access(
object_map: &HashMap<String, Value>,
field: &str,
) -> Option<Value> {
if field != "new" {
return None;
}
if let Some(Value::String(ref type_str)) = object_map.get("__type") {
if let Some(Value::String(ref name)) = object_map.get("__name") {
return match type_str.as_ref() {
"Actor" => Some(Value::from_string(format!("__actor_constructor__:{name}"))),
"Struct" => Some(Value::from_string(format!("__struct_constructor__:{name}"))),
"Class" => Some(Value::from_string(format!(
"__class_constructor__:{name}:new"
))),
_ => None,
};
}
}
None
}
pub(crate) fn check_actor_field_access(
object_map: &HashMap<String, Value>,
field: &str,
) -> Result<Option<Value>, InterpreterError> {
if let Some(Value::String(actor_id)) = object_map.get("__actor_id") {
use crate::runtime::actor_runtime::ACTOR_RUNTIME;
let field_value = ACTOR_RUNTIME.get_actor_field(actor_id.as_ref(), field)?;
Ok(Some(field_value.to_value()))
} else {
Ok(None)
}
}
pub(crate) fn check_struct_visibility(
&self,
object_map: &HashMap<String, Value>,
field: &str,
) -> Result<(), InterpreterError> {
if let Some(Value::String(struct_name)) = object_map.get("__struct_type") {
self.check_field_visibility(struct_name.as_ref(), field)?;
}
Ok(())
}
pub(crate) fn get_object_field(
object_map: &HashMap<String, Value>,
field: &str,
) -> Result<Value, InterpreterError> {
object_map.get(field).cloned().ok_or_else(|| {
InterpreterError::RuntimeError(format!("Object has no field named '{field}'"))
})
}
pub(crate) fn eval_object_literal(
&mut self,
fields: &[crate::frontend::ast::ObjectField],
) -> Result<Value, InterpreterError> {
let mut object = HashMap::new();
for field in fields {
match field {
crate::frontend::ast::ObjectField::KeyValue { key, value } => {
let eval_value = self.eval_expr(value)?;
object.insert(key.clone(), eval_value);
}
crate::frontend::ast::ObjectField::Spread { expr: _ } => {
return Err(InterpreterError::RuntimeError(
"Spread operator in object literals not yet implemented".to_string(),
));
}
}
}
Ok(Value::Object(Arc::new(object)))
}
pub(crate) fn eval_qualified_name(
&self,
module: &str,
name: &str,
) -> Result<Value, InterpreterError> {
if module == "HashMap" && name == "new" {
Ok(Value::from_string("__builtin_hashmap__".to_string()))
} else if module == "String" && (name == "new" || name == "from" || name == "from_utf8") {
Ok(Value::from_string(format!("__builtin_String_{}__", name)))
} else if module == "Command" && name == "new" {
Ok(Value::from_string("__builtin_command_new__".to_string()))
} else if name == "new" {
let qualified_method_name = format!("{}::{}", module, name);
if let Ok(method_value) = self.lookup_variable(&qualified_method_name) {
return Ok(method_value);
}
if let Ok(class_value) = self.lookup_variable(module) {
if let Value::Object(ref class_info) = class_value {
if let Some(Value::String(ref type_str)) = class_info.get("__type") {
if type_str.as_ref() == "Class" {
return Ok(Value::from_string(format!(
"__class_constructor__:{}",
module
)));
}
}
}
}
if let Ok(struct_value) = self.lookup_variable(module) {
if let Value::Object(ref struct_info) = struct_value {
if let Some(Value::String(ref type_str)) = struct_info.get("__type") {
if type_str.as_ref() == "Struct" {
return Ok(Value::from_string(format!(
"__struct_constructor__:{}",
module
)));
}
}
}
}
if let Ok(actor_value) = self.lookup_variable(module) {
if let Value::Object(ref actor_info) = actor_value {
if let Some(Value::String(ref type_str)) = actor_info.get("__type") {
if type_str.as_ref() == "Actor" {
return Ok(Value::from_string(format!(
"__actor_constructor__:{}",
module
)));
}
}
}
}
Err(InterpreterError::RuntimeError(format!(
"Unknown qualified name: {}::{}",
module, name
)))
} else {
let qualified_method_name = format!("{}::{}", module, name);
if let Ok(method_value) = self.lookup_variable(&qualified_method_name) {
Ok(method_value)
} else {
Err(InterpreterError::RuntimeError(format!(
"Unknown qualified name: {}::{}",
module, name
)))
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::runtime::interpreter::Interpreter;
use std::sync::Arc;
#[test]
fn test_eval_index_access_array_positive() {
let mut interp = Interpreter::new();
let result = interp.eval_string("[10, 20, 30][1]").unwrap();
assert_eq!(result, Value::Integer(20));
}
#[test]
fn test_eval_index_access_array_negative() {
let mut interp = Interpreter::new();
let result = interp.eval_string("[10, 20, 30][-1]").unwrap();
assert_eq!(result, Value::Integer(30));
}
#[test]
fn test_eval_index_access_array_out_of_bounds() {
let mut interp = Interpreter::new();
let result = interp.eval_string("[1, 2, 3][10]");
assert!(result.is_err());
}
#[test]
fn test_eval_index_access_string_positive() {
let mut interp = Interpreter::new();
let result = interp.eval_string(r#"{ let s = "hello"; s[0] }"#).unwrap();
assert_eq!(result.to_string(), "\"h\"");
}
#[test]
fn test_eval_index_access_string_negative() {
let mut interp = Interpreter::new();
let result = interp.eval_string(r#"{ let s = "hello"; s[-1] }"#).unwrap();
assert_eq!(result.to_string(), "\"o\"");
}
#[test]
fn test_eval_index_access_string_out_of_bounds() {
let mut interp = Interpreter::new();
let result = interp.eval_string(r#"{ let s = "hi"; s[10] }"#);
assert!(result.is_err());
}
#[test]
fn test_eval_index_access_array_slice_exclusive() {
let mut interp = Interpreter::new();
let result = interp.eval_string("[1, 2, 3, 4, 5][1..4]").unwrap();
if let Value::Array(arr) = result {
assert_eq!(arr.len(), 3);
assert_eq!(arr[0], Value::Integer(2));
assert_eq!(arr[2], Value::Integer(4));
} else {
panic!("Expected array");
}
}
#[test]
fn test_eval_index_access_array_slice_inclusive() {
let mut interp = Interpreter::new();
let result = interp.eval_string("[1, 2, 3, 4, 5][1..=3]").unwrap();
if let Value::Array(arr) = result {
assert_eq!(arr.len(), 3);
assert_eq!(arr[0], Value::Integer(2));
assert_eq!(arr[2], Value::Integer(4));
} else {
panic!("Expected array");
}
}
#[test]
fn test_eval_index_access_string_slice_exclusive() {
let mut interp = Interpreter::new();
let result = interp
.eval_string(r#"{ let s = "hello"; s[1..4] }"#)
.unwrap();
assert_eq!(result.to_string(), "\"ell\"");
}
#[test]
fn test_eval_index_access_tuple_positive() {
let mut interp = Interpreter::new();
let result = interp.eval_string("(10, 20, 30)[1]").unwrap();
assert_eq!(result, Value::Integer(20));
}
#[test]
fn test_eval_index_access_tuple_negative() {
let mut interp = Interpreter::new();
let result = interp.eval_string("(10, 20, 30)[-2]").unwrap();
assert_eq!(result, Value::Integer(20));
}
#[test]
fn test_eval_index_access_tuple_out_of_bounds() {
let mut interp = Interpreter::new();
let result = interp.eval_string("(1, 2)[5]");
assert!(result.is_err());
}
#[test]
fn test_eval_index_access_object_string_key() {
let mut interp = Interpreter::new();
let result = interp.eval_string("{\"a\": 42}[\"a\"]").unwrap();
assert_eq!(result, Value::Integer(42));
}
#[test]
fn test_eval_index_access_object_string_key_not_found() {
let mut interp = Interpreter::new();
let result = interp.eval_string("{\"a\": 42}[\"b\"]");
assert!(result.is_err());
}
#[test]
fn test_eval_index_access_invalid_index_type() {
let mut interp = Interpreter::new();
let result = interp.eval_string("[1, 2, 3][true]");
assert!(result.is_err());
}
#[test]
fn test_eval_index_access_invalid_object_type() {
let mut interp = Interpreter::new();
let result = interp.eval_string("{ let b = true; b[0] }");
assert!(result.is_err());
}
#[test]
fn test_eval_field_access_object_simple() {
let mut interp = Interpreter::new();
let result = interp.eval_string("{x: 10, y: 20}.x").unwrap();
assert_eq!(result, Value::Integer(10));
}
#[test]
fn test_eval_field_access_object_not_found() {
let mut interp = Interpreter::new();
let result = interp.eval_string("{x: 10}.y");
assert!(result.is_err());
}
#[test]
fn test_eval_field_access_tuple_field_0() {
let mut interp = Interpreter::new();
let result = interp.eval_string("(1, 2, 3).0").unwrap();
assert_eq!(result, Value::Integer(1));
}
#[test]
fn test_eval_field_access_tuple_field_1() {
let mut interp = Interpreter::new();
let result = interp.eval_string("(\"hello\", \"world\").1").unwrap();
assert_eq!(result.to_string(), "\"world\"");
}
#[test]
fn test_eval_field_access_invalid_type() {
let mut interp = Interpreter::new();
let result = interp.eval_string("42.foo");
assert!(result.is_err());
}
#[test]
fn test_eval_object_literal_simple() {
let mut interp = Interpreter::new();
let result = interp.eval_string("{a: 1, b: 2}").unwrap();
if let Value::Object(obj) = result {
assert_eq!(obj.get("a"), Some(&Value::Integer(1)));
assert_eq!(obj.get("b"), Some(&Value::Integer(2)));
} else {
panic!("Expected object");
}
}
#[test]
fn test_eval_object_literal_nested() {
let mut interp = Interpreter::new();
let result = interp.eval_string("{outer: {inner: 42}}").unwrap();
if let Value::Object(obj) = result {
if let Some(Value::Object(inner)) = obj.get("outer") {
assert_eq!(inner.get("inner"), Some(&Value::Integer(42)));
} else {
panic!("Expected inner object");
}
} else {
panic!("Expected object");
}
}
#[test]
fn test_eval_object_literal_with_expressions() {
let mut interp = Interpreter::new();
let result = interp.eval_string("{sum: 1 + 2, product: 3 * 4}").unwrap();
if let Value::Object(obj) = result {
assert_eq!(obj.get("sum"), Some(&Value::Integer(3)));
assert_eq!(obj.get("product"), Some(&Value::Integer(12)));
} else {
panic!("Expected object");
}
}
#[test]
fn test_qualified_name_unknown_module_method() {
let interp = Interpreter::new();
let result = interp.eval_qualified_name("Foo", "bar");
assert!(result.is_err());
}
#[test]
fn test_qualified_name_unknown_constructor() {
let interp = Interpreter::new();
let result = interp.eval_qualified_name("MyCustomType", "new");
assert!(result.is_err());
}
#[test]
fn test_constructor_access_non_new_field() {
let obj = HashMap::new();
let result = Interpreter::check_constructor_access(&obj, "foo");
assert!(result.is_none());
}
#[test]
fn test_constructor_access_actor() {
let mut obj = HashMap::new();
obj.insert(
"__type".to_string(),
Value::from_string("Actor".to_string()),
);
obj.insert(
"__name".to_string(),
Value::from_string("MyActor".to_string()),
);
let result = Interpreter::check_constructor_access(&obj, "new");
assert!(result.is_some());
if let Some(Value::String(s)) = result {
assert!(s.contains("__actor_constructor__:MyActor"));
}
}
#[test]
fn test_constructor_access_struct() {
let mut obj = HashMap::new();
obj.insert(
"__type".to_string(),
Value::from_string("Struct".to_string()),
);
obj.insert(
"__name".to_string(),
Value::from_string("MyStruct".to_string()),
);
let result = Interpreter::check_constructor_access(&obj, "new");
assert!(result.is_some());
if let Some(Value::String(s)) = result {
assert!(s.contains("__struct_constructor__:MyStruct"));
}
}
#[test]
fn test_constructor_access_class() {
let mut obj = HashMap::new();
obj.insert(
"__type".to_string(),
Value::from_string("Class".to_string()),
);
obj.insert(
"__name".to_string(),
Value::from_string("MyClass".to_string()),
);
let result = Interpreter::check_constructor_access(&obj, "new");
assert!(result.is_some());
if let Some(Value::String(s)) = result {
assert!(s.contains("__class_constructor__:MyClass:new"));
}
}
#[test]
fn test_constructor_access_unknown_type() {
let mut obj = HashMap::new();
obj.insert(
"__type".to_string(),
Value::from_string("Unknown".to_string()),
);
obj.insert("__name".to_string(), Value::from_string("Name".to_string()));
let result = Interpreter::check_constructor_access(&obj, "new");
assert!(result.is_none());
}
#[test]
fn test_constructor_access_missing_name() {
let mut obj = HashMap::new();
obj.insert(
"__type".to_string(),
Value::from_string("Struct".to_string()),
);
let result = Interpreter::check_constructor_access(&obj, "new");
assert!(result.is_none());
}
#[test]
fn test_actor_field_access_no_actor_id() {
let obj = HashMap::new();
let result = Interpreter::check_actor_field_access(&obj, "field");
assert!(result.is_ok());
assert!(result.unwrap().is_none());
}
#[test]
fn test_get_object_field_exists() {
let mut obj = HashMap::new();
obj.insert("name".to_string(), Value::from_string("Alice".to_string()));
let result = Interpreter::get_object_field(&obj, "name");
assert!(result.is_ok());
assert_eq!(result.unwrap(), Value::from_string("Alice".to_string()));
}
#[test]
fn test_get_object_field_not_found() {
let obj = HashMap::new();
let result = Interpreter::get_object_field(&obj, "missing");
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("no field named 'missing'"));
}
#[test]
fn test_check_struct_visibility_no_struct_type() {
let interp = Interpreter::new();
let obj = HashMap::new();
let result = interp.check_struct_visibility(&obj, "field");
assert!(result.is_ok());
}
#[test]
fn test_qualified_name_hashmap_new() {
let interp = Interpreter::new();
let result = interp.eval_qualified_name("HashMap", "new");
assert!(result.is_ok());
if let Value::String(s) = result.unwrap() {
assert_eq!(s.as_ref(), "__builtin_hashmap__");
} else {
panic!("Expected string");
}
}
#[test]
fn test_qualified_name_string_new() {
let interp = Interpreter::new();
let result = interp.eval_qualified_name("String", "new");
assert!(result.is_ok());
}
#[test]
fn test_qualified_name_string_from() {
let interp = Interpreter::new();
let result = interp.eval_qualified_name("String", "from");
assert!(result.is_ok());
}
#[test]
fn test_qualified_name_string_from_utf8() {
let interp = Interpreter::new();
let result = interp.eval_qualified_name("String", "from_utf8");
assert!(result.is_ok());
}
#[test]
fn test_qualified_name_command_new() {
let interp = Interpreter::new();
let result = interp.eval_qualified_name("Command", "new");
assert!(result.is_ok());
}
#[test]
fn test_qualified_name_unknown() {
let interp = Interpreter::new();
let result = interp.eval_qualified_name("Unknown", "method");
assert!(result.is_err());
}
#[test]
fn test_access_object_field_regular() {
let interp = Interpreter::new();
let mut obj = HashMap::new();
obj.insert("x".to_string(), Value::Integer(42));
let result = interp.access_object_field(&obj, "x");
assert!(result.is_ok());
assert_eq!(result.unwrap(), Value::Integer(42));
}
#[test]
fn test_access_object_field_constructor() {
let interp = Interpreter::new();
let mut obj = HashMap::new();
obj.insert(
"__type".to_string(),
Value::from_string("Struct".to_string()),
);
obj.insert(
"__name".to_string(),
Value::from_string("Point".to_string()),
);
let result = interp.access_object_field(&obj, "new");
assert!(result.is_ok());
}
#[test]
fn test_access_object_mut_field_regular() {
let interp = Interpreter::new();
let mut obj = HashMap::new();
obj.insert("y".to_string(), Value::Integer(100));
let cell = Arc::new(std::sync::Mutex::new(obj));
let result = interp.access_object_mut_field(&cell, "y");
assert!(result.is_ok());
assert_eq!(result.unwrap(), Value::Integer(100));
}
#[test]
fn test_eval_object_literal_empty() {
use crate::frontend::ast::ObjectField;
let mut interp = Interpreter::new();
let fields: Vec<ObjectField> = vec![];
let result = interp.eval_object_literal(&fields);
assert!(result.is_ok());
if let Value::Object(obj) = result.unwrap() {
assert!(obj.is_empty());
} else {
panic!("Expected object");
}
}
#[test]
fn test_index_array_positive() {
let arr = vec![Value::Integer(1), Value::Integer(2), Value::Integer(3)];
let result = Interpreter::index_array(&arr, 1);
assert!(result.is_ok());
assert_eq!(result.unwrap(), Value::Integer(2));
}
#[test]
fn test_index_array_negative() {
let arr = vec![Value::Integer(1), Value::Integer(2), Value::Integer(3)];
let result = Interpreter::index_array(&arr, -1);
assert!(result.is_ok());
assert_eq!(result.unwrap(), Value::Integer(3));
}
#[test]
fn test_index_array_out_of_bounds() {
let arr = vec![Value::Integer(1)];
let result = Interpreter::index_array(&arr, 5);
assert!(result.is_err());
}
#[test]
fn test_index_string_positive() {
let result = Interpreter::index_string("hello", 1);
assert!(result.is_ok());
assert_eq!(result.unwrap(), Value::from_string("e".to_string()));
}
#[test]
fn test_index_string_negative() {
let result = Interpreter::index_string("hello", -1);
assert!(result.is_ok());
assert_eq!(result.unwrap(), Value::from_string("o".to_string()));
}
#[test]
fn test_index_tuple() {
let tuple = vec![Value::Integer(10), Value::Integer(20)];
let result = Interpreter::index_tuple(&tuple, 0);
assert!(result.is_ok());
assert_eq!(result.unwrap(), Value::Integer(10));
}
#[test]
fn test_index_object() {
let mut obj = HashMap::new();
obj.insert("key".to_string(), Value::from_string("value".to_string()));
let result = Interpreter::index_object(&obj, "key");
assert!(result.is_ok());
assert_eq!(result.unwrap(), Value::from_string("value".to_string()));
}
#[test]
fn test_index_object_mut() {
let mut obj = HashMap::new();
obj.insert("data".to_string(), Value::Integer(42));
let cell = Arc::new(std::sync::Mutex::new(obj));
let result = Interpreter::index_object_mut(&cell, "data");
assert!(result.is_ok());
assert_eq!(result.unwrap(), Value::Integer(42));
}
#[test]
fn test_slice_string() {
let start = Value::Integer(0);
let end = Value::Integer(3);
let result = Interpreter::slice_string("hello", &start, &end, false);
assert!(result.is_ok());
assert_eq!(result.unwrap(), Value::from_string("hel".to_string()));
}
#[test]
fn test_slice_string_inclusive() {
let start = Value::Integer(0);
let end = Value::Integer(2);
let result = Interpreter::slice_string("hello", &start, &end, true);
assert!(result.is_ok());
assert_eq!(result.unwrap(), Value::from_string("he".to_string()));
}
#[test]
fn test_slice_array() {
let arr = vec![
Value::Integer(1),
Value::Integer(2),
Value::Integer(3),
Value::Integer(4),
];
let start = Value::Integer(1);
let end = Value::Integer(3);
let result = Interpreter::slice_array(&arr, &start, &end, false);
assert!(result.is_ok());
match result.unwrap() {
Value::Array(arr) => assert_eq!(arr.len(), 2),
_ => panic!("Expected array"),
}
}
#[test]
fn test_slice_array_inclusive() {
let arr = vec![
Value::Integer(1),
Value::Integer(2),
Value::Integer(3),
Value::Integer(4),
];
let start = Value::Integer(1);
let end = Value::Integer(3);
let result = Interpreter::slice_array(&arr, &start, &end, true);
assert!(result.is_ok());
match result.unwrap() {
Value::Array(arr) => assert_eq!(arr.len(), 3),
_ => panic!("Expected array"),
}
}
#[test]
fn test_index_dataframe_row() {
let columns = vec![DataFrameColumn {
name: "x".to_string(),
values: vec![Value::Integer(1), Value::Integer(2)],
}];
let result = Interpreter::index_dataframe_row(&columns, 0);
assert!(result.is_ok());
}
#[test]
fn test_index_dataframe_column() {
let columns = vec![DataFrameColumn {
name: "x".to_string(),
values: vec![Value::Integer(1), Value::Integer(2)],
}];
let result = Interpreter::index_dataframe_column(&columns, "x");
assert!(result.is_ok());
}
#[test]
fn test_index_dataframe_column_not_found() {
let columns = vec![DataFrameColumn {
name: "x".to_string(),
values: vec![Value::Integer(1)],
}];
let result = Interpreter::index_dataframe_column(&columns, "y");
assert!(result.is_err());
}
#[test]
fn test_eval_index_access_array_first_element() {
let mut interp = Interpreter::new();
let result = interp.eval_string("[100, 200, 300][0]").unwrap();
assert_eq!(result, Value::Integer(100));
}
#[test]
fn test_eval_index_access_array_last_element() {
let mut interp = Interpreter::new();
let result = interp.eval_string("[100, 200, 300][2]").unwrap();
assert_eq!(result, Value::Integer(300));
}
#[test]
fn test_eval_index_access_string_unicode() {
let mut interp = Interpreter::new();
let result = interp.eval_string(r#"{ let s = "abc"; s[1] }"#).unwrap();
assert_eq!(result.to_string(), "\"b\"");
}
#[test]
fn test_eval_index_access_array_negative_first() {
let mut interp = Interpreter::new();
let result = interp.eval_string("[1, 2, 3][-3]").unwrap();
assert_eq!(result, Value::Integer(1));
}
#[test]
fn test_eval_index_access_string_negative_first() {
let mut interp = Interpreter::new();
let result = interp.eval_string(r#"{ let s = "xyz"; s[-3] }"#).unwrap();
assert_eq!(result.to_string(), "\"x\"");
}
#[test]
fn test_eval_field_access_object_nested() {
let mut interp = Interpreter::new();
let result = interp.eval_string("{a: {b: 42}}.a").unwrap();
if let Value::Object(obj) = result {
assert_eq!(obj.get("b"), Some(&Value::Integer(42)));
} else {
panic!("Expected object");
}
}
#[test]
fn test_eval_field_access_tuple_field_2() {
let mut interp = Interpreter::new();
let result = interp.eval_string("(10, 20, 30).2").unwrap();
assert_eq!(result, Value::Integer(30));
}
#[test]
fn test_eval_object_literal_empty_via_string() {
let mut interp = Interpreter::new();
let result = interp.eval_string("{}").unwrap();
if let Value::Object(obj) = result {
assert!(obj.is_empty());
} else {
panic!("Expected object");
}
}
#[test]
fn test_eval_object_literal_string_values() {
let mut interp = Interpreter::new();
let result = interp
.eval_string("{name: \"Alice\", city: \"NYC\"}")
.unwrap();
if let Value::Object(obj) = result {
assert_eq!(obj.len(), 2);
} else {
panic!("Expected object");
}
}
#[test]
fn test_eval_object_literal_mixed_types() {
let mut interp = Interpreter::new();
let result = interp
.eval_string("{num: 42, flag: true, text: \"hello\"}")
.unwrap();
if let Value::Object(obj) = result {
assert_eq!(obj.get("num"), Some(&Value::Integer(42)));
assert_eq!(obj.get("flag"), Some(&Value::Bool(true)));
} else {
panic!("Expected object");
}
}
#[test]
fn test_eval_index_access_array_with_floats() {
let mut interp = Interpreter::new();
let result = interp.eval_string("[1.5, 2.5, 3.5][1]").unwrap();
assert_eq!(result, Value::Float(2.5));
}
#[test]
fn test_eval_index_access_array_with_bools() {
let mut interp = Interpreter::new();
let result = interp.eval_string("[true, false, true][0]").unwrap();
assert_eq!(result, Value::Bool(true));
}
#[test]
fn test_eval_index_access_string_slice_to_end() {
let mut interp = Interpreter::new();
let result = interp
.eval_string(r#"{ let s = "hello"; s[2..5] }"#)
.unwrap();
assert_eq!(result.to_string(), "\"llo\"");
}
#[test]
fn test_eval_index_access_tuple_with_strings() {
let mut interp = Interpreter::new();
let result = interp.eval_string("(\"a\", \"b\", \"c\")[1]").unwrap();
assert_eq!(result.to_string(), "\"b\"");
}
#[test]
fn test_eval_index_access_array_nested() {
let mut interp = Interpreter::new();
let result = interp.eval_string("[[1, 2], [3, 4]][0]").unwrap();
if let Value::Array(arr) = result {
assert_eq!(arr.len(), 2);
} else {
panic!("Expected array");
}
}
#[test]
fn test_eval_field_access_tuple_with_nested() {
let mut interp = Interpreter::new();
let result = interp.eval_string("((1, 2), (3, 4)).0").unwrap();
if let Value::Tuple(t) = result {
assert_eq!(t.len(), 2);
} else {
panic!("Expected tuple");
}
}
#[test]
fn test_check_field_visibility_no_struct() {
let interp = Interpreter::new();
let obj = HashMap::new();
let result = interp.check_struct_visibility(&obj, "field");
assert!(result.is_ok());
}
#[test]
fn test_check_field_visibility_with_struct_type() {
let interp = Interpreter::new();
let mut obj = HashMap::new();
obj.insert(
"__struct_type".to_string(),
Value::from_string("TestStruct".to_string()),
);
let result = interp.check_struct_visibility(&obj, "public_field");
assert!(result.is_ok());
}
#[test]
fn test_get_object_field_with_various_types() {
let mut obj = HashMap::new();
obj.insert("int".to_string(), Value::Integer(42));
obj.insert("float".to_string(), Value::Float(3.14));
obj.insert("bool".to_string(), Value::Bool(true));
obj.insert("nil".to_string(), Value::Nil);
assert_eq!(
Interpreter::get_object_field(&obj, "int").unwrap(),
Value::Integer(42)
);
assert_eq!(
Interpreter::get_object_field(&obj, "float").unwrap(),
Value::Float(3.14)
);
assert_eq!(
Interpreter::get_object_field(&obj, "bool").unwrap(),
Value::Bool(true)
);
assert_eq!(
Interpreter::get_object_field(&obj, "nil").unwrap(),
Value::Nil
);
}
#[test]
fn test_constructor_access_with_new_but_no_type() {
let obj = HashMap::new();
let result = Interpreter::check_constructor_access(&obj, "new");
assert!(result.is_none());
}
#[test]
fn test_constructor_access_enum_type() {
let mut obj = HashMap::new();
obj.insert("__type".to_string(), Value::from_string("Enum".to_string()));
obj.insert(
"__name".to_string(),
Value::from_string("MyEnum".to_string()),
);
let result = Interpreter::check_constructor_access(&obj, "new");
assert!(result.is_none());
}
#[test]
fn test_eval_index_access_array_negative_second() {
let mut interp = Interpreter::new();
let result = interp.eval_string("[1, 2, 3, 4, 5][-2]").unwrap();
assert_eq!(result, Value::Integer(4));
}
#[test]
fn test_eval_index_access_string_middle() {
let mut interp = Interpreter::new();
let result = interp.eval_string(r#"{ let s = "abcdef"; s[3] }"#).unwrap();
assert_eq!(result.to_string(), "\"d\"");
}
#[test]
fn test_eval_index_access_tuple_mixed_types() {
let mut interp = Interpreter::new();
let result = interp.eval_string("(1, 2.5, true)[2]").unwrap();
assert_eq!(result, Value::Bool(true));
}
#[test]
fn test_eval_object_literal_with_nil() {
let mut interp = Interpreter::new();
let result = interp.eval_string("{value: nil}").unwrap();
if let Value::Object(obj) = result {
assert_eq!(obj.get("value"), Some(&Value::Nil));
} else {
panic!("Expected object");
}
}
#[test]
fn test_index_object_mut_not_found() {
let obj = HashMap::new();
let cell = Arc::new(std::sync::Mutex::new(obj));
let result = Interpreter::index_object_mut(&cell, "missing");
assert!(result.is_err());
}
#[test]
fn test_slice_array_open_start() {
let arr = vec![Value::Integer(1), Value::Integer(2), Value::Integer(3)];
let result = Interpreter::slice_array(&arr, &Value::Nil, &Value::Integer(2), false);
assert!(result.is_ok());
if let Value::Array(sliced) = result.unwrap() {
assert_eq!(sliced.len(), 2);
} else {
panic!("Expected array");
}
}
#[test]
fn test_slice_array_open_end() {
let arr = vec![Value::Integer(1), Value::Integer(2), Value::Integer(3)];
let result = Interpreter::slice_array(&arr, &Value::Integer(1), &Value::Nil, false);
assert!(result.is_ok());
if let Value::Array(sliced) = result.unwrap() {
assert_eq!(sliced.len(), 2);
} else {
panic!("Expected array");
}
}
#[test]
fn test_slice_array_negative_indices() {
let arr = vec![
Value::Integer(1),
Value::Integer(2),
Value::Integer(3),
Value::Integer(4),
];
let result =
Interpreter::slice_array(&arr, &Value::Integer(-3), &Value::Integer(-1), false);
assert!(result.is_ok());
if let Value::Array(sliced) = result.unwrap() {
assert_eq!(sliced.len(), 2);
} else {
panic!("Expected array");
}
}
#[test]
fn test_slice_string_open_start() {
let result = Interpreter::slice_string("hello", &Value::Nil, &Value::Integer(3), false);
assert!(result.is_ok());
assert_eq!(result.unwrap().to_string(), "\"hel\"");
}
#[test]
fn test_slice_string_open_end() {
let result = Interpreter::slice_string("hello", &Value::Integer(2), &Value::Nil, false);
assert!(result.is_ok());
assert_eq!(result.unwrap().to_string(), "\"llo\"");
}
#[test]
fn test_slice_string_negative_start() {
let result = Interpreter::slice_string("hello", &Value::Integer(-3), &Value::Nil, false);
assert!(result.is_ok());
assert_eq!(result.unwrap().to_string(), "\"llo\"");
}
#[test]
fn test_slice_string_negative_end() {
let result =
Interpreter::slice_string("hello", &Value::Integer(0), &Value::Integer(-2), false);
assert!(result.is_ok());
assert_eq!(result.unwrap().to_string(), "\"hel\"");
}
#[test]
fn test_index_dataframe_row_multiple_columns() {
let columns = vec![
DataFrameColumn {
name: "a".to_string(),
values: vec![Value::Integer(1), Value::Integer(2)],
},
DataFrameColumn {
name: "b".to_string(),
values: vec![
Value::from_string("x".to_string()),
Value::from_string("y".to_string()),
],
},
];
let result = Interpreter::index_dataframe_row(&columns, 1);
assert!(result.is_ok());
if let Value::Object(row) = result.unwrap() {
assert_eq!(row.get("a"), Some(&Value::Integer(2)));
} else {
panic!("Expected object");
}
}
#[test]
fn test_index_dataframe_row_out_of_bounds() {
let columns = vec![DataFrameColumn {
name: "col".to_string(),
values: vec![Value::Integer(1)],
}];
let result = Interpreter::index_dataframe_row(&columns, 10);
assert!(result.is_err());
}
#[test]
fn test_index_dataframe_row_empty() {
let columns: Vec<DataFrameColumn> = vec![];
let result = Interpreter::index_dataframe_row(&columns, 0);
assert!(result.is_err());
}
#[test]
fn test_access_object_field_missing() {
let interp = Interpreter::new();
let obj = HashMap::new();
let result = interp.access_object_field(&obj, "missing");
assert!(result.is_err());
}
#[test]
fn test_access_object_mut_field_missing() {
let interp = Interpreter::new();
let obj = HashMap::new();
let cell = Arc::new(std::sync::Mutex::new(obj));
let result = interp.access_object_mut_field(&cell, "missing");
assert!(result.is_err());
}
#[test]
fn test_eval_field_access_object_with_kind() {
let mut interp = Interpreter::new();
let result = interp
.eval_string("{kind: \"Point\", x: 10, y: 20}.kind")
.unwrap();
assert_eq!(result.to_string(), "\"Point\"");
}
#[test]
fn test_eval_index_access_array_single_element() {
let mut interp = Interpreter::new();
let result = interp.eval_string("[42][0]").unwrap();
assert_eq!(result, Value::Integer(42));
}
#[test]
fn test_eval_index_access_tuple_single_element() {
let mut interp = Interpreter::new();
let result = interp.eval_string("(42,)[0]").unwrap();
assert_eq!(result, Value::Integer(42));
}
#[test]
fn test_eval_index_access_string_single_char() {
let mut interp = Interpreter::new();
let result = interp.eval_string("\"x\"[0]").unwrap();
assert_eq!(result.to_string(), "\"x\"");
}
#[test]
fn test_check_actor_field_access_no_actor_id() {
let obj = HashMap::new();
let result = Interpreter::check_actor_field_access(&obj, "field");
assert!(result.is_ok());
assert!(result.unwrap().is_none());
}
#[test]
fn test_check_actor_field_access_with_regular_field() {
let mut obj = HashMap::new();
obj.insert("regular".to_string(), Value::Integer(42));
let result = Interpreter::check_actor_field_access(&obj, "regular");
assert!(result.is_ok());
assert!(result.unwrap().is_none());
}
#[test]
fn test_eval_qualified_name_hashmap_new() {
let interp = Interpreter::new();
let result = interp.eval_qualified_name("HashMap", "new").unwrap();
assert_eq!(
result,
Value::from_string("__builtin_hashmap__".to_string())
);
}
#[test]
fn test_eval_qualified_name_string_new() {
let interp = Interpreter::new();
let result = interp.eval_qualified_name("String", "new").unwrap();
assert_eq!(
result,
Value::from_string("__builtin_String_new__".to_string())
);
}
#[test]
fn test_eval_qualified_name_string_from() {
let interp = Interpreter::new();
let result = interp.eval_qualified_name("String", "from").unwrap();
assert_eq!(
result,
Value::from_string("__builtin_String_from__".to_string())
);
}
#[test]
fn test_eval_qualified_name_string_from_utf8() {
let interp = Interpreter::new();
let result = interp
.eval_qualified_name("String", "from_utf8")
.unwrap();
assert_eq!(
result,
Value::from_string("__builtin_String_from_utf8__".to_string())
);
}
#[test]
fn test_eval_qualified_name_command_new() {
let interp = Interpreter::new();
let result = interp.eval_qualified_name("Command", "new").unwrap();
assert_eq!(
result,
Value::from_string("__builtin_command_new__".to_string())
);
}
#[test]
fn test_eval_qualified_name_class_constructor() {
let mut interp = Interpreter::new();
let mut class_info = HashMap::new();
class_info.insert(
"__type".to_string(),
Value::from_string("Class".to_string()),
);
interp.set_variable("MyClass", Value::Object(Arc::new(class_info)));
let result = interp.eval_qualified_name("MyClass", "new").unwrap();
assert_eq!(
result,
Value::from_string("__class_constructor__:MyClass".to_string())
);
}
#[test]
fn test_eval_qualified_name_struct_constructor() {
let mut interp = Interpreter::new();
let mut struct_info = HashMap::new();
struct_info.insert(
"__type".to_string(),
Value::from_string("Struct".to_string()),
);
interp.set_variable("Point", Value::Object(Arc::new(struct_info)));
let result = interp.eval_qualified_name("Point", "new").unwrap();
assert_eq!(
result,
Value::from_string("__struct_constructor__:Point".to_string())
);
}
#[test]
fn test_eval_qualified_name_actor_constructor() {
let mut interp = Interpreter::new();
let mut actor_info = HashMap::new();
actor_info.insert(
"__type".to_string(),
Value::from_string("Actor".to_string()),
);
interp.set_variable("Counter", Value::Object(Arc::new(actor_info)));
let result = interp.eval_qualified_name("Counter", "new").unwrap();
assert_eq!(
result,
Value::from_string("__actor_constructor__:Counter".to_string())
);
}
#[test]
fn test_eval_qualified_name_unknown_new() {
let interp = Interpreter::new();
let result = interp.eval_qualified_name("Unknown", "new");
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("Unknown qualified name"));
}
#[test]
fn test_eval_qualified_name_impl_method() {
let mut interp = Interpreter::new();
interp.set_variable(
"Logger::new_with_options",
Value::Integer(42),
);
let result = interp
.eval_qualified_name("Logger", "new_with_options")
.unwrap();
assert_eq!(result, Value::Integer(42));
}
#[test]
fn test_eval_qualified_name_unknown_method() {
let interp = Interpreter::new();
let result = interp.eval_qualified_name("Foo", "bar");
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("Unknown qualified name: Foo::bar"));
}
#[test]
fn test_eval_qualified_name_user_defined_new() {
let mut interp = Interpreter::new();
interp.set_variable("Widget::new", Value::Integer(999));
let result = interp.eval_qualified_name("Widget", "new").unwrap();
assert_eq!(result, Value::Integer(999));
}
#[test]
fn test_qualified_name_new_with_unknown_type_marker() {
let mut interp = Interpreter::new();
let mut obj = std::collections::HashMap::new();
obj.insert(
"__type".to_string(),
Value::from_string("SomeOtherType".to_string()),
);
interp.set_variable("FooBar", Value::Object(Arc::new(obj)));
let result = interp.eval_qualified_name("FooBar", "new");
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("Unknown qualified name: FooBar::new"));
}
#[test]
fn test_qualified_name_new_with_non_object_var() {
let mut interp = Interpreter::new();
interp.set_variable("SimpleVal", Value::Integer(42));
let result = interp.eval_qualified_name("SimpleVal", "new");
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("Unknown qualified name: SimpleVal::new"));
}
#[test]
fn test_qualified_name_new_with_empty_object() {
let mut interp = Interpreter::new();
let obj = std::collections::HashMap::new();
interp.set_variable("EmptyObj", Value::Object(Arc::new(obj)));
let result = interp.eval_qualified_name("EmptyObj", "new");
assert!(result.is_err());
}
#[test]
fn test_qualified_name_non_new_method_found() {
let mut interp = Interpreter::new();
interp.set_variable("MyModule::helper", Value::Integer(123));
let result = interp.eval_qualified_name("MyModule", "helper").unwrap();
assert_eq!(result, Value::Integer(123));
}
#[test]
fn test_qualified_name_non_new_method_not_found() {
let interp = Interpreter::new();
let result = interp.eval_qualified_name("NoModule", "missing_method");
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("Unknown qualified name: NoModule::missing_method"));
}
#[test]
fn test_eval_field_access_enum_variant() {
use crate::frontend::ast::{Expr, ExprKind, Span};
let mut interp = Interpreter::new();
let mut enum_obj = std::collections::HashMap::new();
enum_obj.insert("__type".to_string(), Value::from_string("Enum".to_string()));
enum_obj.insert("__name".to_string(), Value::from_string("Color".to_string()));
interp.set_variable("Color", Value::Object(Arc::new(enum_obj)));
let object_expr = Expr::new(ExprKind::Identifier("Color".to_string()), Span::default());
let result = interp.eval_field_access(&object_expr, "Red");
assert!(result.is_ok(), "Enum variant access should succeed: {:?}", result.err());
if let Ok(Value::EnumVariant { enum_name, variant_name, data }) = result {
assert_eq!(enum_name, "Color");
assert_eq!(variant_name, "Red");
assert!(data.is_none(), "Unit variant should have no data");
} else {
panic!("Expected EnumVariant value");
}
}
#[test]
fn test_eval_field_access_struct() {
use crate::frontend::ast::{Expr, ExprKind, Span};
let mut interp = Interpreter::new();
let mut fields = std::collections::HashMap::new();
fields.insert("x".to_string(), Value::Integer(10));
fields.insert("y".to_string(), Value::Integer(20));
interp.set_variable("point", Value::Struct {
name: "Point".to_string(),
fields: Arc::new(fields),
});
let object_expr = Expr::new(ExprKind::Identifier("point".to_string()), Span::default());
let result = interp.eval_field_access(&object_expr, "x");
assert!(result.is_ok(), "Struct field access should succeed");
assert_eq!(result.unwrap(), Value::Integer(10));
}
#[test]
fn test_eval_field_access_struct_missing_field() {
use crate::frontend::ast::{Expr, ExprKind, Span};
let mut interp = Interpreter::new();
let fields = std::collections::HashMap::new();
interp.set_variable("empty_struct", Value::Struct {
name: "Empty".to_string(),
fields: Arc::new(fields),
});
let object_expr = Expr::new(ExprKind::Identifier("empty_struct".to_string()), Span::default());
let result = interp.eval_field_access(&object_expr, "nonexistent");
assert!(result.is_err(), "Missing struct field should error");
assert!(result.unwrap_err().to_string().contains("not found in struct"));
}
#[test]
fn test_eval_field_access_class() {
use crate::frontend::ast::{Expr, ExprKind, Span};
use std::sync::RwLock;
let mut interp = Interpreter::new();
let mut fields = std::collections::HashMap::new();
fields.insert("name".to_string(), Value::from_string("Alice".to_string()));
interp.set_variable("person", Value::Class {
class_name: "Person".to_string(),
fields: Arc::new(RwLock::new(fields)),
methods: Arc::new(std::collections::HashMap::new()),
});
let object_expr = Expr::new(ExprKind::Identifier("person".to_string()), Span::default());
let result = interp.eval_field_access(&object_expr, "name");
assert!(result.is_ok(), "Class field access should succeed");
assert_eq!(result.unwrap(), Value::from_string("Alice".to_string()));
}
#[test]
fn test_eval_field_access_class_missing_field() {
use crate::frontend::ast::{Expr, ExprKind, Span};
use std::sync::RwLock;
let mut interp = Interpreter::new();
let fields = std::collections::HashMap::new();
interp.set_variable("empty_person", Value::Class {
class_name: "Person".to_string(),
fields: Arc::new(RwLock::new(fields)),
methods: Arc::new(std::collections::HashMap::new()),
});
let object_expr = Expr::new(ExprKind::Identifier("empty_person".to_string()), Span::default());
let result = interp.eval_field_access(&object_expr, "nonexistent");
assert!(result.is_err(), "Missing class field should error");
assert!(result.unwrap_err().to_string().contains("not found in class"));
}
#[test]
fn test_eval_field_access_tuple() {
use crate::frontend::ast::{Expr, ExprKind, Span};
let mut interp = Interpreter::new();
interp.set_variable("tup", Value::Tuple(Arc::from(vec![
Value::Integer(10),
Value::Integer(20),
Value::Integer(30),
])));
let object_expr = Expr::new(ExprKind::Identifier("tup".to_string()), Span::default());
let result = interp.eval_field_access(&object_expr, "0");
assert!(result.is_ok(), "Tuple field access should succeed");
assert_eq!(result.unwrap(), Value::Integer(10));
}
#[test]
fn test_eval_field_access_tuple_second_element() {
use crate::frontend::ast::{Expr, ExprKind, Span};
let mut interp = Interpreter::new();
interp.set_variable("tup2", Value::Tuple(Arc::from(vec![
Value::from_string("a".to_string()),
Value::from_string("b".to_string()),
])));
let object_expr = Expr::new(ExprKind::Identifier("tup2".to_string()), Span::default());
let result = interp.eval_field_access(&object_expr, "1");
assert!(result.is_ok(), "Tuple second element access should succeed");
assert_eq!(result.unwrap(), Value::from_string("b".to_string()));
}
#[test]
fn test_eval_field_access_non_object_error() {
use crate::frontend::ast::{Expr, ExprKind, Span};
let mut interp = Interpreter::new();
interp.set_variable("num", Value::Integer(42));
let object_expr = Expr::new(ExprKind::Identifier("num".to_string()), Span::default());
let result = interp.eval_field_access(&object_expr, "field");
assert!(result.is_err(), "Field access on integer should error");
assert!(result.unwrap_err().to_string().contains("Cannot access field"));
}
#[test]
fn test_eval_field_access_dataframe_column() {
use crate::frontend::ast::{Expr, ExprKind, Span};
let mut interp = Interpreter::new();
let columns = vec![
crate::runtime::value::DataFrameColumn {
name: "age".to_string(),
values: vec![Value::Integer(25), Value::Integer(30)],
},
];
interp.set_variable("df_val", Value::DataFrame { columns });
let object_expr = Expr::new(ExprKind::Identifier("df_val".to_string()), Span::default());
let result = interp.eval_field_access(&object_expr, "age");
assert!(result.is_ok(), "DataFrame column access should succeed: {:?}", result.err());
}
#[test]
fn test_eval_field_access_objectmut() {
use crate::frontend::ast::{Expr, ExprKind, Span};
use std::sync::Mutex;
let mut interp = Interpreter::new();
let mut obj = std::collections::HashMap::new();
obj.insert("value".to_string(), Value::Integer(42));
interp.set_variable("mut_obj", Value::ObjectMut(Arc::new(Mutex::new(obj))));
let object_expr = Expr::new(ExprKind::Identifier("mut_obj".to_string()), Span::default());
let result = interp.eval_field_access(&object_expr, "value");
assert!(result.is_ok(), "ObjectMut field access should succeed");
assert_eq!(result.unwrap(), Value::Integer(42));
}
}