use crate::eval::environment::Environment;
use crate::eval::evaluator::eval;
use crate::value::Value;
use hamelin_lib::tree::ast::identifier::SimpleIdentifier;
use hamelin_lib::tree::builder::{
array, field, field_ref, struct_literal, tuple, ExpressionBuilder,
};
use hamelin_lib::tree::options::ExpressionTypeCheckOptions;
use hamelin_lib::tree::typed_ast::environment::TypeEnvironment;
use hamelin_lib::type_check_expression;
use hamelin_lib::types::{
array::Array, struct_type::Struct, tuple::Tuple, Type, INT, STRING, VARIANT,
};
use std::sync::Arc;
use super::test_helpers::{test_context, TestContext};
#[test]
fn broadcast_struct_field_simple() {
let ctx = test_context();
let expr = field(
array()
.element(struct_literal().field("name", "alice").field("age", 30))
.element(struct_literal().field("name", "bob").field("age", 25)),
"name",
);
let result = ctx.eval_expr(&expr);
assert_eq!(
result,
Value::Array(vec![
Value::String("alice".to_string()),
Value::String("bob".to_string()),
])
);
}
#[test]
fn broadcast_struct_field_nested_struct_in_struct() {
let ctx = test_context();
let expr = field(
field(
array()
.element(struct_literal().field("outer", struct_literal().field("inner", 1)))
.element(struct_literal().field("outer", struct_literal().field("inner", 2))),
"outer",
),
"inner",
);
let result = ctx.eval_expr(&expr);
assert_eq!(result, Value::Array(vec![Value::Int(1), Value::Int(2)]));
}
#[test]
fn broadcast_struct_field_array_valued_field_is_not_flattened() {
let ctx = test_context();
let expr = field(
array()
.element(struct_literal().field("nums", array().element(1).element(2)))
.element(struct_literal().field("nums", array().element(3).element(4)))
.element(struct_literal().field("nums", array().element(5))),
"nums",
);
let result = ctx.eval_expr(&expr);
assert_eq!(
result,
Value::Array(vec![
Value::Array(vec![Value::Int(1), Value::Int(2)]),
Value::Array(vec![Value::Int(3), Value::Int(4)]),
Value::Array(vec![Value::Int(5)]),
])
);
}
#[test]
fn broadcast_struct_field_null_outer_array() {
let mut ctx = TestContext::default();
ctx.set(
"arr",
Value::Null,
Array::new(Struct::default().with_str("name", STRING).into()).into(),
);
let expr = field(field_ref("arr"), "name");
let result = ctx.eval_expr(&expr);
assert_eq!(result, Value::Null);
}
#[test]
fn broadcast_struct_field_null_element_preserved() {
let elem_type: Type = Struct::default().with_str("name", STRING).into();
let mut ctx = TestContext::default();
ctx.set(
"arr",
Value::Array(vec![
Value::Struct({
let mut m = ordermap::OrderMap::new();
m.insert(
SimpleIdentifier::new("name"),
Value::String("alice".to_string()),
);
m
}),
Value::Null,
Value::Struct({
let mut m = ordermap::OrderMap::new();
m.insert(
SimpleIdentifier::new("name"),
Value::String("charlie".to_string()),
);
m
}),
]),
Array::new(elem_type).into(),
);
let expr = field(field_ref("arr"), "name");
let result = ctx.eval_expr(&expr);
assert_eq!(
result,
Value::Array(vec![
Value::String("alice".to_string()),
Value::Null,
Value::String("charlie".to_string()),
])
);
}
#[test]
fn broadcast_tuple_element_first() {
let ctx = test_context();
let expr = field(
array()
.element(tuple().element(1).element("a"))
.element(tuple().element(2).element("b"))
.element(tuple().element(3).element("c")),
"f0",
);
let result = ctx.eval_expr(&expr);
assert_eq!(
result,
Value::Array(vec![Value::Int(1), Value::Int(2), Value::Int(3)])
);
}
#[test]
fn broadcast_tuple_element_second() {
let ctx = test_context();
let expr = field(
array()
.element(tuple().element(1).element("a"))
.element(tuple().element(2).element("b")),
"f1",
);
let result = ctx.eval_expr(&expr);
assert_eq!(
result,
Value::Array(vec![
Value::String("a".to_string()),
Value::String("b".to_string()),
])
);
}
#[test]
fn broadcast_tuple_element_array_valued_is_not_flattened() {
let ctx = test_context();
let expr = field(
array()
.element(tuple().element(array().element(1).element(2)).element("a"))
.element(tuple().element(array().element(3)).element("b")),
"f0",
);
let result = ctx.eval_expr(&expr);
assert_eq!(
result,
Value::Array(vec![
Value::Array(vec![Value::Int(1), Value::Int(2)]),
Value::Array(vec![Value::Int(3)]),
])
);
}
#[test]
fn broadcast_tuple_element_null_outer_array() {
let mut ctx = TestContext::default();
ctx.set(
"arr",
Value::Null,
Array::new(Tuple::new(vec![INT, STRING]).into()).into(),
);
let expr = field(field_ref("arr"), "f0");
let result = ctx.eval_expr(&expr);
assert_eq!(result, Value::Null);
}
#[test]
fn broadcast_variant_field_simple() {
let mut env = Environment::new();
env.bind(
SimpleIdentifier::new("arr"),
Value::Array(vec![
Value::Variant(serde_json::json!({"foo": 1})),
Value::Variant(serde_json::json!({"foo": 2})),
]),
);
let mut trans_env = TypeEnvironment::default();
trans_env.bind_str("arr", Array::new(VARIANT).into());
let expr = type_check_expression(
field(field_ref("arr"), "foo").build(),
ExpressionTypeCheckOptions::builder()
.bindings(Arc::new(trans_env))
.build(),
)
.output;
let result = eval(&expr, &env).unwrap();
assert_eq!(
result,
Value::Array(vec![
Value::Variant(serde_json::json!(1)),
Value::Variant(serde_json::json!(2)),
])
);
}
#[test]
fn broadcast_variant_field_missing_field_is_null_variant() {
let mut env = Environment::new();
env.bind(
SimpleIdentifier::new("arr"),
Value::Array(vec![
Value::Variant(serde_json::json!({"foo": 1})),
Value::Variant(serde_json::json!({"bar": 2})),
]),
);
let mut trans_env = TypeEnvironment::default();
trans_env.bind_str("arr", Array::new(VARIANT).into());
let expr = type_check_expression(
field(field_ref("arr"), "foo").build(),
ExpressionTypeCheckOptions::builder()
.bindings(Arc::new(trans_env))
.build(),
)
.output;
let result = eval(&expr, &env).unwrap();
assert_eq!(
result,
Value::Array(vec![
Value::Variant(serde_json::json!(1)),
Value::Variant(serde_json::Value::Null),
])
);
}
#[test]
fn broadcast_variant_field_null_outer_array() {
let mut ctx = TestContext::default();
ctx.set("arr", Value::Null, Array::new(VARIANT).into());
let expr = field(field_ref("arr"), "foo");
let result = ctx.eval_expr(&expr);
assert_eq!(result, Value::Null);
}
#[test]
fn transform_lambda_with_broadcast_struct_field_in_body() {
use hamelin_lib::tree::builder::{call, lambda1};
let ctx = test_context();
let expr = call("transform")
.arg(
array()
.element(
array()
.element(struct_literal().field("name", "a"))
.element(struct_literal().field("name", "b")),
)
.element(array().element(struct_literal().field("name", "c"))),
)
.arg(lambda1("x").body(field(field_ref("x"), "name")));
let result = ctx.eval_expr(&expr);
assert_eq!(
result,
Value::Array(vec![
Value::Array(vec![
Value::String("a".to_string()),
Value::String("b".to_string()),
]),
Value::Array(vec![Value::String("c".to_string())]),
])
);
}
#[test]
fn transform_lambda_with_scalar_struct_field_in_body_still_works() {
use hamelin_lib::tree::builder::{call, lambda1};
let ctx = test_context();
let expr = call("transform")
.arg(
array()
.element(struct_literal().field("name", "a"))
.element(struct_literal().field("name", "b")),
)
.arg(lambda1("x").body(field(field_ref("x"), "name")));
let result = ctx.eval_expr(&expr);
assert_eq!(
result,
Value::Array(vec![
Value::String("a".to_string()),
Value::String("b".to_string()),
])
);
}