hamelin_eval 0.7.11

Expression evaluation for Hamelin query language
Documentation
use crate::eval::environment::Environment;
use crate::reverse_eval::domain::Constraint;
use crate::reverse_eval::reverse::reverse_eval;
use crate::value::Value;
use hamelin_lib::tree::builder::*;
use hamelin_lib::tree::options::ExpressionTypeCheckOptions;
use hamelin_lib::tree::typed_ast::environment::TypeEnvironment;
use hamelin_lib::type_check_expression;
use hamelin_lib::types::INT;
use pretty_assertions::assert_eq;
use std::sync::Arc;

#[test]
fn test_reverse_eval_field_ref() {
    let env = Environment::new();

    // Setup translation environment with bindings
    let mut trans_env = TypeEnvironment::default();
    trans_env.bind_str("x", INT);
    let bindings = Arc::new(trans_env);

    // Build: x
    let expr = type_check_expression(
        field_ref("x").build(),
        ExpressionTypeCheckOptions::builder()
            .bindings(bindings)
            .build(),
    )
    .output;

    // If x = 10, then x should be constrained to 10
    let constraint = Constraint::Equals(Value::Int(10));
    let result = reverse_eval(&expr, constraint, &env).expect("Reverse eval failed");

    assert_eq!(result, Some(Constraint::Equals(Value::Int(10))));
}

#[test]
fn test_reverse_eval_int_literal_satisfied() {
    let env = Environment::new();

    // Build: 42
    let expr = type_check_expression(int(42).build(), ExpressionTypeCheckOptions::default()).output;

    // Constraint satisfied
    let constraint = Constraint::Equals(Value::Int(42));
    let result = reverse_eval(&expr, constraint, &env).expect("Reverse eval failed");

    // Literals don't constrain any variables
    assert_eq!(result, None);
}

#[test]
fn test_reverse_eval_int_literal_unsatisfied() {
    let env = Environment::new();

    // Build: 42
    let expr = type_check_expression(int(42).build(), ExpressionTypeCheckOptions::default()).output;

    // Constraint NOT satisfied
    let constraint = Constraint::Equals(Value::Int(100));
    let result = reverse_eval(&expr, constraint, &env);

    assert!(result.is_err());
    assert!(result.unwrap_err().to_string().contains("unsatisfiable"));
}

#[test]
fn test_reverse_eval_empty_constraint() {
    let env = Environment::new();

    // Setup translation environment with bindings
    let mut trans_env = TypeEnvironment::default();
    trans_env.bind_str("x", INT);
    let bindings = Arc::new(trans_env);

    // Build: x + 5
    let expr = type_check_expression(
        add(field_ref("x"), 5).build(),
        ExpressionTypeCheckOptions::builder()
            .bindings(bindings)
            .build(),
    )
    .output;

    // Empty constraint means no solutions
    let constraint = Constraint::Empty;
    let result =
        reverse_eval(&expr, constraint, &env).expect("Reverse eval should handle empty constraint");

    // Empty constraint produces no variable constraints
    assert_eq!(result, None);
}

#[test]
fn test_reverse_eval_universal_constraint() {
    let env = Environment::new();

    // Setup translation environment with bindings
    let mut trans_env = TypeEnvironment::default();
    trans_env.bind_str("x", INT);
    let bindings = Arc::new(trans_env);

    // Build: x
    let expr = type_check_expression(
        field_ref("x").build(),
        ExpressionTypeCheckOptions::builder()
            .bindings(bindings)
            .build(),
    )
    .output;

    // Universal constraint means any value is acceptable
    let constraint = Constraint::Universal;
    let result = reverse_eval(&expr, constraint, &env).expect("Reverse eval failed");

    assert_eq!(result, Some(Constraint::Universal));
}