use crate::eval::evaluator::eval;
use crate::value::Value;
use hamelin_lib::tree::ast::identifier::SimpleIdentifier;
use hamelin_lib::tree::builder::{add, array, field_ref, tuple, ExpressionBuilder};
use hamelin_lib::tree::options::ExpressionTypeCheckOptions;
use hamelin_lib::type_check_expression;
use hamelin_lib::types::{INT, STRING};
use std::sync::Arc;
use super::test_helpers::TestContext;
#[test]
fn test_eval_with_variables() {
let mut ctx = TestContext::default();
ctx.set("x", Value::Int(10), INT);
ctx.set("y", Value::Int(20), INT);
let expr = type_check_expression(
add(field_ref("x"), field_ref("y")).build(),
ExpressionTypeCheckOptions::builder()
.bindings(Arc::new(ctx.translation_env.clone()))
.build(),
)
.output;
let result = eval(&expr, &ctx.env).unwrap();
assert_eq!(result, Value::Int(30));
}
#[test]
fn test_eval_field_reference() {
let mut ctx = TestContext::default();
ctx.set("x", Value::Int(100), INT);
let expr = type_check_expression(
field_ref("x").build(),
ExpressionTypeCheckOptions::builder()
.bindings(Arc::new(ctx.translation_env.clone()))
.build(),
)
.output;
let result = eval(&expr, &ctx.env).unwrap();
assert_eq!(result, Value::Int(100));
}
#[test]
fn test_eval_unbound_variable() {
let ctx = TestContext::default();
let expr = type_check_expression(
field_ref("undefined").build(),
ExpressionTypeCheckOptions::builder()
.bindings(Arc::new(ctx.translation_env.clone()))
.build(),
)
.output;
assert!(matches!(
expr.kind,
hamelin_lib::tree::typed_ast::expression::TypedExpressionKind::Error(_)
));
}
#[test]
fn test_eval_array_with_variables() {
let mut ctx = TestContext::default();
ctx.set("x", Value::Int(10), INT);
ctx.set("y", Value::Int(20), INT);
let expr = type_check_expression(
array()
.element(field_ref("x"))
.element(5)
.element(field_ref("y"))
.element(42)
.build(),
ExpressionTypeCheckOptions::builder()
.bindings(Arc::new(ctx.translation_env.clone()))
.build(),
)
.output;
let result = eval(&expr, &ctx.env).unwrap();
assert_eq!(
result,
Value::Array(vec![
Value::Int(10),
Value::Int(5),
Value::Int(20),
Value::Int(42)
])
);
}
#[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 = type_check_expression(
tuple()
.element(field_ref("name"))
.element(field_ref("age"))
.element("static")
.build(),
ExpressionTypeCheckOptions::builder()
.bindings(Arc::new(ctx.translation_env.clone()))
.build(),
)
.output;
let result = eval(&expr, &ctx.env).unwrap();
assert_eq!(
result,
Value::Tuple(vec![
Value::String("Alice".to_string()),
Value::Int(30),
Value::String("static".to_string())
])
);
}
#[test]
fn test_eval_struct_with_variables() {
use hamelin_lib::tree::builder::struct_literal;
let mut ctx = TestContext::default();
ctx.set("x", Value::Int(42), INT);
ctx.set("y", Value::String("test".to_string()), STRING);
let expr = struct_literal()
.field("field_x", field_ref("x"))
.field("field_y", field_ref("y"))
.field("constant", 100);
let typed = type_check_expression(
expr.build(),
ExpressionTypeCheckOptions::builder()
.bindings(Arc::new(ctx.translation_env.clone()))
.build(),
)
.output;
let result = eval(&typed, &ctx.env).unwrap();
let mut expected_fields = ordermap::OrderMap::new();
expected_fields.insert(
"field_x".parse::<SimpleIdentifier>().unwrap(),
Value::Int(42),
);
expected_fields.insert(
"field_y".parse::<SimpleIdentifier>().unwrap(),
Value::String("test".to_string()),
);
expected_fields.insert(
"constant".parse::<SimpleIdentifier>().unwrap(),
Value::Int(100),
);
assert_eq!(result, Value::Struct(expected_fields));
}
#[test]
fn test_eval_variable_with_different_types() {
let mut ctx = TestContext::default();
ctx.set("int_var", Value::Int(123), INT);
ctx.set(
"string_var",
Value::String("hello world".to_string()),
STRING,
);
let expr = type_check_expression(
field_ref("int_var").build(),
ExpressionTypeCheckOptions::builder()
.bindings(Arc::new(ctx.translation_env.clone()))
.build(),
)
.output;
let result = eval(&expr, &ctx.env).unwrap();
assert_eq!(result, Value::Int(123));
let expr = type_check_expression(
field_ref("string_var").build(),
ExpressionTypeCheckOptions::builder()
.bindings(Arc::new(ctx.translation_env.clone()))
.build(),
)
.output;
let result = eval(&expr, &ctx.env).unwrap();
assert_eq!(result, Value::String("hello world".to_string()));
}
#[test]
fn test_eval_null_variable() {
let mut ctx = TestContext::default();
ctx.set("nullable_var", Value::Null, INT);
let expr = type_check_expression(
field_ref("nullable_var").build(),
ExpressionTypeCheckOptions::builder()
.bindings(Arc::new(ctx.translation_env.clone()))
.build(),
)
.output;
let result = eval(&expr, &ctx.env).unwrap();
assert_eq!(result, Value::Null);
assert!(result.is_null());
}