use crate::eval::environment::Environment;
use crate::eval::error::EvalError;
use crate::eval::evaluator::eval;
use crate::eval::tests::test_helpers::TestContext;
use crate::value::{DecimalValue, Value};
use hamelin_lib::tree::builder::{
add, divide, field_ref, modulo, multiply, subtract, ExpressionBuilder,
};
use hamelin_lib::tree::options::ExpressionTypeCheckOptions;
use hamelin_lib::type_check_expression;
use hamelin_lib::types::INT;
use std::sync::Arc;
use super::test_helpers::setup_environment;
#[test]
fn test_eval_binary_addition_int() {
let env = setup_environment();
let expr =
type_check_expression(add(5, 3).build(), ExpressionTypeCheckOptions::default()).output;
let result = eval(&expr, &env).unwrap();
assert_eq!(result, Value::Int(8));
}
#[test]
fn test_eval_binary_addition_double() {
let env = setup_environment();
let expr =
type_check_expression(add(2.5, 3.7).build(), ExpressionTypeCheckOptions::default()).output;
let result = eval(&expr, &env).unwrap();
assert_eq!(result, Value::Double(6.2));
}
#[test]
fn test_eval_binary_addition_mixed_types() {
let env = setup_environment();
let expr =
type_check_expression(add(5, 3.2).build(), ExpressionTypeCheckOptions::default()).output;
let result = eval(&expr, &env).unwrap();
assert_eq!(result, Value::Double(8.2));
}
#[test]
fn test_eval_binary_subtraction() {
let env = setup_environment();
let expr = type_check_expression(
subtract(10, 4).build(),
ExpressionTypeCheckOptions::default(),
)
.output;
let result = eval(&expr, &env).unwrap();
assert_eq!(result, Value::Int(6));
}
#[test]
fn test_eval_binary_multiplication() {
let env = setup_environment();
let expr = type_check_expression(
multiply(6, 7).build(),
ExpressionTypeCheckOptions::default(),
)
.output;
let result = eval(&expr, &env).unwrap();
assert_eq!(result, Value::Int(42));
}
#[test]
fn test_eval_binary_division() {
let env = setup_environment();
let expr =
type_check_expression(divide(15, 3).build(), ExpressionTypeCheckOptions::default()).output;
let result = eval(&expr, &env).unwrap();
assert_eq!(result, Value::Int(5));
}
#[test]
fn test_eval_binary_division_by_zero() {
let env = setup_environment();
let expr =
type_check_expression(divide(10, 0).build(), ExpressionTypeCheckOptions::default()).output;
let result = eval(&expr, &env);
assert!(result.is_err());
assert!(matches!(
result.unwrap_err(),
EvalError::ExecutionError { .. }
));
}
#[test]
fn test_eval_modulo_operator() {
let mut ctx = TestContext::default();
ctx.set("x", Value::Int(10), INT);
ctx.set("y", Value::Int(3), INT);
let expr = type_check_expression(
modulo(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(1));
}
#[test]
fn test_eval_modulo_division_by_zero() {
let env = setup_environment();
let expr =
type_check_expression(modulo(10, 0).build(), ExpressionTypeCheckOptions::default()).output;
let result = eval(&expr, &env);
assert!(result.is_err());
assert!(matches!(
result.unwrap_err(),
EvalError::ExecutionError { .. }
));
}
#[test]
fn test_eval_nested_expressions() {
let env = setup_environment();
let expr = type_check_expression(
multiply(add(5, 3), subtract(10, 2)).build(),
ExpressionTypeCheckOptions::default(),
)
.output;
let result = eval(&expr, &env).unwrap();
assert_eq!(result, Value::Int(64));
}
#[test]
fn test_eval_string_concatenation() {
let env = Environment::new();
let expr = type_check_expression(
add("hello", " world").build(),
ExpressionTypeCheckOptions::default(),
)
.output;
let result = eval(&expr, &env).unwrap();
assert_eq!(result, Value::String("hello world".to_string()));
}
#[test]
fn test_eval_decimal_addition() {
use hamelin_lib::tree::builder::decimal_from_parts;
let env = setup_environment();
let expr = type_check_expression(
add(decimal_from_parts(314, 3, 2), decimal_from_parts(286, 3, 2)).build(),
ExpressionTypeCheckOptions::default(),
)
.output;
let result = eval(&expr, &env).unwrap();
assert_eq!(
result,
Value::Decimal(DecimalValue {
unscaled: 600,
scale: 2
})
);
}
#[test]
fn test_eval_decimal_subtraction() {
use hamelin_lib::tree::builder::decimal_from_parts;
let env = setup_environment();
let expr = type_check_expression(
subtract(decimal_from_parts(575, 3, 2), decimal_from_parts(225, 3, 2)).build(),
ExpressionTypeCheckOptions::default(),
)
.output;
let result = eval(&expr, &env).unwrap();
assert_eq!(
result,
Value::Decimal(DecimalValue {
unscaled: 350,
scale: 2
})
);
}
#[test]
fn test_eval_decimal_multiplication() {
use hamelin_lib::tree::builder::decimal_from_parts;
let env = setup_environment();
let expr = type_check_expression(
multiply(decimal_from_parts(25, 2, 1), decimal_from_parts(30, 2, 1)).build(),
ExpressionTypeCheckOptions::default(),
)
.output;
let result = eval(&expr, &env).unwrap();
assert_eq!(
result,
Value::Decimal(DecimalValue {
unscaled: 750,
scale: 2
})
);
}
#[test]
fn test_eval_decimal_division() {
use hamelin_lib::tree::builder::decimal_from_parts;
let env = setup_environment();
let expr = type_check_expression(
divide(decimal_from_parts(100, 2, 1), decimal_from_parts(20, 2, 1)).build(),
ExpressionTypeCheckOptions::default(),
)
.output;
let result = eval(&expr, &env).unwrap();
assert_eq!(
result,
Value::Decimal(DecimalValue {
unscaled: 5000000,
scale: 6
})
);
}
#[test]
fn test_eval_decimal_mixed_with_int() {
use hamelin_lib::tree::builder::decimal_from_parts;
let env = setup_environment();
let expr = type_check_expression(
add(decimal_from_parts(314, 3, 2), 5).build(),
ExpressionTypeCheckOptions::default(),
)
.output;
let result = eval(&expr, &env).unwrap();
assert_eq!(
result,
Value::Decimal(DecimalValue {
unscaled: 814,
scale: 2
})
);
}
#[test]
fn test_eval_decimal_mixed_with_double() {
use hamelin_lib::tree::builder::decimal_from_parts;
let env = setup_environment();
let expr = type_check_expression(
add(decimal_from_parts(314, 3, 2), 2.5).build(),
ExpressionTypeCheckOptions::default(),
)
.output;
let result = eval(&expr, &env).unwrap();
if let Value::Double(d) = result {
assert!((d - 5.64).abs() < 1e-10, "Expected ~5.64, got {}", d);
} else {
panic!("Expected Double, got {:?}", result);
}
}