use std::collections::HashMap;
use relon_codegen_cranelift::AotEvaluator;
use relon_codegen_llvm::LlvmAotEvaluator;
use relon_eval_api::{Evaluator, Value};
fn run_tree_walk(src: &str, args: HashMap<String, Value>) -> Value {
use relon_evaluator::{Context, TreeWalkEvaluator};
use relon_parser::parse_document;
let node = parse_document(src).expect("parse");
let analyzed = std::sync::Arc::new(relon_analyzer::analyze(&node));
let ctx = Context::new()
.with_root(node)
.with_analyzed(std::sync::Arc::clone(&analyzed));
let ctx = std::sync::Arc::new({
let mut ctx = ctx;
TreeWalkEvaluator::prepare_in_place(&mut ctx);
ctx
});
TreeWalkEvaluator::new(std::sync::Arc::clone(&ctx))
.run_main(
&std::sync::Arc::new(relon_eval_api::scope::Scope::default()),
args,
)
.expect("tree-walk run_main")
}
fn assert_three_way(src: &str, args: HashMap<String, Value>) -> Value {
let tw = run_tree_walk(src, args.clone());
let cl = AotEvaluator::from_source(src).expect("cranelift from_source");
let cl_v = cl.run_main(args.clone()).expect("cranelift run_main");
assert_eq!(tw, cl_v, "tree-walk vs cranelift divergence");
let llvm = LlvmAotEvaluator::from_source(src).expect("llvm from_source");
let llvm_v = llvm.run_main(args.clone()).expect("llvm run_main");
assert_eq!(tw, llvm_v, "tree-walk vs llvm divergence");
assert_eq!(cl_v, llvm_v, "cranelift vs llvm divergence");
tw
}
fn s(v: &str) -> Value {
Value::String(v.into())
}
fn schema_val(brand: &str, fields: Vec<(&str, Value)>) -> Value {
Value::branded_dict(fields, Some(brand.to_string()))
}
#[test]
fn string_param_identity_three_way() {
const SRC: &str = "#main(String s) -> String\ns";
let args = HashMap::from([("s".to_string(), s("hello world"))]);
assert_eq!(assert_three_way(SRC, args), s("hello world"));
}
#[test]
fn string_param_length_three_way() {
const SRC: &str = "#main(String s) -> Int\ns.length()";
let args = HashMap::from([("s".to_string(), s("héllo"))]);
assert_eq!(assert_three_way(SRC, args), Value::Int(6));
}
#[test]
fn list_int_param_length_three_way() {
const SRC: &str = "#main(List<Int> xs) -> Int\nxs.length()";
let args = HashMap::from([(
"xs".to_string(),
Value::list(vec![Value::Int(10), Value::Int(20), Value::Int(30)]),
)]);
assert_eq!(assert_three_way(SRC, args), Value::Int(3));
}
#[test]
fn list_int_param_identity_three_way() {
const SRC: &str = "#main(List<Int> xs) -> List<Int>\nxs";
let args = HashMap::from([(
"xs".to_string(),
Value::list(vec![
Value::Int(1),
Value::Int(2),
Value::Int(3),
Value::Int(4),
]),
)]);
assert_eq!(
assert_three_way(SRC, args),
Value::list(vec![
Value::Int(1),
Value::Int(2),
Value::Int(3),
Value::Int(4)
])
);
}
#[test]
fn list_string_param_length_three_way() {
const SRC: &str = "#main(List<String> xs) -> Int\nxs.length()";
let args = HashMap::from([(
"xs".to_string(),
Value::list(vec![s("a"), s("bb"), s("ccc")]),
)]);
assert_eq!(assert_three_way(SRC, args), Value::Int(3));
}
#[test]
fn schema_string_field_three_way() {
const SRC: &str =
"#schema Cfg { active: Bool, name: String, port: Int }\n#main(Cfg cfg) -> String\ncfg.name";
let args = HashMap::from([(
"cfg".to_string(),
schema_val(
"Cfg",
vec![
("active", Value::Bool(true)),
("name", s("web-frontend")),
("port", Value::Int(8080)),
],
),
)]);
assert_eq!(assert_three_way(SRC, args), s("web-frontend"));
}
#[test]
fn schema_list_string_field_three_way() {
const SRC: &str = "#schema Cfg { tags: List<String>, port: Int }\n\
#main(Cfg cfg) -> Int\ncfg.tags.length()";
let args = HashMap::from([(
"cfg".to_string(),
schema_val(
"Cfg",
vec![
("tags", Value::list(vec![s("x"), s("y"), s("z"), s("w")])),
("port", Value::Int(1)),
],
),
)]);
assert_eq!(assert_three_way(SRC, args), Value::Int(4));
}
#[test]
fn schema_list_int_field_three_way() {
const SRC: &str = "#schema Cfg { nums: List<Int>, port: Int }\n\
#main(Cfg cfg) -> Int\ncfg.nums.length()";
let args = HashMap::from([(
"cfg".to_string(),
schema_val(
"Cfg",
vec![
(
"nums",
Value::list(vec![Value::Int(5), Value::Int(6), Value::Int(7)]),
),
("port", Value::Int(1)),
],
),
)]);
assert_eq!(assert_three_way(SRC, args), Value::Int(3));
}
#[test]
fn nested_schema_field_three_way() {
const SRC: &str = "#schema Inner { x: Int }\n\
#schema Outer { inner: Inner, tag: Int }\n\
#main(Outer o) -> Int\no.inner.x + o.tag";
let args = HashMap::from([(
"o".to_string(),
schema_val(
"Outer",
vec![
("inner", schema_val("Inner", vec![("x", Value::Int(7))])),
("tag", Value::Int(3)),
],
),
)]);
assert_eq!(assert_three_way(SRC, args), Value::Int(10));
}
#[test]
fn nested_schema_field_prefix_form_three_way() {
const SRC: &str = "#schema Inner { Int x: * }\n\
#schema Outer { Inner inner: *, Int tag: * }\n\
#main(Outer o) -> Int\no.inner.x + o.tag";
let args = HashMap::from([(
"o".to_string(),
schema_val(
"Outer",
vec![
("inner", schema_val("Inner", vec![("x", Value::Int(7))])),
("tag", Value::Int(3)),
],
),
)]);
assert_eq!(assert_three_way(SRC, args), Value::Int(10));
}
#[test]
fn nested_schema_field_three_levels_three_way() {
const SRC: &str = "#schema A { v: Int }\n\
#schema B { a: A }\n\
#schema C { b: B, k: Int }\n\
#main(C c) -> Int\nc.b.a.v + c.k";
let args = HashMap::from([(
"c".to_string(),
schema_val(
"C",
vec![
(
"b",
schema_val(
"B",
vec![("a", schema_val("A", vec![("v", Value::Int(40))]))],
),
),
("k", Value::Int(2)),
],
),
)]);
assert_eq!(assert_three_way(SRC, args), Value::Int(42));
}
#[test]
fn list_schema_param_length_three_way() {
const SRC: &str = "#schema Cfg { name: String, port: Int }\n\
#main(List<Cfg> items) -> Int\nitems.length()";
let args = HashMap::from([(
"items".to_string(),
Value::list(vec![
schema_val("Cfg", vec![("name", s("a")), ("port", Value::Int(1))]),
schema_val("Cfg", vec![("name", s("bb")), ("port", Value::Int(2))]),
]),
)]);
assert_eq!(assert_three_way(SRC, args), Value::Int(2));
}
#[test]
fn schema_list_schema_field_three_way() {
const SRC: &str = "#schema Inner { x: Int }\n\
#schema Cfg { items: List<Inner>, port: Int }\n\
#main(Cfg cfg) -> Int\ncfg.port";
let args = HashMap::from([(
"cfg".to_string(),
schema_val(
"Cfg",
vec![
(
"items",
Value::list(vec![
schema_val("Inner", vec![("x", Value::Int(10))]),
schema_val("Inner", vec![("x", Value::Int(20))]),
schema_val("Inner", vec![("x", Value::Int(30))]),
]),
),
("port", Value::Int(99)),
],
),
)]);
assert_eq!(assert_three_way(SRC, args), Value::Int(99));
}
#[test]
fn nested_list_int_param_length_three_way() {
const SRC: &str = "#main(List<List<Int>> xss) -> Int\nxss.length()";
let args = HashMap::from([(
"xss".to_string(),
Value::list(vec![
Value::list(vec![Value::Int(1), Value::Int(2)]),
Value::list(vec![Value::Int(3)]),
Value::list(vec![Value::Int(4), Value::Int(5), Value::Int(6)]),
Value::list(vec![]),
]),
)]);
assert_eq!(assert_three_way(SRC, args), Value::Int(4));
}
#[test]
fn schema_nested_list_field_three_way() {
const SRC: &str = "#schema Cfg { grid: List<List<Int>>, port: Int }\n\
#main(Cfg cfg) -> Int\ncfg.port";
let args = HashMap::from([(
"cfg".to_string(),
schema_val(
"Cfg",
vec![
(
"grid",
Value::list(vec![
Value::list(vec![Value::Int(1), Value::Int(2)]),
Value::list(vec![Value::Int(3), Value::Int(4)]),
]),
),
("port", Value::Int(7)),
],
),
)]);
assert_eq!(assert_three_way(SRC, args), Value::Int(7));
}
#[test]
fn nested_schema_with_list_schema_field_three_way() {
const SRC: &str = "#schema Leaf { x: Int }\n\
#schema Mid { items: List<Leaf> }\n\
#schema Outer { mid: Mid, tag: Int }\n\
#main(Outer o) -> Int\no.tag";
let args = HashMap::from([(
"o".to_string(),
schema_val(
"Outer",
vec![
(
"mid",
schema_val(
"Mid",
vec![(
"items",
Value::list(vec![
schema_val("Leaf", vec![("x", Value::Int(1))]),
schema_val("Leaf", vec![("x", Value::Int(2))]),
]),
)],
),
),
("tag", Value::Int(9)),
],
),
)]);
assert_eq!(assert_three_way(SRC, args), Value::Int(9));
}
#[test]
fn unsupported_pointer_indirect_shapes_loudly_capped() {
let src = "#main(Dict<String, Int> d) -> Dict<String, Int>\nd";
let cl = AotEvaluator::from_source(src);
assert!(cl.is_err(), "cranelift must loudly reject `{src}`, got Ok");
let llvm = LlvmAotEvaluator::from_source(src);
assert!(llvm.is_err(), "llvm must loudly reject `{src}`, got Ok");
}
#[test]
fn nested_pointer_array_param_length_both_backends() {
let src = "#main(List<List<String>> xss) -> Int\nxss.length()";
assert!(
AotEvaluator::from_source(src).is_ok(),
"cranelift must compile the List<List<String>> param length read"
);
assert!(
LlvmAotEvaluator::from_source(src).is_ok(),
"llvm must compile the List<List<String>> param length read"
);
}
#[test]
fn nested_list_identity_return_both_backends() {
let src = "#main(List<List<Int>> xss) -> List<List<Int>>\nxss";
assert!(
AotEvaluator::from_source(src).is_ok(),
"cranelift must compile the S1 nested-list identity return"
);
assert!(
LlvmAotEvaluator::from_source(src).is_ok(),
"llvm must compile the S2 nested-list identity return (in-place region-walk ABI)"
);
}
#[test]
fn list_schema_identity_return_both_backends() {
let src = "#schema P { name: String, x: Int }\n#main(List<P> ps) -> List<P>\nps";
assert!(
AotEvaluator::from_source(src).is_ok(),
"cranelift must compile the S4 List<Schema> identity return"
);
assert!(
LlvmAotEvaluator::from_source(src).is_ok(),
"llvm must compile the S4 List<Schema> identity return (in-place region-walk ABI)"
);
}
#[test]
fn param_field_list_return_both_backends() {
for src in [
"#schema P { x: Int }\n#schema W { ps: List<P>, n: Int }\n\
#main(W w) -> List<P>\nw.ps",
"#schema W { rows: List<List<Int>>, n: Int }\n\
#main(W w) -> List<List<Int>>\nw.rows",
] {
assert!(
AotEvaluator::from_source(src).is_ok(),
"cranelift must compile the F4 parameter-field list return: `{src}`"
);
assert!(
LlvmAotEvaluator::from_source(src).is_ok(),
"llvm must compile the F4 parameter-field list return: `{src}`"
);
}
}