use std::sync::Arc;
use crate::eval::evaluator::eval;
use crate::value::Value;
use hamelin_lib::tree::builder::{field, field_ref, pair, tuple, ExpressionBuilder};
use hamelin_lib::tree::options::ExpressionTypeCheckOptions;
use hamelin_lib::tree::typed_ast::expression::TypedExpressionKind;
use hamelin_lib::type_check_expression;
use hamelin_lib::types::{INT, STRING};
use super::test_helpers::TestContext;
#[test]
fn test_eval_tuple_literal() {
let ctx = TestContext::default();
let expr = tuple().element(1).element("hello").element(true);
let result = ctx.eval_expr(&expr);
match result {
Value::Tuple(tup) => {
assert_eq!(tup.len(), 3);
assert_eq!(tup[0], Value::Int(1));
assert_eq!(tup[1], Value::String("hello".to_string()));
assert_eq!(tup[2], Value::Boolean(true));
}
_ => panic!("Expected Tuple value"),
}
}
#[test]
fn test_eval_tuple_with_variables() {
let mut ctx = TestContext::default();
ctx.set("name", Value::String("Alice".to_string()), STRING);
ctx.set("age", Value::Int(30), INT);
let expr = tuple()
.element(field_ref("name"))
.element(field_ref("age"))
.element("static");
let result = ctx.eval_expr(&expr);
assert_eq!(
result,
Value::Tuple(vec![
Value::String("Alice".to_string()),
Value::Int(30),
Value::String("static".to_string())
])
);
}
#[test]
fn test_eval_pair_literal() {
let ctx = TestContext::default();
let expr = pair(1, 2);
let result = ctx.eval_expr(&expr);
assert_eq!(result, Value::Tuple(vec![Value::Int(1), Value::Int(2)]));
let expr = pair(42, "hello");
let result = ctx.eval_expr(&expr);
assert_eq!(
result,
Value::Tuple(vec![Value::Int(42), Value::String("hello".to_string())])
);
let expr = pair(pair(1, 2), 3);
let result = ctx.eval_expr(&expr);
assert_eq!(
result,
Value::Tuple(vec![
Value::Tuple(vec![Value::Int(1), Value::Int(2)]),
Value::Int(3)
])
);
}
#[test]
fn test_eval_empty_tuple() {
let ctx = TestContext::default();
let expr_builder = tuple();
let expr = type_check_expression(
expr_builder.build(),
ExpressionTypeCheckOptions::builder()
.bindings(Arc::new(ctx.translation_env.clone()))
.build(),
)
.output;
if matches!(
expr.kind,
hamelin_lib::tree::typed_ast::expression::TypedExpressionKind::Error(_)
) {
return;
}
let result = eval(&expr, &ctx.env).unwrap();
match result {
Value::Tuple(tup) => {
assert_eq!(tup.len(), 0);
}
_ => panic!("Expected empty Tuple value"),
}
}
#[test]
fn test_eval_single_element_tuple() {
let ctx = TestContext::default();
let expr = tuple().element("solo");
let result = ctx.eval_expr(&expr);
assert_eq!(
result,
Value::Tuple(vec![Value::String("solo".to_string())])
);
}
#[test]
fn test_eval_nested_tuples() {
let ctx = TestContext::default();
let inner_tuple1 = tuple().element(1).element(2);
let inner_tuple2 = tuple().element(3).element(4);
let expr = tuple().element(inner_tuple1).element(inner_tuple2);
let result = ctx.eval_expr(&expr);
let expected = Value::Tuple(vec![
Value::Tuple(vec![Value::Int(1), Value::Int(2)]),
Value::Tuple(vec![Value::Int(3), Value::Int(4)]),
]);
assert_eq!(result, expected);
}
#[test]
fn test_eval_tuple_with_mixed_types() {
let ctx = TestContext::default();
let expr = tuple()
.element(42) .element("text") .element(true) .element(3.14);
let result = ctx.eval_expr(&expr);
let expected = Value::Tuple(vec![
Value::Int(42),
Value::String("text".to_string()),
Value::Boolean(true),
Value::Double(3.14),
]);
assert_eq!(result, expected);
}
#[test]
fn test_eval_pair_with_nulls() {
use hamelin_lib::tree::builder::null;
let ctx = TestContext::default();
let expr = pair(null(), 42);
let result = ctx.eval_expr(&expr);
assert_eq!(result, Value::Tuple(vec![Value::Null, Value::Int(42)]));
let expr = pair(null(), null());
let result = ctx.eval_expr(&expr);
assert_eq!(result, Value::Tuple(vec![Value::Null, Value::Null]));
}
#[test]
fn test_eval_tuple_access_length() {
let ctx = TestContext::default();
let expr = tuple().element("a").element("b").element("c");
let result = ctx.eval_expr(&expr);
match result {
Value::Tuple(tup) => {
assert_eq!(tup.len(), 3);
assert_eq!(tup[0], Value::String("a".to_string()));
assert_eq!(tup[1], Value::String("b".to_string()));
assert_eq!(tup[2], Value::String("c".to_string()));
}
_ => panic!("Expected Tuple value"),
}
}
#[test]
fn test_eval_tuple_field_access() {
use hamelin_lib::tree::builder::field;
let ctx = TestContext::default();
let f0_expr = field(tuple().element("first").element(42).element(true), "f0");
let result = ctx.eval_expr(&f0_expr);
assert_eq!(result, Value::String("first".to_string()));
let f1_expr = field(tuple().element("first").element(42).element(true), "f1");
let result = ctx.eval_expr(&f1_expr);
assert_eq!(result, Value::Int(42));
let f2_expr = field(tuple().element("first").element(42).element(true), "f2");
let result = ctx.eval_expr(&f2_expr);
assert_eq!(result, Value::Boolean(true));
}
#[test]
fn test_eval_pair_field_access() {
use hamelin_lib::tree::builder::field;
let ctx = TestContext::default();
let f0_expr = field(pair("hello", 123), "f0");
let result = ctx.eval_expr(&f0_expr);
assert_eq!(result, Value::String("hello".to_string()));
let f1_expr = field(pair("hello", 123), "f1");
let result = ctx.eval_expr(&f1_expr);
assert_eq!(result, Value::Int(123));
}
#[test]
fn test_eval_nested_tuple_field_access() {
use hamelin_lib::tree::builder::field;
let ctx = TestContext::default();
let nested_expr = field(
field(
tuple()
.element(tuple().element(1).element(2))
.element(tuple().element(3).element(4)),
"f0",
),
"f0",
);
let result = ctx.eval_expr(&nested_expr);
assert_eq!(result, Value::Int(1));
let nested_expr = field(
field(
tuple()
.element(tuple().element(1).element(2))
.element(tuple().element(3).element(4)),
"f1",
),
"f1",
);
let result = ctx.eval_expr(&nested_expr);
assert_eq!(result, Value::Int(4));
}
#[test]
fn test_eval_tuple_field_access_out_of_bounds() {
use hamelin_lib::tree::builder::field;
let ctx = TestContext::default();
let f2_expr = field(tuple().element("first").element("second"), "f2");
let typed_expr = type_check_expression(
f2_expr.build(),
ExpressionTypeCheckOptions::builder()
.bindings(Arc::new(ctx.translation_env.clone()))
.build(),
)
.output;
assert!(matches!(typed_expr.kind, TypedExpressionKind::Error(_)));
}
#[test]
fn test_eval_tuple_field_access_invalid_field_name() {
let ctx = TestContext::default();
let invalid_names = vec!["0", "1", "x", "field", "f", "f-1"];
for invalid_name in invalid_names {
let invalid_expr = field(tuple().element("first").element("second"), invalid_name);
let typed_expr = type_check_expression(
invalid_expr.build(),
ExpressionTypeCheckOptions::builder()
.bindings(Arc::new(ctx.translation_env.clone()))
.build(),
)
.output;
assert!(
matches!(
typed_expr.kind,
hamelin_lib::tree::typed_ast::expression::TypedExpressionKind::Error(_)
),
"Field name '{}' should have caused an error",
invalid_name
);
}
}