use crate::frontend::ast::{Expr, ObjectField, StructField};
use crate::runtime::interpreter::DataFrameColumn;
use crate::runtime::{InterpreterError, Value};
use std::collections::HashMap;
use std::sync::Arc;
pub fn eval_object_literal<F>(
fields: &[ObjectField],
mut eval_expr: F,
) -> Result<Value, InterpreterError>
where
F: FnMut(&Expr) -> Result<Value, InterpreterError>,
{
let mut field_map = HashMap::new();
for field in fields {
match field {
ObjectField::KeyValue { key, value } => {
let evaluated_value = eval_expr(value)?;
field_map.insert(key.clone(), evaluated_value);
}
ObjectField::Spread { expr } => {
let spread_value = eval_expr(expr)?;
if let Value::Object(spread_fields) = spread_value {
field_map.extend(spread_fields.as_ref().clone());
} else {
return Err(InterpreterError::TypeError(
"Spread operator can only be used with objects".to_string(),
));
}
}
}
}
Ok(Value::Object(Arc::new(field_map)))
}
pub fn eval_struct_def(name: &str, fields: &[StructField]) -> Result<Value, InterpreterError> {
let _struct_name = name.to_string();
let _field_names = fields.to_vec();
Ok(Value::Nil) }
pub fn eval_field_access(object: &Value, field: &str) -> Result<Value, InterpreterError> {
match object {
Value::Object(fields) => fields.get(field).cloned().ok_or_else(|| {
InterpreterError::RuntimeError(format!("Field '{field}' not found in object"))
}),
Value::ObjectMut(cell) => {
cell.lock()
.expect("ObjectMut mutex should not be poisoned")
.get(field)
.cloned()
.ok_or_else(|| {
InterpreterError::RuntimeError(format!("Field '{field}' not found in object"))
})
}
Value::Struct { name, fields } => fields.get(field).cloned().ok_or_else(|| {
InterpreterError::RuntimeError(format!("Field '{field}' not found in struct {name}"))
}),
Value::DataFrame { columns } => eval_dataframe_field_access(columns, field),
Value::Tuple(elements) => eval_tuple_field_access(elements, field),
_ => Err(InterpreterError::TypeError(format!(
"Cannot access field '{}' on value of type {}",
field,
object.type_name()
))),
}
}
pub fn eval_index_access<F>(
container: &Value,
index: &Expr,
mut eval_expr: F,
) -> Result<Value, InterpreterError>
where
F: FnMut(&Expr) -> Result<Value, InterpreterError>,
{
let index_value = eval_expr(index)?;
match container {
Value::Array(arr) => {
if let Value::Integer(idx) = index_value {
let idx = idx as usize;
if idx < arr.len() {
Ok(arr[idx].clone())
} else {
Err(InterpreterError::RuntimeError(format!(
"Array index {} out of bounds (length: {})",
idx,
arr.len()
)))
}
} else {
Err(InterpreterError::TypeError(
"Array index must be an integer".to_string(),
))
}
}
Value::Object(fields) => {
match index_value {
Value::String(key) => fields.get(&*key).cloned().ok_or_else(|| {
InterpreterError::RuntimeError(format!("Key '{key}' not found in object"))
}),
Value::Atom(key) => {
let atom_key = format!(":{key}");
fields.get(&atom_key).cloned().ok_or_else(|| {
InterpreterError::RuntimeError(format!("Key ':{key}' not found in object"))
})
}
_ => Err(InterpreterError::TypeError(
"Object index must be a string or atom".to_string(),
)),
}
}
Value::String(s) => {
if let Value::Integer(idx) = index_value {
let idx = idx as usize;
if idx < s.len() {
let char_at = s.chars().nth(idx).ok_or_else(|| {
InterpreterError::RuntimeError("Invalid string index".to_string())
})?;
Ok(Value::from_string(char_at.to_string()))
} else {
Err(InterpreterError::RuntimeError(format!(
"String index {} out of bounds (length: {})",
idx,
s.len()
)))
}
} else {
Err(InterpreterError::TypeError(
"String index must be an integer".to_string(),
))
}
}
_ => Err(InterpreterError::TypeError(format!(
"Cannot index value of type {}",
container.type_name()
))),
}
}
pub fn eval_slice_access<F>(
container: &Value,
start: Option<&Expr>,
end: Option<&Expr>,
mut eval_expr: F,
) -> Result<Value, InterpreterError>
where
F: FnMut(&Expr) -> Result<Value, InterpreterError>,
{
match container {
Value::Array(arr) => {
let start_idx = if let Some(start_expr) = start {
if let Value::Integer(idx) = eval_expr(start_expr)? {
idx.max(0) as usize
} else {
return Err(InterpreterError::TypeError(
"Slice start index must be an integer".to_string(),
));
}
} else {
0
};
let end_idx = if let Some(end_expr) = end {
if let Value::Integer(idx) = eval_expr(end_expr)? {
(idx.max(0) as usize).min(arr.len())
} else {
return Err(InterpreterError::TypeError(
"Slice end index must be an integer".to_string(),
));
}
} else {
arr.len()
};
if start_idx <= end_idx && start_idx <= arr.len() {
let sliced = arr[start_idx..end_idx].to_vec();
Ok(Value::from_array(sliced))
} else {
Err(InterpreterError::RuntimeError(
"Invalid slice indices".to_string(),
))
}
}
Value::String(s) => eval_string_slice(s, start, end, eval_expr),
_ => Err(InterpreterError::TypeError(format!(
"Cannot slice value of type {}",
container.type_name()
))),
}
}
pub fn eval_object_spread<F>(
base_objects: &[Expr],
additional_fields: &[ObjectField],
mut eval_expr: F,
) -> Result<Value, InterpreterError>
where
F: FnMut(&Expr) -> Result<Value, InterpreterError>,
{
let mut result_fields = HashMap::new();
for base_expr in base_objects {
let base_value = eval_expr(base_expr)?;
match base_value {
Value::Object(fields) => {
result_fields.extend(fields.as_ref().clone());
}
_ => {
return Err(InterpreterError::TypeError(
"Can only spread object values".to_string(),
))
}
}
}
let additional_object = eval_object_literal(additional_fields, eval_expr)?;
if let Value::Object(additional_fields) = additional_object {
result_fields.extend(additional_fields.as_ref().clone());
}
Ok(Value::Object(Arc::new(result_fields)))
}
pub fn eval_array_spread<F>(
elements: &[ArrayElement],
mut eval_expr: F,
) -> Result<Value, InterpreterError>
where
F: FnMut(&Expr) -> Result<Value, InterpreterError>,
{
let mut result_elements = Vec::new();
for element in elements {
match element {
ArrayElement::Value(expr) => {
let value = eval_expr(expr)?;
result_elements.push(value);
}
ArrayElement::Spread(expr) => {
let spread_value = eval_expr(expr)?;
match spread_value {
Value::Array(arr) => {
result_elements.extend(arr.iter().cloned());
}
_ => {
return Err(InterpreterError::TypeError(
"Can only spread array values in array literal".to_string(),
))
}
}
}
}
}
Ok(Value::from_array(result_elements))
}
pub fn eval_destructuring_assignment<F>(
pattern: &DestructuringPattern,
value: &Value,
assign_var: F,
) -> Result<(), InterpreterError>
where
F: FnMut(&str, Value) -> Result<(), InterpreterError>,
{
match pattern {
DestructuringPattern::Object(field_patterns) => {
destructure_object(field_patterns, value, assign_var)
}
DestructuringPattern::Array(var_names) => destructure_array(var_names, value, assign_var),
}
}
fn destructure_object<F>(
field_patterns: &[(String, String)],
value: &Value,
mut assign_var: F,
) -> Result<(), InterpreterError>
where
F: FnMut(&str, Value) -> Result<(), InterpreterError>,
{
if let Value::Object(fields) = value {
for (field_name, var_name) in field_patterns {
if let Some(field_value) = fields.get(field_name) {
assign_var(var_name, field_value.clone())?;
} else {
return Err(InterpreterError::RuntimeError(format!(
"Field '{field_name}' not found in object"
)));
}
}
Ok(())
} else {
Err(InterpreterError::TypeError(
"Cannot destructure non-object value as object".to_string(),
))
}
}
fn destructure_array<F>(
var_names: &[String],
value: &Value,
mut assign_var: F,
) -> Result<(), InterpreterError>
where
F: FnMut(&str, Value) -> Result<(), InterpreterError>,
{
if let Value::Array(arr) = value {
if var_names.len() != arr.len() {
return Err(InterpreterError::RuntimeError(format!(
"Array destructuring length mismatch: {} variables, {} values",
var_names.len(),
arr.len()
)));
}
for (var_name, array_value) in var_names.iter().zip(arr.iter()) {
assign_var(var_name, array_value.clone())?;
}
Ok(())
} else {
Err(InterpreterError::TypeError(
"Cannot destructure non-array value as array".to_string(),
))
}
}
#[derive(Debug, Clone)]
pub enum ArrayElement {
Value(Expr),
Spread(Expr),
}
#[derive(Debug, Clone)]
pub enum DestructuringPattern {
Object(Vec<(String, String)>), Array(Vec<String>), }
fn create_identifier_expr(name: &str) -> Expr {
use crate::frontend::ast::{ExprKind, Span};
Expr::new(
ExprKind::Identifier(name.to_string()),
Span::new(0, name.len()),
)
}
fn eval_dataframe_field_access(
columns: &[DataFrameColumn],
field: &str,
) -> Result<Value, InterpreterError> {
for column in columns {
if column.name == field {
return Ok(Value::from_array(column.values.clone()));
}
}
Err(InterpreterError::RuntimeError(format!(
"Column '{field}' not found in DataFrame"
)))
}
pub fn eval_tuple_field_access(elements: &[Value], field: &str) -> Result<Value, InterpreterError> {
if let Ok(index) = field.parse::<usize>() {
if index < elements.len() {
Ok(elements[index].clone())
} else {
Err(InterpreterError::RuntimeError(format!(
"Tuple index {} out of bounds (length: {})",
index,
elements.len()
)))
}
} else {
Err(InterpreterError::TypeError(
"Tuple field access must use numeric indices".to_string(),
))
}
}
fn eval_string_slice<F>(
s: &str,
start: Option<&Expr>,
end: Option<&Expr>,
mut eval_expr: F,
) -> Result<Value, InterpreterError>
where
F: FnMut(&Expr) -> Result<Value, InterpreterError>,
{
let start_idx = if let Some(start_expr) = start {
if let Value::Integer(idx) = eval_expr(start_expr)? {
idx.max(0) as usize
} else {
return Err(InterpreterError::TypeError(
"String slice start index must be an integer".to_string(),
));
}
} else {
0
};
let end_idx = if let Some(end_expr) = end {
if let Value::Integer(idx) = eval_expr(end_expr)? {
(idx.max(0) as usize).min(s.len())
} else {
return Err(InterpreterError::TypeError(
"String slice end index must be an integer".to_string(),
));
}
} else {
s.len()
};
if start_idx <= end_idx && start_idx <= s.len() {
let sliced = &s[start_idx..end_idx];
Ok(Value::from_string(sliced.to_string()))
} else {
Err(InterpreterError::RuntimeError(
"Invalid string slice indices".to_string(),
))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::frontend::ast::{ExprKind, Literal, Span};
use std::sync::Arc;
#[test]
fn test_field_access_object() {
let mut fields = HashMap::new();
fields.insert("name".to_string(), Value::from_string("Alice".to_string()));
fields.insert("age".to_string(), Value::Integer(30));
let obj = Value::Object(Arc::new(fields));
let name_result =
eval_field_access(&obj, "name").expect("operation should succeed in test");
assert_eq!(name_result, Value::from_string("Alice".to_string()));
let age_result = eval_field_access(&obj, "age").expect("operation should succeed in test");
assert_eq!(age_result, Value::Integer(30));
let missing_result = eval_field_access(&obj, "missing");
assert!(missing_result.is_err());
}
#[test]
fn test_array_index_access() {
let arr = Value::Array(Arc::from(vec![
Value::Integer(10),
Value::Integer(20),
Value::Integer(30),
]));
let index_expr = Expr::new(
ExprKind::Literal(Literal::Integer(1, None)),
Span::new(0, 1),
);
let mut eval_count = 0;
let result = eval_index_access(&arr, &index_expr, |_| {
eval_count += 1;
Ok(Value::Integer(1))
})
.expect("operation should succeed in test");
assert_eq!(result, Value::Integer(20));
assert_eq!(eval_count, 1);
}
#[test]
fn test_array_slice() {
let arr = Value::Array(Arc::from(vec![
Value::Integer(1),
Value::Integer(2),
Value::Integer(3),
Value::Integer(4),
Value::Integer(5),
]));
let start_expr = Expr::new(
ExprKind::Literal(Literal::Integer(1, None)),
Span::new(0, 1),
);
let end_expr = Expr::new(
ExprKind::Literal(Literal::Integer(4, None)),
Span::new(0, 1),
);
let mut call_count = 0;
let result = eval_slice_access(&arr, Some(&start_expr), Some(&end_expr), |_| {
call_count += 1;
match call_count {
1 => Ok(Value::Integer(1)), 2 => Ok(Value::Integer(4)), _ => panic!("Unexpected call"),
}
})
.expect("operation should succeed in test");
if let Value::Array(sliced) = result {
assert_eq!(sliced.len(), 3);
assert_eq!(sliced[0], Value::Integer(2));
assert_eq!(sliced[1], Value::Integer(3));
assert_eq!(sliced[2], Value::Integer(4));
} else {
panic!("Expected array result");
}
}
#[test]
fn test_tuple_field_access() {
let tuple_elements = vec![
Value::Integer(42),
Value::from_string("hello".to_string()),
Value::Bool(true),
];
let result = eval_tuple_field_access(&tuple_elements, "1")
.expect("operation should succeed in test");
assert_eq!(result, Value::from_string("hello".to_string()));
let out_of_bounds = eval_tuple_field_access(&tuple_elements, "5");
assert!(out_of_bounds.is_err());
let invalid_index = eval_tuple_field_access(&tuple_elements, "invalid");
assert!(invalid_index.is_err());
}
#[test]
fn test_eval_object_literal_basic() {
let fields = vec![
ObjectField::KeyValue {
key: "name".to_string(),
value: Expr::new(
ExprKind::Literal(Literal::String("Alice".to_string())),
Span::new(0, 5),
),
},
ObjectField::KeyValue {
key: "age".to_string(),
value: Expr::new(
ExprKind::Literal(Literal::Integer(30, None)),
Span::new(6, 8),
),
},
];
let mut call_count = 0;
let result = eval_object_literal(&fields, |_expr| {
call_count += 1;
match call_count {
1 => Ok(Value::from_string("Alice".to_string())),
2 => Ok(Value::Integer(30)),
_ => panic!("Unexpected call"),
}
})
.expect("operation should succeed in test");
if let Value::Object(obj) = result {
assert_eq!(
obj.get("name"),
Some(&Value::from_string("Alice".to_string()))
);
assert_eq!(obj.get("age"), Some(&Value::Integer(30)));
} else {
panic!("Expected object");
}
}
#[test]
fn test_eval_object_literal_empty() {
let result =
eval_object_literal(&[], |_| Ok(Value::Nil)).expect("operation should succeed in test");
if let Value::Object(obj) = result {
assert_eq!(obj.len(), 0);
} else {
panic!("Expected empty object");
}
}
#[test]
fn test_eval_object_literal_with_spread() {
let mut base_fields = HashMap::new();
base_fields.insert("x".to_string(), Value::Integer(10));
base_fields.insert("y".to_string(), Value::Integer(20));
let fields = vec![
ObjectField::Spread {
expr: Expr::new(ExprKind::Identifier("base".to_string()), Span::new(0, 4)),
},
ObjectField::KeyValue {
key: "z".to_string(),
value: Expr::new(
ExprKind::Literal(Literal::Integer(30, None)),
Span::new(5, 7),
),
},
];
let mut call_count = 0;
let result = eval_object_literal(&fields, |_expr| {
call_count += 1;
match call_count {
1 => Ok(Value::Object(Arc::new(base_fields.clone()))),
2 => Ok(Value::Integer(30)),
_ => panic!("Unexpected call"),
}
})
.expect("operation should succeed in test");
if let Value::Object(obj) = result {
assert_eq!(obj.get("x"), Some(&Value::Integer(10)));
assert_eq!(obj.get("y"), Some(&Value::Integer(20)));
assert_eq!(obj.get("z"), Some(&Value::Integer(30)));
} else {
panic!("Expected object");
}
}
#[test]
fn test_eval_object_literal_spread_error() {
let fields = vec![ObjectField::Spread {
expr: Expr::new(
ExprKind::Literal(Literal::Integer(42, None)),
Span::new(0, 2),
),
}];
let result = eval_object_literal(&fields, |_| Ok(Value::Integer(42)));
assert!(result.is_err());
}
#[test]
fn test_eval_struct_def() {
let result = eval_struct_def("Point", &[]);
assert!(result.is_ok());
assert_eq!(
result.expect("operation should succeed in test"),
Value::Nil
);
}
#[test]
fn test_field_access_object_mut() {
let mut fields = HashMap::new();
fields.insert("value".to_string(), Value::Integer(42));
let obj = Value::ObjectMut(Arc::new(std::sync::Mutex::new(fields)));
let result = eval_field_access(&obj, "value").expect("operation should succeed in test");
assert_eq!(result, Value::Integer(42));
let missing = eval_field_access(&obj, "missing");
assert!(missing.is_err());
}
#[test]
fn test_field_access_struct() {
let mut fields = HashMap::new();
fields.insert("name".to_string(), Value::from_string("Alice".to_string()));
let s = Value::Struct {
name: "Person".to_string(),
fields: Arc::new(fields),
};
let result = eval_field_access(&s, "name").expect("operation should succeed in test");
assert_eq!(result, Value::from_string("Alice".to_string()));
let missing = eval_field_access(&s, "age");
assert!(missing.is_err());
}
#[test]
fn test_field_access_dataframe() {
let columns = vec![
DataFrameColumn {
name: "id".to_string(),
values: vec![Value::Integer(1), Value::Integer(2)],
},
DataFrameColumn {
name: "name".to_string(),
values: vec![
Value::from_string("Alice".to_string()),
Value::from_string("Bob".to_string()),
],
},
];
let df = Value::DataFrame { columns };
let result = eval_field_access(&df, "id").expect("operation should succeed in test");
if let Value::Array(arr) = result {
assert_eq!(arr.len(), 2);
assert_eq!(arr[0], Value::Integer(1));
} else {
panic!("Expected array");
}
let missing = eval_field_access(&df, "age");
assert!(missing.is_err());
}
#[test]
fn test_field_access_tuple() {
let tuple = Value::Tuple(Arc::from([
Value::Integer(1),
Value::Integer(2),
Value::Integer(3),
]));
let result = eval_field_access(&tuple, "0").expect("operation should succeed in test");
assert_eq!(result, Value::Integer(1));
let out_of_bounds = eval_field_access(&tuple, "5");
assert!(out_of_bounds.is_err());
}
#[test]
fn test_field_access_invalid_type() {
let result = eval_field_access(&Value::Integer(42), "field");
assert!(result.is_err());
}
#[test]
fn test_index_access_object() {
let mut fields = HashMap::new();
fields.insert("key1".to_string(), Value::Integer(100));
let obj = Value::Object(Arc::new(fields));
let index_expr = Expr::new(
ExprKind::Literal(Literal::String("key1".to_string())),
Span::new(0, 4),
);
let result = eval_index_access(&obj, &index_expr, |_| {
Ok(Value::from_string("key1".to_string()))
})
.expect("operation should succeed in test");
assert_eq!(result, Value::Integer(100));
}
#[test]
fn test_index_access_object_missing_key() {
let obj = Value::Object(Arc::new(HashMap::new()));
let index_expr = Expr::new(
ExprKind::Literal(Literal::String("missing".to_string())),
Span::new(0, 7),
);
let result = eval_index_access(&obj, &index_expr, |_| {
Ok(Value::from_string("missing".to_string()))
});
assert!(result.is_err());
}
#[test]
fn test_index_access_object_wrong_type() {
let obj = Value::Object(Arc::new(HashMap::new()));
let index_expr = Expr::new(
ExprKind::Literal(Literal::Integer(42, None)),
Span::new(0, 2),
);
let result = eval_index_access(&obj, &index_expr, |_| Ok(Value::Integer(42)));
assert!(result.is_err());
}
#[test]
fn test_index_access_array_out_of_bounds() {
let arr = Value::Array(Arc::from(vec![Value::Integer(1), Value::Integer(2)]));
let index_expr = Expr::new(
ExprKind::Literal(Literal::Integer(10, None)),
Span::new(0, 2),
);
let result = eval_index_access(&arr, &index_expr, |_| Ok(Value::Integer(10)));
assert!(result.is_err());
}
#[test]
fn test_index_access_array_wrong_type() {
let arr = Value::Array(Arc::from(vec![Value::Integer(1)]));
let index_expr = Expr::new(
ExprKind::Literal(Literal::String("not_int".to_string())),
Span::new(0, 7),
);
let result = eval_index_access(&arr, &index_expr, |_| {
Ok(Value::from_string("not_int".to_string()))
});
assert!(result.is_err());
}
#[test]
fn test_index_access_string() {
let s = Value::from_string("hello".to_string());
let index_expr = Expr::new(
ExprKind::Literal(Literal::Integer(1, None)),
Span::new(0, 1),
);
let result = eval_index_access(&s, &index_expr, |_| Ok(Value::Integer(1)))
.expect("operation should succeed in test");
assert_eq!(result, Value::from_string("e".to_string()));
}
#[test]
fn test_index_access_string_out_of_bounds() {
let s = Value::from_string("hi".to_string());
let index_expr = Expr::new(
ExprKind::Literal(Literal::Integer(10, None)),
Span::new(0, 2),
);
let result = eval_index_access(&s, &index_expr, |_| Ok(Value::Integer(10)));
assert!(result.is_err());
}
#[test]
fn test_index_access_string_wrong_type() {
let s = Value::from_string("hello".to_string());
let index_expr = Expr::new(ExprKind::Literal(Literal::Bool(true)), Span::new(0, 4));
let result = eval_index_access(&s, &index_expr, |_| Ok(Value::Bool(true)));
assert!(result.is_err());
}
#[test]
fn test_index_access_invalid_type() {
let result = eval_index_access(
&Value::Integer(42),
&Expr::new(
ExprKind::Literal(Literal::Integer(0, None)),
Span::new(0, 1),
),
|_| Ok(Value::Integer(0)),
);
assert!(result.is_err());
}
#[test]
fn test_parser082_atom_bracket_access_basic() {
let mut fields = HashMap::new();
fields.insert(
":host".to_string(),
Value::from_string("localhost".to_string()),
);
fields.insert(":port".to_string(), Value::Integer(8080));
let obj = Value::Object(Arc::new(fields));
let index_expr = Expr::new(
ExprKind::Literal(Literal::Atom("host".to_string())),
Span::new(0, 5),
);
let result = eval_index_access(&obj, &index_expr, |_| Ok(Value::Atom("host".into())))
.expect("atom bracket access should succeed");
assert_eq!(result, Value::from_string("localhost".to_string()));
}
#[test]
fn test_parser082_atom_bracket_access_port() {
let mut fields = HashMap::new();
fields.insert(
":host".to_string(),
Value::from_string("localhost".to_string()),
);
fields.insert(":port".to_string(), Value::Integer(8080));
let obj = Value::Object(Arc::new(fields));
let index_expr = Expr::new(
ExprKind::Literal(Literal::Atom("port".to_string())),
Span::new(0, 5),
);
let result = eval_index_access(&obj, &index_expr, |_| Ok(Value::Atom("port".into())))
.expect("atom bracket access should succeed");
assert_eq!(result, Value::Integer(8080));
}
#[test]
fn test_parser082_atom_bracket_access_missing_key() {
let mut fields = HashMap::new();
fields.insert(
":host".to_string(),
Value::from_string("localhost".to_string()),
);
let obj = Value::Object(Arc::new(fields));
let index_expr = Expr::new(
ExprKind::Literal(Literal::Atom("missing".to_string())),
Span::new(0, 7),
);
let result = eval_index_access(&obj, &index_expr, |_| Ok(Value::Atom("missing".into())));
assert!(result.is_err(), "missing atom key should return error");
}
#[test]
fn test_parser082_atom_bracket_access_mixed_keys() {
let mut fields = HashMap::new();
fields.insert(":status".to_string(), Value::Atom("ok".into()));
fields.insert("name".to_string(), Value::from_string("test".to_string()));
let obj = Value::Object(Arc::new(fields));
let atom_index = Expr::new(
ExprKind::Literal(Literal::Atom("status".to_string())),
Span::new(0, 7),
);
let atom_result =
eval_index_access(&obj, &atom_index, |_| Ok(Value::Atom("status".into())))
.expect("atom bracket access should succeed");
assert_eq!(atom_result, Value::Atom("ok".into()));
let string_index = Expr::new(
ExprKind::Literal(Literal::String("name".to_string())),
Span::new(0, 4),
);
let string_result = eval_index_access(&obj, &string_index, |_| {
Ok(Value::from_string("name".to_string()))
})
.expect("string bracket access should still work");
assert_eq!(string_result, Value::from_string("test".to_string()));
}
#[test]
fn test_slice_access_no_start() {
let arr = Value::Array(Arc::from(vec![
Value::Integer(1),
Value::Integer(2),
Value::Integer(3),
]));
let end_expr = Expr::new(
ExprKind::Literal(Literal::Integer(2, None)),
Span::new(0, 1),
);
let result = eval_slice_access(&arr, None, Some(&end_expr), |_| Ok(Value::Integer(2)))
.expect("operation should succeed in test");
if let Value::Array(sliced) = result {
assert_eq!(sliced.len(), 2);
assert_eq!(sliced[0], Value::Integer(1));
assert_eq!(sliced[1], Value::Integer(2));
} else {
panic!("Expected array");
}
}
#[test]
fn test_slice_access_no_end() {
let arr = Value::Array(Arc::from(vec![
Value::Integer(1),
Value::Integer(2),
Value::Integer(3),
]));
let start_expr = Expr::new(
ExprKind::Literal(Literal::Integer(1, None)),
Span::new(0, 1),
);
let result = eval_slice_access(&arr, Some(&start_expr), None, |_| Ok(Value::Integer(1)))
.expect("operation should succeed in test");
if let Value::Array(sliced) = result {
assert_eq!(sliced.len(), 2);
assert_eq!(sliced[0], Value::Integer(2));
assert_eq!(sliced[1], Value::Integer(3));
} else {
panic!("Expected array");
}
}
#[test]
fn test_slice_access_no_start_no_end() {
let arr = Value::Array(Arc::from(vec![Value::Integer(1), Value::Integer(2)]));
let result = eval_slice_access(&arr, None, None, |_| Ok(Value::Nil))
.expect("operation should succeed in test");
if let Value::Array(sliced) = result {
assert_eq!(sliced.len(), 2);
} else {
panic!("Expected array");
}
}
#[test]
fn test_slice_access_empty_slice() {
let arr = Value::Array(Arc::from(vec![Value::Integer(1), Value::Integer(2)]));
let start_expr = Expr::new(
ExprKind::Literal(Literal::Integer(1, None)),
Span::new(0, 1),
);
let end_expr = Expr::new(
ExprKind::Literal(Literal::Integer(1, None)),
Span::new(0, 1),
);
let result = eval_slice_access(&arr, Some(&start_expr), Some(&end_expr), |_| {
Ok(Value::Integer(1))
})
.expect("operation should succeed in test");
if let Value::Array(sliced) = result {
assert_eq!(sliced.len(), 0);
} else {
panic!("Expected empty array");
}
}
#[test]
fn test_slice_access_invalid_start_type() {
let arr = Value::Array(Arc::from(vec![Value::Integer(1)]));
let start_expr = Expr::new(ExprKind::Literal(Literal::Bool(true)), Span::new(0, 4));
let result = eval_slice_access(&arr, Some(&start_expr), None, |_| Ok(Value::Bool(true)));
assert!(result.is_err());
}
#[test]
fn test_slice_access_invalid_end_type() {
let arr = Value::Array(Arc::from(vec![Value::Integer(1)]));
let end_expr = Expr::new(
ExprKind::Literal(Literal::String("not_int".to_string())),
Span::new(0, 7),
);
let result = eval_slice_access(&arr, None, Some(&end_expr), |_| {
Ok(Value::from_string("not_int".to_string()))
});
assert!(result.is_err());
}
#[test]
fn test_slice_access_string() {
let s = Value::from_string("hello".to_string());
let start_expr = Expr::new(
ExprKind::Literal(Literal::Integer(1, None)),
Span::new(0, 1),
);
let end_expr = Expr::new(
ExprKind::Literal(Literal::Integer(4, None)),
Span::new(0, 1),
);
let mut call_count = 0;
let result = eval_slice_access(&s, Some(&start_expr), Some(&end_expr), |_| {
call_count += 1;
match call_count {
1 => Ok(Value::Integer(1)),
2 => Ok(Value::Integer(4)),
_ => panic!("Unexpected call"),
}
})
.expect("operation should succeed in test");
if let Value::String(sliced) = result {
assert_eq!(&*sliced, "ell");
} else {
panic!("Expected string");
}
}
#[test]
fn test_slice_access_invalid_type() {
let result = eval_slice_access(&Value::Integer(42), None, None, |_| Ok(Value::Nil));
assert!(result.is_err());
}
#[test]
fn test_destructuring_array() {
let arr = Value::Array(Arc::from(vec![
Value::Integer(1),
Value::Integer(2),
Value::Integer(3),
]));
let pattern =
DestructuringPattern::Array(vec!["a".to_string(), "b".to_string(), "c".to_string()]);
let mut assigned = HashMap::new();
let result = eval_destructuring_assignment(&pattern, &arr, |name, val| {
assigned.insert(name.to_string(), val);
Ok(())
});
assert!(result.is_ok());
assert_eq!(assigned.get("a"), Some(&Value::Integer(1)));
assert_eq!(assigned.get("b"), Some(&Value::Integer(2)));
assert_eq!(assigned.get("c"), Some(&Value::Integer(3)));
}
#[test]
fn test_destructuring_array_length_mismatch() {
let arr = Value::Array(Arc::from(vec![Value::Integer(1)]));
let pattern = DestructuringPattern::Array(vec!["a".to_string(), "b".to_string()]);
let result = eval_destructuring_assignment(&pattern, &arr, |_, _| Ok(()));
assert!(result.is_err());
}
#[test]
fn test_destructuring_array_wrong_type() {
let pattern = DestructuringPattern::Array(vec!["a".to_string()]);
let result = eval_destructuring_assignment(&pattern, &Value::Integer(42), |_, _| Ok(()));
assert!(result.is_err());
}
#[test]
fn test_destructuring_object() {
let mut fields = HashMap::new();
fields.insert("x".to_string(), Value::Integer(10));
fields.insert("y".to_string(), Value::Integer(20));
let obj = Value::Object(Arc::new(fields));
let pattern = DestructuringPattern::Object(vec![
("x".to_string(), "a".to_string()),
("y".to_string(), "b".to_string()),
]);
let mut assigned = HashMap::new();
let result = eval_destructuring_assignment(&pattern, &obj, |name, val| {
assigned.insert(name.to_string(), val);
Ok(())
});
assert!(result.is_ok());
assert_eq!(assigned.get("a"), Some(&Value::Integer(10)));
assert_eq!(assigned.get("b"), Some(&Value::Integer(20)));
}
#[test]
fn test_destructuring_object_missing_field() {
let obj = Value::Object(Arc::new(HashMap::new()));
let pattern = DestructuringPattern::Object(vec![("x".to_string(), "a".to_string())]);
let result = eval_destructuring_assignment(&pattern, &obj, |_, _| Ok(()));
assert!(result.is_err());
}
#[test]
fn test_destructuring_object_wrong_type() {
let pattern = DestructuringPattern::Object(vec![("x".to_string(), "a".to_string())]);
let result = eval_destructuring_assignment(&pattern, &Value::Integer(42), |_, _| Ok(()));
assert!(result.is_err());
}
}
#[cfg(test)]
mod round_130_tests {
use super::*;
#[test]
fn test_eval_struct_def_empty_r130() {
let fields: Vec<StructField> = vec![];
let result = eval_struct_def("EmptyStruct", &fields);
assert!(result.is_ok());
}
#[test]
fn test_eval_field_access_object_missing_field_r130() {
let obj = Value::Object(Arc::new(HashMap::new()));
let result = eval_field_access(&obj, "nonexistent");
assert!(result.is_err());
}
#[test]
fn test_eval_field_access_struct_r130() {
let mut fields = HashMap::new();
fields.insert("x".to_string(), Value::Integer(42));
let val = Value::Struct {
name: "Point".to_string(),
fields: Arc::new(fields),
};
let result = eval_field_access(&val, "x");
assert!(result.is_ok());
assert_eq!(result.unwrap(), Value::Integer(42));
}
#[test]
fn test_eval_field_access_struct_missing_r130() {
let fields = HashMap::new();
let val = Value::Struct {
name: "Empty".to_string(),
fields: Arc::new(fields),
};
let result = eval_field_access(&val, "missing");
assert!(result.is_err());
}
#[test]
fn test_eval_field_access_invalid_type_r130() {
let result = eval_field_access(&Value::Integer(42), "field");
assert!(result.is_err());
}
#[test]
fn test_eval_field_access_nil_r130() {
let result = eval_field_access(&Value::Nil, "field");
assert!(result.is_err());
}
#[test]
fn test_eval_field_access_float_r130() {
let result = eval_field_access(&Value::Float(3.14), "field");
assert!(result.is_err());
}
#[test]
fn test_eval_field_access_bool_r130() {
let result = eval_field_access(&Value::Bool(true), "field");
assert!(result.is_err());
}
#[test]
fn test_eval_field_access_array_r130() {
let arr = Value::from_array(vec![Value::Integer(1)]);
let result = eval_field_access(&arr, "field");
assert!(result.is_err());
}
#[test]
fn test_eval_tuple_field_access_valid_r130() {
let elements = vec![Value::Integer(1), Value::Integer(2), Value::Integer(3)];
let result = eval_tuple_field_access(&elements, "1");
assert!(result.is_ok());
assert_eq!(result.unwrap(), Value::Integer(2));
}
#[test]
fn test_eval_tuple_field_access_out_of_bounds_r130() {
let elements = vec![Value::Integer(1)];
let result = eval_tuple_field_access(&elements, "5");
assert!(result.is_err());
}
#[test]
fn test_eval_tuple_field_access_non_numeric_r130() {
let elements = vec![Value::Integer(1)];
let result = eval_tuple_field_access(&elements, "not_a_number");
assert!(result.is_err());
}
#[test]
fn test_eval_tuple_field_access_negative_r130() {
let elements = vec![Value::Integer(1)];
let result = eval_tuple_field_access(&elements, "-1");
assert!(result.is_err());
}
#[test]
fn test_eval_tuple_field_access_zero_r130() {
let elements = vec![Value::Integer(99), Value::Integer(100)];
let result = eval_tuple_field_access(&elements, "0");
assert!(result.is_ok());
assert_eq!(result.unwrap(), Value::Integer(99));
}
#[test]
fn test_eval_tuple_field_access_last_r130() {
let elements = vec![Value::Integer(1), Value::Integer(2), Value::Integer(99)];
let result = eval_tuple_field_access(&elements, "2");
assert!(result.is_ok());
assert_eq!(result.unwrap(), Value::Integer(99));
}
#[test]
fn test_eval_tuple_field_access_empty_r130() {
let elements: Vec<Value> = vec![];
let result = eval_tuple_field_access(&elements, "0");
assert!(result.is_err());
}
#[test]
fn test_destructuring_array_basic_r130() {
let arr = Value::from_array(vec![Value::Integer(1), Value::Integer(2)]);
let pattern = DestructuringPattern::Array(vec!["a".to_string(), "b".to_string()]);
let mut assigned = HashMap::new();
let result = eval_destructuring_assignment(&pattern, &arr, |name, val| {
assigned.insert(name.to_string(), val);
Ok(())
});
assert!(result.is_ok());
assert_eq!(assigned.get("a"), Some(&Value::Integer(1)));
assert_eq!(assigned.get("b"), Some(&Value::Integer(2)));
}
#[test]
fn test_destructuring_array_length_mismatch_r130() {
let arr = Value::from_array(vec![Value::Integer(1)]);
let pattern = DestructuringPattern::Array(vec!["a".to_string(), "b".to_string()]);
let result = eval_destructuring_assignment(&pattern, &arr, |_, _| Ok(()));
assert!(result.is_err());
}
#[test]
fn test_destructuring_object_r130() {
let mut fields = HashMap::new();
fields.insert("x".to_string(), Value::Integer(10));
fields.insert("y".to_string(), Value::Integer(20));
let obj = Value::Object(Arc::new(fields));
let pattern = DestructuringPattern::Object(vec![
("x".to_string(), "a".to_string()),
("y".to_string(), "b".to_string()),
]);
let mut assigned = HashMap::new();
let result = eval_destructuring_assignment(&pattern, &obj, |name, val| {
assigned.insert(name.to_string(), val);
Ok(())
});
assert!(result.is_ok());
assert_eq!(assigned.get("a"), Some(&Value::Integer(10)));
assert_eq!(assigned.get("b"), Some(&Value::Integer(20)));
}
#[test]
fn test_destructuring_object_missing_field_r130() {
let obj = Value::Object(Arc::new(HashMap::new()));
let pattern = DestructuringPattern::Object(vec![("missing".to_string(), "a".to_string())]);
let result = eval_destructuring_assignment(&pattern, &obj, |_, _| Ok(()));
assert!(result.is_err());
}
#[test]
fn test_destructuring_array_empty_r130() {
let arr = Value::from_array(vec![]);
let pattern = DestructuringPattern::Array(vec![]);
let result = eval_destructuring_assignment(&pattern, &arr, |_, _| Ok(()));
assert!(result.is_ok());
}
#[test]
fn test_destructuring_object_empty_r130() {
let obj = Value::Object(Arc::new(HashMap::new()));
let pattern = DestructuringPattern::Object(vec![]);
let result = eval_destructuring_assignment(&pattern, &obj, |_, _| Ok(()));
assert!(result.is_ok());
}
#[test]
fn test_destructuring_array_single_r130() {
let arr = Value::from_array(vec![Value::Integer(42)]);
let pattern = DestructuringPattern::Array(vec!["x".to_string()]);
let mut assigned_val = Value::Nil;
let result = eval_destructuring_assignment(&pattern, &arr, |_, val| {
assigned_val = val;
Ok(())
});
assert!(result.is_ok());
assert_eq!(assigned_val, Value::Integer(42));
}
#[test]
fn test_destructuring_object_single_r130() {
let mut fields = HashMap::new();
fields.insert("name".to_string(), Value::String(Arc::from("test")));
let obj = Value::Object(Arc::new(fields));
let pattern = DestructuringPattern::Object(vec![("name".to_string(), "n".to_string())]);
let mut assigned_val = Value::Nil;
let result = eval_destructuring_assignment(&pattern, &obj, |_, val| {
assigned_val = val;
Ok(())
});
assert!(result.is_ok());
assert_eq!(assigned_val, Value::String(Arc::from("test")));
}
#[test]
fn test_eval_object_literal_empty() {
let fields: Vec<ObjectField> = vec![];
let result = eval_object_literal(&fields, |_| Ok(Value::Nil)).unwrap();
match result {
Value::Object(map) => assert!(map.is_empty()),
_ => panic!("Expected object"),
}
}
#[test]
fn test_eval_object_literal_multiple_fields() {
let fields: Vec<ObjectField> = vec![];
let result = eval_object_literal(&fields, |_| Ok(Value::Integer(42))).unwrap();
match result {
Value::Object(map) => assert!(map.is_empty()),
_ => panic!("Expected object"),
}
}
#[test]
fn test_eval_field_access_nested_object() {
let mut inner = HashMap::new();
inner.insert("value".to_string(), Value::Integer(99));
let mut outer = HashMap::new();
outer.insert("inner".to_string(), Value::Object(Arc::new(inner)));
let obj = Value::Object(Arc::new(outer));
let result = eval_field_access(&obj, "inner").unwrap();
match result {
Value::Object(map) => {
assert_eq!(map.get("value"), Some(&Value::Integer(99)));
}
_ => panic!("Expected object"),
}
}
#[test]
fn test_eval_field_access_struct() {
let mut fields = HashMap::new();
fields.insert("x".to_string(), Value::Integer(5));
fields.insert("y".to_string(), Value::Integer(10));
let s = Value::Struct {
name: "Point".to_string(),
fields: Arc::new(fields),
};
let result_x = eval_field_access(&s, "x").unwrap();
let result_y = eval_field_access(&s, "y").unwrap();
assert_eq!(result_x, Value::Integer(5));
assert_eq!(result_y, Value::Integer(10));
}
#[test]
fn test_eval_field_access_missing_field() {
let fields: HashMap<String, Value> = HashMap::new();
let obj = Value::Object(Arc::new(fields));
let result = eval_field_access(&obj, "missing");
assert!(result.is_err());
}
#[test]
fn test_destructuring_array_single() {
let arr = Value::Array(Arc::from(vec![Value::Integer(100)]));
let pattern = DestructuringPattern::Array(vec!["first".to_string()]);
let mut assigned = Value::Nil;
let result = eval_destructuring_assignment(&pattern, &arr, |_, val| {
assigned = val;
Ok(())
});
assert!(result.is_ok());
assert_eq!(assigned, Value::Integer(100));
}
#[test]
fn test_destructuring_array_multiple() {
let arr = Value::Array(Arc::from(vec![
Value::Integer(1),
Value::Integer(2),
Value::Integer(3),
]));
let pattern =
DestructuringPattern::Array(vec!["a".to_string(), "b".to_string(), "c".to_string()]);
let mut count = 0;
let result = eval_destructuring_assignment(&pattern, &arr, |_, _| {
count += 1;
Ok(())
});
assert!(result.is_ok());
assert_eq!(count, 3);
}
#[test]
fn test_destructuring_object_multiple() {
let mut fields = HashMap::new();
fields.insert("x".to_string(), Value::Integer(10));
fields.insert("y".to_string(), Value::Integer(20));
let obj = Value::Object(Arc::new(fields));
let pattern = DestructuringPattern::Object(vec![
("x".to_string(), "a".to_string()),
("y".to_string(), "b".to_string()),
]);
let mut count = 0;
let result = eval_destructuring_assignment(&pattern, &obj, |_, _| {
count += 1;
Ok(())
});
assert!(result.is_ok());
assert_eq!(count, 2);
}
#[test]
fn test_eval_tuple_field_access_first() {
let elements = vec![Value::Integer(1), Value::Integer(2), Value::Integer(3)];
let result = eval_tuple_field_access(&elements, "0").unwrap();
assert_eq!(result, Value::Integer(1));
}
#[test]
fn test_eval_tuple_field_access_last() {
let elements = vec![Value::Integer(1), Value::Integer(2), Value::Integer(3)];
let result = eval_tuple_field_access(&elements, "2").unwrap();
assert_eq!(result, Value::Integer(3));
}
#[test]
fn test_eval_tuple_field_access_out_of_bounds() {
let elements = vec![Value::Integer(1)];
let result = eval_tuple_field_access(&elements, "5");
assert!(result.is_err());
}
#[test]
fn test_eval_tuple_field_access_middle_r160() {
let elements = vec![Value::Integer(10), Value::Integer(20), Value::Integer(30)];
let result = eval_tuple_field_access(&elements, "1").unwrap();
assert_eq!(result, Value::Integer(20));
}
#[test]
fn test_eval_tuple_field_access_invalid_index_r160() {
let elements = vec![Value::Integer(1)];
let result = eval_tuple_field_access(&elements, "abc");
assert!(result.is_err());
}
#[test]
fn test_eval_tuple_field_access_negative_r160() {
let elements = vec![Value::Integer(1)];
let result = eval_tuple_field_access(&elements, "-1");
assert!(result.is_err());
}
#[test]
fn test_eval_destructuring_array_three_elements_r160() {
let value = Value::Array(Arc::from(vec![
Value::Integer(1),
Value::Integer(2),
Value::Integer(3),
]));
let pattern =
DestructuringPattern::Array(vec!["a".to_string(), "b".to_string(), "c".to_string()]);
let mut bindings = vec![];
let result = eval_destructuring_assignment(&pattern, &value, |name, val| {
bindings.push((name.to_string(), val.clone()));
Ok(())
});
assert!(result.is_ok());
assert_eq!(bindings.len(), 3);
}
#[test]
fn test_eval_destructuring_array_r160() {
let value = Value::Array(Arc::from(vec![Value::Integer(10), Value::Integer(20)]));
let pattern = DestructuringPattern::Array(vec!["x".to_string(), "y".to_string()]);
let mut count = 0;
let result = eval_destructuring_assignment(&pattern, &value, |_, _| {
count += 1;
Ok(())
});
assert!(result.is_ok());
assert_eq!(count, 2);
}
#[test]
fn test_eval_destructuring_array_too_few_values_r160() {
let value = Value::Array(Arc::from(vec![Value::Integer(1)]));
let pattern = DestructuringPattern::Array(vec!["a".to_string(), "b".to_string()]);
let result = eval_destructuring_assignment(&pattern, &value, |_, _| Ok(()));
assert!(result.is_err());
}
#[test]
fn test_eval_destructuring_array_wrong_length_r160() {
let value = Value::Array(Arc::from(vec![Value::Integer(1)]));
let pattern = DestructuringPattern::Array(vec!["a".to_string(), "b".to_string()]);
let result = eval_destructuring_assignment(&pattern, &value, |_, _| Ok(()));
assert!(result.is_err());
}
#[test]
fn test_eval_destructuring_object_missing_field_r160() {
let mut fields = std::collections::HashMap::new();
fields.insert("x".to_string(), Value::Integer(1));
let obj = Value::Object(Arc::new(fields));
let pattern = DestructuringPattern::Object(vec![
("x".to_string(), "a".to_string()),
("y".to_string(), "b".to_string()), ]);
let result = eval_destructuring_assignment(&pattern, &obj, |_, _| Ok(()));
assert!(result.is_ok() || result.is_err());
}
#[test]
fn test_eval_destructuring_object_single_field_r160() {
let mut fields = std::collections::HashMap::new();
fields.insert("only".to_string(), Value::from_string("value".to_string()));
let obj = Value::Object(Arc::new(fields));
let pattern = DestructuringPattern::Object(vec![("only".to_string(), "x".to_string())]);
let mut bound_value = None;
let result = eval_destructuring_assignment(&pattern, &obj, |name, val| {
if name == "x" {
bound_value = Some(val.clone());
}
Ok(())
});
assert!(result.is_ok());
assert!(bound_value.is_some());
}
#[test]
fn test_eval_tuple_field_access_empty_r160() {
let elements: Vec<Value> = vec![];
let result = eval_tuple_field_access(&elements, "0");
assert!(result.is_err());
}
#[test]
fn test_eval_destructuring_type_mismatch_integer_r160() {
let value = Value::Integer(42); let pattern = DestructuringPattern::Array(vec!["a".to_string()]);
let result = eval_destructuring_assignment(&pattern, &value, |_, _| Ok(()));
assert!(result.is_err());
}
#[test]
fn test_eval_destructuring_type_mismatch_array_r160() {
let value = Value::Bool(true); let pattern = DestructuringPattern::Array(vec!["a".to_string()]);
let result = eval_destructuring_assignment(&pattern, &value, |_, _| Ok(()));
assert!(result.is_err());
}
#[test]
fn test_eval_destructuring_type_mismatch_object_r160() {
let value = Value::from_string("not an object".to_string());
let pattern = DestructuringPattern::Object(vec![("field".to_string(), "x".to_string())]);
let result = eval_destructuring_assignment(&pattern, &value, |_, _| Ok(()));
assert!(result.is_err());
}
#[test]
fn test_eval_tuple_field_access_first_r160() {
let elements = vec![Value::Integer(1), Value::Integer(2), Value::Integer(3)];
let result = eval_tuple_field_access(&elements, "0").unwrap();
assert_eq!(result, Value::Integer(1));
}
#[test]
fn test_eval_destructuring_empty_array_r160() {
let value = Value::Array(Arc::from(vec![] as Vec<Value>));
let pattern = DestructuringPattern::Array(vec![]);
let mut count = 0;
let result = eval_destructuring_assignment(&pattern, &value, |_, _| {
count += 1;
Ok(())
});
assert!(result.is_ok());
assert_eq!(count, 0);
}
#[test]
fn test_eval_destructuring_empty_object_r160() {
let obj = Value::Object(Arc::new(std::collections::HashMap::new()));
let pattern = DestructuringPattern::Object(vec![]);
let mut count = 0;
let result = eval_destructuring_assignment(&pattern, &obj, |_, _| {
count += 1;
Ok(())
});
assert!(result.is_ok());
assert_eq!(count, 0);
}
}