use super::TestProject; use crate::module::CheckedModules; use aiken_lang::ast::{Definition, Function, TraceLevel, Tracing, TypedTest, TypedValidator}; use pretty_assertions::assert_eq; use std::rc::Rc; use uplc::{ ast::{Constant, Data, DeBruijn, Name, Program, Term, Type}, builder::{CONSTR_FIELDS_EXPOSER, CONSTR_INDEX_EXPOSER, EXPECT_ON_LIST}, machine::{cost_model::ExBudget, runtime::Compressable}, optimize::{self}, }; enum TestType { Func(TypedTest), Validator(TypedValidator), } fn assert_uplc(source_code: &str, expected: Term, should_fail: bool, verbose_mode: bool) { let mut project = TestProject::new(); let modules = CheckedModules::singleton(project.check(project.parse(source_code))); let mut generator = project.new_generator(if verbose_mode { Tracing::All(TraceLevel::Verbose) } else { Tracing::All(TraceLevel::Silent) }); let Some(checked_module) = modules.values().next() else { unreachable!("There's got to be one right?") }; let mut scripts = vec![]; for def in checked_module.ast.definitions() { if let Definition::Test(func) = def { scripts.push(( checked_module.input_path.clone(), checked_module.name.clone(), TestType::Func(func.clone()), )); } else if let Definition::Validator(func) = def { scripts.push(( checked_module.input_path.clone(), checked_module.name.clone(), TestType::Validator(func.clone()), )); } } assert_eq!(scripts.len(), 1); let script = &scripts[0]; match &script.2 { TestType::Func(Function { body: func, .. }) => { let program = generator.generate_raw(func, &[], &script.1); let pretty_program = program.to_pretty(); let debruijn_program: Program = program.try_into().unwrap(); let expected = Program { version: (1, 1, 0), term: expected, }; let expected = optimize::aiken_optimize_and_intern(expected); let pretty_expected = expected.to_pretty(); let expected: Program = expected.try_into().unwrap(); assert!( debruijn_program.to_pretty() == expected.to_pretty(), "=============== generated:\n{}\n\n=============== expected:\n{}", pretty_program, pretty_expected, ); let mut eval = debruijn_program.eval(ExBudget::default()); assert_eq!( eval.failed(false), should_fail, "logs - {}\n", format!("{:#?}", eval.logs()) ); assert!(if should_fail { eval.failed(false) } else { !eval.failed(false) }); } TestType::Validator(func) => { let program = generator.generate(func, &script.1); let pretty_program = program.to_pretty(); let debruijn_program: Program = program.try_into().unwrap(); let expected = Program { version: (1, 1, 0), term: expected, }; let expected = optimize::aiken_optimize_and_intern(expected); let pretty_expected = expected.to_pretty(); let expected: Program = expected.try_into().unwrap(); assert!( debruijn_program.to_pretty() == expected.to_pretty(), "=============== generated:\n{}\n\n=============== expected:\n{}", pretty_program, pretty_expected, ); } } } #[test] fn acceptance_test_1_length() { let src = r#" pub fn length(xs: List) -> Int { when xs is { [] -> 0 [_, ..rest] -> 1 + length(rest) } } test length_1() { length([1, 2, 3]) == 3 } "#; let uplc = Term::equals_integer() .apply( Term::var("length") .lambda("length") .apply(Term::var("length").apply(Term::var("length"))) .lambda("length") .apply( Term::var("xs") .delayed_choose_list( Term::integer(0.into()), Term::add_integer() .apply(Term::integer(1.into())) .apply( Term::var("length") .apply(Term::var("length")) .apply(Term::var("rest")), ) .lambda("rest") .apply(Term::tail_list().apply(Term::var("xs"))), ) .lambda("xs") .lambda("length"), ) .apply(Term::list_values(vec![ Constant::Data(Data::integer(1.into())), Constant::Data(Data::integer(2.into())), Constant::Data(Data::integer(3.into())), ])), ) .apply(Term::integer(3.into())); assert_uplc(src, uplc.clone(), false, true); assert_uplc(src, uplc, false, false); } #[test] fn acceptance_test_2_repeat() { let src = r#" pub fn repeat(x: a, n: Int) -> List { if n <= 0 { [] } else { [x, ..repeat(x, n - 1)] } } test repeat_1() { repeat("aiken", 2) == ["aiken", "aiken"] } "#; let uplc = Term::equals_data() .apply( Term::list_data().apply( Term::var("repeat") .lambda("repeat") .apply( Term::var("repeat") .apply(Term::var("repeat")) .apply(Term::var("n")) .lambda("repeat") .apply( Term::less_than_equals_integer() .apply(Term::var("n")) .apply(Term::integer(0.into())) .delayed_if_then_else( Term::empty_list(), Term::mk_cons() .apply(Term::b_data().apply(Term::var("x"))) .apply( Term::var("repeat") .apply(Term::var("repeat")) .apply( Term::subtract_integer() .apply(Term::var("n")) .apply(Term::integer(1.into())), ), ), ) .lambda("n") .lambda("repeat"), ) .lambda("n") .lambda("x"), ) .apply(Term::byte_string("aiken".as_bytes().to_vec())) .apply(Term::integer(2.into())), ), ) .apply(Term::list_data().apply(Term::list_values(vec![ Constant::Data(Data::bytestring("aiken".as_bytes().to_vec())), Constant::Data(Data::bytestring("aiken".as_bytes().to_vec())), ]))); assert_uplc(src, uplc.clone(), false, true); assert_uplc(src, uplc, false, false); } #[test] fn acceptance_test_3_concat() { let src = r#" pub fn foldr(xs: List, f: fn(a, b) -> b, zero: b) -> b { when xs is { [] -> zero [x, ..rest] -> f(x, foldr(rest, f, zero)) } } pub fn concat(left: List, right: List) -> List { foldr(left, fn(x, xs) { [x, ..xs] }, right) } test concat_1() { concat([1, 2, 3], [4, 5, 6]) == [1, 2, 3, 4, 5, 6] } "#; let uplc = Term::equals_data() .apply( Term::list_data().apply( Term::var("concat") .lambda("concat") .apply( Term::var("foldr") .apply(Term::var("left")) .apply( Term::mk_cons() .apply(Term::i_data().apply(Term::var("x"))) .apply(Term::var("xs")) .lambda("xs") .lambda("x"), ) .apply(Term::var("right")) .lambda("right") .lambda("left"), ) .lambda("foldr") .apply( Term::var("foldr") .apply(Term::var("foldr")) .apply(Term::var("xs")) .lambda("foldr") .apply( Term::var("xs") .delayed_choose_list( Term::var("zero"), Term::var("f") .apply(Term::var("x")) .apply( Term::var("foldr") .apply(Term::var("foldr")) .apply(Term::var("rest")), ) .lambda("rest") .apply(Term::tail_list().apply(Term::var("xs"))) .lambda("x") .apply(Term::un_i_data().apply( Term::head_list().apply(Term::var("xs")), )), ) .lambda("xs") .lambda("foldr"), ) .lambda("zero") .lambda("f") .lambda("xs"), ) .apply(Term::list_values(vec![ Constant::Data(Data::integer(1.into())), Constant::Data(Data::integer(2.into())), Constant::Data(Data::integer(3.into())), ])) .apply(Term::list_values(vec![ Constant::Data(Data::integer(4.into())), Constant::Data(Data::integer(5.into())), Constant::Data(Data::integer(6.into())), ])), ), ) .apply(Term::list_data().apply(Term::list_values(vec![ Constant::Data(Data::integer(1.into())), Constant::Data(Data::integer(2.into())), Constant::Data(Data::integer(3.into())), Constant::Data(Data::integer(4.into())), Constant::Data(Data::integer(5.into())), Constant::Data(Data::integer(6.into())), ]))); assert_uplc(src, uplc.clone(), false, true); assert_uplc(src, uplc, false, false); } #[test] fn acceptance_test_4_concat_no_anon_func() { let src = r#" pub fn foldr(xs: List, f: fn(a, b) -> b, zero: b) -> b { when xs is { [] -> zero [x, ..rest] -> f(x, foldr(rest, f, zero)) } } pub fn prepend(x: a, xs: List) -> List { [x, ..xs] } pub fn concat(left: List, right: List) -> List { foldr(left, prepend, right) } test concat_1() { concat([1, 2, 3], [4, 5, 6]) == [1, 2, 3, 4, 5, 6] } "#; let uplc = Term::equals_data() .apply( Term::list_data().apply( Term::var("concat") .lambda("concat") .apply( Term::var("foldr") .apply(Term::var("left")) .apply(Term::var("prepend")) .apply(Term::var("right")) .lambda("right") .lambda("left"), ) .lambda("prepend") .apply( Term::mk_cons() .apply(Term::i_data().apply(Term::var("x"))) .apply(Term::var("xs")) .lambda("xs") .lambda("x"), ) .lambda("foldr") .apply( Term::var("foldr") .apply(Term::var("foldr")) .apply(Term::var("xs")) .lambda("foldr") .apply( Term::var("xs") .delayed_choose_list( Term::var("zero"), Term::var("f") .apply(Term::var("x")) .apply( Term::var("foldr") .apply(Term::var("foldr")) .apply(Term::var("rest")), ) .lambda("rest") .apply(Term::tail_list().apply(Term::var("xs"))) .lambda("x") .apply(Term::un_i_data().apply( Term::head_list().apply(Term::var("xs")), )), ) .lambda("xs") .lambda("foldr"), ) .lambda("zero") .lambda("f") .lambda("xs"), ) .apply(Term::list_values(vec![ Constant::Data(Data::integer(1.into())), Constant::Data(Data::integer(2.into())), Constant::Data(Data::integer(3.into())), ])) .apply(Term::list_values(vec![ Constant::Data(Data::integer(4.into())), Constant::Data(Data::integer(5.into())), Constant::Data(Data::integer(6.into())), ])), ), ) .apply(Term::list_data().apply(Term::list_values(vec![ Constant::Data(Data::integer(1.into())), Constant::Data(Data::integer(2.into())), Constant::Data(Data::integer(3.into())), Constant::Data(Data::integer(4.into())), Constant::Data(Data::integer(5.into())), Constant::Data(Data::integer(6.into())), ]))); assert_uplc(src, uplc.clone(), false, true); assert_uplc(src, uplc, false, false); } #[test] fn acceptance_test_5_direct_head() { let src = r#" use aiken/builtin.{head_list} test head_1() { let head = fn(xs){ when xs is { [] -> None _ -> Some(head_list(xs)) } } head([1, 2, 3]) == Some(1) } "#; let uplc = Term::equals_data() .apply( Term::var("head") .lambda("head") .apply( Term::var("xs") .delayed_choose_list( Term::Constant(Constant::Data(Data::constr(1, vec![])).into()), Term::constr_data().apply(Term::integer(0.into())).apply( Term::mk_cons() .apply(Term::head_list().apply(Term::var("xs"))) .apply(Term::empty_list()), ), ) .lambda("xs"), ) .apply(Term::list_values(vec![ Constant::Data(Data::integer(1.into())), Constant::Data(Data::integer(2.into())), Constant::Data(Data::integer(3.into())), ])), ) .apply(Term::Constant( Constant::Data(Data::constr(0, vec![Data::integer(1.into())])).into(), )); assert_uplc(src, uplc.clone(), false, true); assert_uplc(src, uplc, false, false); } #[test] fn acceptance_test_5_direct_2_heads() { let src = r#" use aiken/builtin.{head_list} test head_2() { let head = fn(xs: List){ when xs is { [] -> None [a] -> Some(xs) [a, b, ..] -> Some([a,b]) } } head([1, 2, 3]) == Some([1, 2]) } "#; let uplc = Term::equals_data() .apply( Term::var("head") .lambda("head") .apply( Term::var("xs") .delayed_choose_list( Term::Constant(Constant::Data(Data::constr(1, vec![])).into()), Term::var("tail_1") .delayed_choose_list( Term::constr_data() .apply(Term::integer(0.into())) .apply( Term::mk_cons() .apply(Term::list_data().apply(Term::var("xs"))) .apply(Term::empty_list()), ) .lambda("a") .apply( Term::un_i_data() .apply(Term::head_list().apply(Term::var("xs"))), ), Term::constr_data() .apply(Term::integer(0.into())) .apply( Term::mk_cons() .apply( Term::list_data().apply( Term::mk_cons() .apply( Term::i_data() .apply(Term::var("a")), ) .apply( Term::mk_cons() .apply( Term::i_data() .apply(Term::var("b")), ) .apply(Term::empty_list()), ), ), ) .apply(Term::empty_list()), ) .lambda("b") .apply( Term::un_i_data().apply( Term::head_list().apply(Term::var("tail_1")), ), ) .lambda("a") .apply( Term::un_i_data() .apply(Term::head_list().apply(Term::var("xs"))), ), ) .lambda("tail_1") .apply(Term::tail_list().apply(Term::var("xs"))), ) .lambda("xs"), ) .apply(Term::list_values(vec![ Constant::Data(Data::integer(1.into())), Constant::Data(Data::integer(2.into())), Constant::Data(Data::integer(3.into())), ])), ) .apply(Term::Constant( Constant::Data(Data::constr( 0, vec![Data::list(vec![ Data::integer(1.into()), Data::integer(2.into()), ])], )) .into(), )); assert_uplc(src, uplc.clone(), false, true); assert_uplc(src, uplc, false, false); } #[test] fn acceptance_test_5_head_not_empty() { let src = r#" use aiken/builtin.{head_list} pub fn head(xs: List) -> Option { when xs is { [] -> None _ -> Some(head_list(xs)) } } test head_1() { head([1, 2, 3]) == Some(1) } "#; let uplc = Term::equals_data() .apply( Term::var("head") .lambda("head") .apply( Term::var("xs") .delayed_choose_list( Term::Constant(Constant::Data(Data::constr(1, vec![])).into()), Term::constr_data().apply(Term::integer(0.into())).apply( Term::mk_cons() .apply(Term::head_list().apply(Term::var("xs"))) .apply(Term::empty_list()), ), ) .lambda("xs"), ) .apply(Term::list_values(vec![ Constant::Data(Data::integer(1.into())), Constant::Data(Data::integer(2.into())), Constant::Data(Data::integer(3.into())), ])), ) .apply(Term::Constant( Constant::Data(Data::constr(0, vec![Data::integer(1.into())])).into(), )); assert_uplc(src, uplc.clone(), false, true); assert_uplc(src, uplc, false, false); } #[test] fn acceptance_test_5_head_empty() { let src = r#" use aiken/builtin.{head_list} pub fn head(xs: List) -> Option { when xs is { [] -> None _ -> Some(head_list(xs)) } } test head_1() { head([]) == None } "#; let uplc = Term::equals_data() .apply( Term::var("head") .lambda("head") .apply( Term::var("xs") .delayed_choose_list( Term::Constant(Constant::Data(Data::constr(1, vec![])).into()), Term::constr_data().apply(Term::integer(0.into())).apply( Term::mk_cons() .apply(Term::head_list().apply(Term::var("xs"))) .apply(Term::empty_list()), ), ) .lambda("xs"), ) .apply(Term::list_values(vec![])), ) .apply(Term::Constant( Constant::Data(Data::constr(1, vec![])).into(), )); assert_uplc(src, uplc.clone(), false, true); assert_uplc(src, uplc, false, false); } #[test] fn acceptance_test_6_if_else() { let src = r#" test bar() { let x = 1 if x == 1 { True } else { False } } "#; let uplc = Term::equals_integer() .apply(Term::integer(1.into())) .apply(Term::integer(1.into())) .delayed_if_then_else(Term::bool(true), Term::bool(false)); assert_uplc(src, uplc.clone(), false, true); assert_uplc(src, uplc, false, false); } #[test] fn acceptance_test_6_equals_pair() { let src = r#" test foo() { Pair(1, []) == Pair(1, []) } "#; let uplc = Term::equals_data() .apply( Term::map_data().apply( Term::mk_cons() .apply(Term::Constant( Constant::ProtoPair( Type::Data, Type::Data, Constant::Data(Data::integer(1.into())).into(), Constant::Data(Data::list(vec![])).into(), ) .into(), )) .apply(Term::empty_map()), ), ) .apply( Term::map_data().apply( Term::mk_cons() .apply(Term::Constant( Constant::ProtoPair( Type::Data, Type::Data, Constant::Data(Data::integer(1.into())).into(), Constant::Data(Data::list(vec![])).into(), ) .into(), )) .apply(Term::empty_map()), ), ); assert_uplc(src, uplc.clone(), false, true); assert_uplc(src, uplc, false, false); } #[test] fn acceptance_test_6_equals_tuple() { let src = r#" test foo() { (1, []) == (1, []) } "#; let uplc = Term::equals_data() .apply( Term::list_data().apply(Term::Constant( Constant::ProtoList( Type::Data, vec![ Constant::Data(Data::integer(1.into())), Constant::Data(Data::list(vec![])), ], ) .into(), )), ) .apply( Term::list_data().apply(Term::Constant( Constant::ProtoList( Type::Data, vec![ Constant::Data(Data::integer(1.into())), Constant::Data(Data::list(vec![])), ], ) .into(), )), ); assert_uplc(src, uplc.clone(), false, true); assert_uplc(src, uplc, false, false); } #[test] fn acceptance_test_7_unzip_tuple() { let src = r#" pub fn unzip(xs: List<(a, b)>) -> (List, List) { when xs is { [] -> ([], []) [(a, b), ..rest] -> { let (a_tail, b_tail) = unzip(rest) ([a, ..a_tail], [b, ..b_tail]) } } } test unzip1() { let x = [(3, #"55"), (4, #"7799")] unzip(x) == ([3, 4], [#"55", #"7799"]) } "#; let uplc = Term::equals_data() .apply( Term::list_data().apply( Term::var("unzip") .lambda("unzip") .apply(Term::var("unzip").apply(Term::var("unzip"))) .lambda("unzip") .apply( Term::var("xs") .delayed_choose_list( Term::list_values(vec![ Constant::Data(Data::list(vec![])), Constant::Data(Data::list(vec![])), ]), Term::mk_cons() .apply( Term::list_data().apply( Term::mk_cons() .apply(Term::i_data().apply(Term::var("a"))) .apply(Term::var("a_tail")), ), ) .apply( Term::mk_cons() .apply( Term::list_data().apply( Term::mk_cons() .apply( Term::b_data() .apply(Term::var("b")), ) .apply(Term::var("b_tail")), ), ) .apply(Term::empty_list()), ) .lambda("b_tail") .apply(Term::unlist_data().apply(Term::head_list().apply( Term::tail_list().apply(Term::var("tail_tuple")), ))) .lambda("a_tail") .apply(Term::unlist_data().apply( Term::head_list().apply(Term::var("tail_tuple")), )) .lambda("tail_tuple") .apply( Term::var("unzip") .apply(Term::var("unzip")) .apply(Term::var("rest")), ) .lambda("rest") .lambda("b") .lambda("a") .apply(Term::un_i_data().apply( Term::head_list().apply(Term::var("head_tuple")), )) .apply(Term::un_b_data().apply(Term::head_list().apply( Term::tail_list().apply(Term::var("head_tuple")), ))) .apply(Term::tail_list().apply(Term::var("xs"))) .lambda("head_tuple") .apply( Term::unlist_data() .apply(Term::head_list().apply(Term::var("xs"))), ), ) .lambda("xs") .lambda("unzip"), ) .apply(Term::var("x")), ), ) .apply(Term::list_data().apply(Term::list_values(vec![ Constant::Data(Data::list(vec![ Data::integer(3.into()), Data::integer(4.into()), ])), Constant::Data(Data::list(vec![ Data::bytestring(vec![85]), Data::bytestring(vec![119, 153]), ])), ]))) .lambda("x") .apply(Term::list_values(vec![ Constant::Data(Data::list(vec![ Data::integer(3.into()), Data::bytestring(vec![85]), ])), Constant::Data(Data::list(vec![ Data::integer(4.into()), Data::bytestring(vec![119, 153]), ])), ])); assert_uplc(src, uplc.clone(), false, true); assert_uplc(src, uplc, false, false); } #[test] fn acceptance_test_7_unzip_pair() { let src = r#" type Pairs = List> pub fn unzip(xs: Pairs) -> Pair, List> { when xs is { [] -> Pair([], []) [Pair(a, b), ..rest] -> { let Pair(a_tail, b_tail) = unzip(rest) Pair([a, ..a_tail], [b, ..b_tail]) } } } test unzip1() { let x = [Pair(3, #"55"), Pair(4, #"7799")] unzip(x) == Pair([3, 4], [#"55", #"7799"]) } "#; let uplc = Term::equals_data() .apply( Term::map_data().apply( Term::mk_cons() .apply( Term::var("unzip") .lambda("unzip") .apply(Term::var("unzip").apply(Term::var("unzip"))) .lambda("unzip") .apply( Term::var("xs") .delayed_choose_list( Term::pair_values( Constant::Data(Data::list(vec![])), Constant::Data(Data::list(vec![])), ), Term::mk_pair_data() .apply( Term::list_data().apply( Term::mk_cons() .apply(Term::i_data().apply(Term::var("a"))) .apply(Term::var("a_tail")), ), ) .apply( Term::list_data().apply( Term::mk_cons() .apply(Term::b_data().apply(Term::var("b"))) .apply(Term::var("b_tail")), ), ) .lambda("b_tail") .apply(Term::unlist_data().apply( Term::snd_pair().apply(Term::var("tail_pair")), )) .lambda("a_tail") .apply(Term::unlist_data().apply( Term::fst_pair().apply(Term::var("tail_pair")), )) .lambda("tail_pair") .apply( Term::var("unzip") .apply(Term::var("unzip")) .apply(Term::var("rest")), ) .lambda("rest") .lambda("b") .lambda("a") .apply(Term::un_i_data().apply( Term::fst_pair().apply(Term::var("head_pair")), )) .apply(Term::un_b_data().apply( Term::snd_pair().apply(Term::var("head_pair")), )) .apply(Term::tail_list().apply(Term::var("xs"))) .lambda("head_pair") .apply(Term::head_list().apply(Term::var("xs"))), ) .lambda("xs") .lambda("unzip"), ) .apply(Term::var("x")), ) .apply(Term::empty_map()), ), ) .apply( Term::map_data().apply( Term::mk_cons() .apply(Term::pair_values( Constant::Data(Data::list(vec![ Data::integer(3.into()), Data::integer(4.into()), ])), Constant::Data(Data::list(vec![ Data::bytestring(vec![85]), Data::bytestring(vec![119, 153]), ])), )) .apply(Term::empty_map()), ), ) .lambda("x") .apply(Term::map_values(vec![ Constant::ProtoPair( Type::Data, Type::Data, Constant::Data(Data::integer(3.into())).into(), Constant::Data(Data::bytestring(vec![85])).into(), ), Constant::ProtoPair( Type::Data, Type::Data, Constant::Data(Data::integer(4.into())).into(), Constant::Data(Data::bytestring(vec![119, 153])).into(), ), ])); assert_uplc(src, uplc.clone(), false, true); assert_uplc(src, uplc, false, false); } #[test] fn acceptance_test_8_is_empty() { let src = r#" use aiken/builtin pub fn is_empty(bytes: ByteArray) -> Bool { builtin.length_of_bytearray(bytes) == 0 } test is_empty_1() { is_empty(#"") == True } "#; let uplc = Term::var("is_empty") .lambda("is_empty") .apply( Term::equals_integer() .apply(Term::length_of_bytearray().apply(Term::var("bytes"))) .apply(Term::integer(0.into())) .lambda("bytes"), ) .apply(Term::byte_string(vec![])) .delayed_if_then_else( Term::bool(true), Term::bool(true).if_then_else(Term::bool(false), Term::bool(true)), ); assert_uplc(src, uplc.clone(), false, true); assert_uplc(src, uplc, false, false); } #[test] fn acceptance_test_8_is_not_empty() { let src = r#" use aiken/builtin pub fn is_empty(bytes: ByteArray) -> Bool { builtin.length_of_bytearray(bytes) == 0 } test is_empty_1() { is_empty(#"01") == False } "#; let uplc = Term::var("is_empty") .lambda("is_empty") .apply( Term::equals_integer() .apply(Term::length_of_bytearray().apply(Term::var("bytes"))) .apply(Term::integer(0.into())) .lambda("bytes"), ) .apply(Term::byte_string(vec![1])) .delayed_if_then_else( Term::bool(false), Term::bool(false).if_then_else(Term::bool(false), Term::bool(true)), ); assert_uplc(src, uplc.clone(), false, true); assert_uplc(src, uplc, false, false); } #[test] fn acceptance_test_9_is_empty() { let src = r#" use aiken/builtin.{length_of_bytearray} pub fn is_empty(bytes: ByteArray) -> Bool { length_of_bytearray(bytes) == 0 } test is_empty_1() { is_empty(#"") == True } "#; let uplc = Term::var("is_empty") .lambda("is_empty") .apply( Term::equals_integer() .apply(Term::length_of_bytearray().apply(Term::var("bytes"))) .apply(Term::integer(0.into())) .lambda("bytes"), ) .apply(Term::byte_string(vec![])) .delayed_if_then_else( Term::bool(true), Term::bool(true).if_then_else(Term::bool(false), Term::bool(true)), ); assert_uplc(src, uplc.clone(), false, true); assert_uplc(src, uplc, false, false); } #[test] fn acceptance_test_10_map_none() { let src = r#" pub fn map(opt: Option, f: fn(a) -> b) -> Option { when opt is { None -> None Some(a) -> Some(f(a)) } } fn add_one(n: Int) -> Int { n + 1 } test map_1() { map(None, add_one) == None } "#; let uplc = Term::equals_data() .apply( Term::var("map") .lambda("map") .apply( Term::equals_integer() .apply(Term::integer(1.into())) .apply(Term::var("constr_index")) .delayed_if_then_else( Term::Constant(Constant::Data(Data::constr(1, vec![])).into()), Term::constr_data() .apply(Term::integer(0.into())) .apply( Term::mk_cons() .apply( Term::i_data() .apply(Term::var("f").apply(Term::var("a"))), ) .apply(Term::empty_list()), ) .lambda("a") .apply( Term::un_i_data() .apply(Term::head_list().apply(Term::var("constr_fields"))), ) .lambda("constr_fields") .apply( Term::snd_pair() .apply(Term::unconstr_data().apply(Term::var("opt"))), ), ) .lambda("constr_index") .apply( Term::fst_pair().apply(Term::unconstr_data().apply(Term::var("opt"))), ) .lambda("f") .lambda("opt"), ) .apply(Term::Constant( Constant::Data(Data::constr(1, vec![])).into(), )) .apply( Term::var("add_one").lambda("add_one").apply( Term::add_integer() .apply(Term::var("n")) .apply(Term::integer(1.into())) .lambda("n"), ), ), ) .apply(Term::Constant( Constant::Data(Data::constr(1, vec![])).into(), )); assert_uplc(src, uplc.clone(), false, true); assert_uplc(src, uplc, false, false); } #[test] fn acceptance_test_10_map_some() { let src = r#" pub fn map(opt: Option, f: fn(a) -> b) -> Option { when opt is { None -> None Some(a) -> Some(f(a)) } } fn add_one(n: Int) -> Int { n + 1 } test map_1() { map(Some(1), add_one) == Some(2) } "#; let uplc = Term::equals_data() .apply( Term::var("map") .lambda("map") .apply( Term::equals_integer() .apply(Term::integer(1.into())) .apply(Term::var("constr_index")) .delayed_if_then_else( Term::Constant(Constant::Data(Data::constr(1, vec![])).into()), Term::constr_data() .apply(Term::integer(0.into())) .apply( Term::mk_cons() .apply( Term::i_data() .apply(Term::var("f").apply(Term::var("a"))), ) .apply(Term::empty_list()), ) .lambda("a") .apply( Term::un_i_data() .apply(Term::head_list().apply(Term::var("constr_fields"))), ) .lambda("constr_fields") .apply( Term::snd_pair() .apply(Term::unconstr_data().apply(Term::var("opt"))), ), ) .lambda("constr_index") .apply( Term::fst_pair().apply(Term::unconstr_data().apply(Term::var("opt"))), ) .lambda("f") .lambda("opt"), ) .apply(Term::Constant( Constant::Data(Data::constr(0, vec![Data::integer(1.into())])).into(), )) .apply( Term::var("add_one").lambda("add_one").apply( Term::add_integer() .apply(Term::var("n")) .apply(Term::integer(1.into())) .lambda("n"), ), ), ) .apply(Term::Constant( Constant::Data(Data::constr(0, vec![Data::integer(2.into())])).into(), )); assert_uplc(src, uplc.clone(), false, true); assert_uplc(src, uplc, false, false); } #[test] fn acceptance_test_11_map_empty() { let src = r#" pub fn map(xs: List, f: fn(a) -> result) -> List { when xs is { [] -> [] [x, ..rest] -> [f(x), ..map(rest, f)] } } test map_1() { map([], fn(n) { n + 1 }) == [] } "#; let uplc = Term::equals_data() .apply( Term::list_data().apply( Term::var("map") .lambda("map") .apply( Term::var("map") .apply(Term::var("map")) .apply(Term::var("xs")) .lambda("map") .apply( Term::var("xs") .delayed_choose_list( Term::empty_list(), Term::mk_cons() .apply( Term::i_data() .apply(Term::var("f").apply(Term::var("x"))), ) .apply( Term::var("map") .apply(Term::var("map")) .apply(Term::var("rest")), ) .lambda("rest") .apply(Term::tail_list().apply(Term::var("xs"))) .lambda("x") .apply( Term::un_i_data().apply( Term::head_list().apply(Term::var("xs")), ), ), ) .lambda("xs") .lambda("map"), ) .lambda("f") .lambda("xs"), ) .apply(Term::empty_list()) .apply( Term::add_integer() .apply(Term::var("n")) .apply(Term::integer(1.into())) .lambda("n"), ), ), ) .apply(Term::list_data().apply(Term::empty_list())); assert_uplc(src, uplc.clone(), false, true); assert_uplc(src, uplc, false, false); } #[test] fn acceptance_test_11_map_filled() { let src = r#" pub fn map(xs: List, f: fn(a) -> result) -> List { when xs is { [] -> [] [x, ..rest] -> [f(x), ..map(rest, f)] } } test map_1() { map([6, 7, 8], fn(n) { n + 1 }) == [7, 8, 9] } "#; let uplc = Term::equals_data() .apply( Term::list_data().apply( Term::var("map") .lambda("map") .apply( Term::var("map") .apply(Term::var("map")) .apply(Term::var("xs")) .lambda("map") .apply( Term::var("xs") .delayed_choose_list( Term::empty_list(), Term::mk_cons() .apply( Term::i_data() .apply(Term::var("f").apply(Term::var("x"))), ) .apply( Term::var("map") .apply(Term::var("map")) .apply(Term::var("rest")), ) .lambda("rest") .apply(Term::tail_list().apply(Term::var("xs"))) .lambda("x") .apply( Term::un_i_data().apply( Term::head_list().apply(Term::var("xs")), ), ), ) .lambda("xs") .lambda("map"), ) .lambda("f") .lambda("xs"), ) .apply(Term::list_values(vec![ Constant::Data(Data::integer(6.into())), Constant::Data(Data::integer(7.into())), Constant::Data(Data::integer(8.into())), ])) .apply( Term::add_integer() .apply(Term::var("n")) .apply(Term::integer(1.into())) .lambda("n"), ), ), ) .apply(Term::list_data().apply(Term::list_values(vec![ Constant::Data(Data::integer(7.into())), Constant::Data(Data::integer(8.into())), Constant::Data(Data::integer(9.into())), ]))); assert_uplc(src, uplc.clone(), false, true); assert_uplc(src, uplc, false, false); } #[test] fn acceptance_test_12_filter_even() { let src = r#" use aiken/builtin pub fn filter(xs: List, f: fn(a) -> Bool) -> List { when xs is { [] -> [] [x, ..rest] -> if f(x) { [x, ..filter(rest, f)] } else { filter(rest, f) } } } test filter_1() { filter([1, 2, 3, 4, 5, 6], fn(x) { builtin.mod_integer(x, 2) == 0 }) == [2, 4, 6] } "#; let uplc = Term::equals_data() .apply( Term::list_data().apply( Term::var("filter") .lambda("filter") .apply( Term::var("filter") .apply(Term::var("filter")) .apply(Term::var("xs")) .lambda("filter") .apply( Term::var("xs") .delayed_choose_list( Term::empty_list(), Term::var("f") .apply(Term::var("x")) .delayed_if_then_else( Term::mk_cons() .apply(Term::i_data().apply(Term::var("x"))) .apply( Term::var("filter") .apply(Term::var("filter")) .apply(Term::var("rest")), ), Term::var("filter") .apply(Term::var("filter")) .apply(Term::var("rest")), ) .lambda("rest") .lambda("x") .apply(Term::un_i_data().apply( Term::head_list().apply(Term::var("xs")), )) .apply(Term::tail_list().apply(Term::var("xs"))), ) .lambda("xs") .lambda("filter"), ) .lambda("f") .lambda("xs"), ) .apply(Term::list_values(vec![ Constant::Data(Data::integer(1.into())), Constant::Data(Data::integer(2.into())), Constant::Data(Data::integer(3.into())), Constant::Data(Data::integer(4.into())), Constant::Data(Data::integer(5.into())), Constant::Data(Data::integer(6.into())), ])) .apply( Term::equals_integer() .apply( Term::mod_integer() .apply(Term::var("x")) .apply(Term::integer(2.into())), ) .apply(Term::integer(0.into())) .lambda("x"), ), ), ) .apply(Term::list_data().apply(Term::list_values(vec![ Constant::Data(Data::integer(2.into())), Constant::Data(Data::integer(4.into())), Constant::Data(Data::integer(6.into())), ]))); assert_uplc(src, uplc.clone(), false, true); assert_uplc(src, uplc, false, false); } #[test] fn acceptance_test_14_list_creation() { let src = r#" test foo() { [0 - 2, 0 - 1, 0] == [-2, -1, 0] } "#; let uplc = Term::equals_data() .apply( Term::list_data().apply( Term::mk_cons() .apply( Term::i_data().apply( Term::subtract_integer() .apply(Term::integer(0.into())) .apply(Term::integer(2.into())), ), ) .apply( Term::mk_cons() .apply( Term::i_data().apply( Term::subtract_integer() .apply(Term::integer(0.into())) .apply(Term::integer(1.into())), ), ) .apply( Term::mk_cons() .apply(Term::Constant( Constant::Data(Data::integer(0.into())).into(), )) .apply(Term::empty_list()), ), ), ), ) .apply(Term::list_data().apply(Term::list_values(vec![ Constant::Data(Data::integer((-2).into())), Constant::Data(Data::integer((-1).into())), Constant::Data(Data::integer(0.into())), ]))); assert_uplc(src, uplc.clone(), false, true); assert_uplc(src, uplc, false, false); } #[test] fn acceptance_test_15_zero_arg() { let src = r#" pub opaque type Pairs { inner: List>, } pub fn new() { Pairs { inner: [] } } test new_1() { new() == Pairs { inner: [] } } "#; let uplc = Term::equals_data() .apply(Term::map_data().apply(Term::empty_map())) .apply(Term::map_data().apply(Term::empty_map())); assert_uplc(src, uplc.clone(), false, true); assert_uplc(src, uplc, false, false); } #[test] fn acceptance_test_16_drop() { let src = r#" use aiken/builtin pub fn slice(bytes: ByteArray, start: Int, end: Int) -> ByteArray { builtin.slice_bytearray(start, end, bytes) } pub fn length(bytes: ByteArray) -> Int { builtin.length_of_bytearray(bytes) } pub fn drop(bytes: ByteArray, n: Int) -> ByteArray { slice(bytes, n, length(bytes) - n) } test drop_1() { let x = #"01020304050607" drop(x, 2) == #"0304050607" } "#; let uplc = Term::equals_bytestring() .apply( Term::var("drop") .lambda("drop") .apply( Term::var("slice") .apply(Term::var("bytes")) .apply(Term::var("n")) .apply( Term::subtract_integer() .apply(Term::var("length").apply(Term::var("bytes"))) .apply(Term::var("n")), ) .lambda("n") .lambda("bytes"), ) .lambda("slice") .apply( Term::slice_bytearray() .apply(Term::var("start")) .apply(Term::var("end")) .apply(Term::var("bytes")) .lambda("end") .lambda("start") .lambda("bytes"), ) .lambda("length") .apply( Term::length_of_bytearray() .apply(Term::var("bytes")) .lambda("bytes"), ) .apply(Term::var("x")) .apply(Term::integer(2.into())), ) .apply(Term::byte_string(vec![3, 4, 5, 6, 7])) .lambda("x") .apply(Term::byte_string(vec![1, 2, 3, 4, 5, 6, 7])); assert_uplc(src, uplc.clone(), false, true); assert_uplc(src, uplc, false, false); } #[test] fn acceptance_test_17_take() { let src = r#" use aiken/builtin pub fn slice(bytes: ByteArray, start: Int, end: Int) -> ByteArray { builtin.slice_bytearray(start, end, bytes) } pub fn take(bytes: ByteArray, n: Int) -> ByteArray { slice(bytes, 0, n) } test take_1() { take(#"010203", 2) == #"0102" } "#; let uplc = Term::equals_bytestring() .apply( Term::var("take") .lambda("take") .apply( Term::var("slice") .apply(Term::var("bytes")) .apply(Term::integer(0.into())) .apply(Term::var("n")) .lambda("n") .lambda("bytes"), ) .lambda("slice") .apply( Term::slice_bytearray() .apply(Term::var("start")) .apply(Term::var("end")) .apply(Term::var("bytes")) .lambda("end") .lambda("start") .lambda("bytes"), ) .apply(Term::byte_string(vec![1, 2, 3])) .apply(Term::integer(2.into())), ) .apply(Term::byte_string(vec![1, 2])); assert_uplc(src, uplc.clone(), false, true); assert_uplc(src, uplc, false, false); } #[test] fn acceptance_test_18_or_else() { let src = r#" pub fn or_else(opt: Option, default: a) -> a { when opt is { None -> default Some(a) -> a } } test or_else_2() { or_else(Some(42), 14) == 42 } "#; let uplc = Term::equals_integer() .apply( Term::var("or_else") .lambda("or_else") .apply( Term::equals_integer() .apply(Term::integer(1.into())) .apply(Term::var("subject")) .delayed_if_then_else( Term::var("default"), Term::var("a") .lambda("a") .apply( Term::un_i_data() .apply(Term::head_list().apply(Term::var("opt_fields"))), ) .lambda("opt_fields") .apply(Term::var(CONSTR_FIELDS_EXPOSER).apply(Term::var("opt"))), ) .lambda("subject") .apply( Term::fst_pair().apply(Term::unconstr_data().apply(Term::var("opt"))), ) .lambda("default") .lambda("opt"), ) .apply(Term::data(Data::constr(0, vec![Data::integer(42.into())]))) .apply(Term::integer(14.into())), ) .apply(Term::integer(42.into())) .lambda(CONSTR_FIELDS_EXPOSER) .apply( Term::snd_pair() .apply(Term::unconstr_data().apply(Term::var("x"))) .lambda("x"), ); assert_uplc(src, uplc.clone(), false, true); assert_uplc(src, uplc, false, false); } #[test] fn acceptance_test_19_map_none_wrap_int() { let src = r#" pub fn map(opt: Option, f: fn(a) -> result) -> Option { when opt is { None -> None Some(a) -> Some(f(a)) } } test map_1() { map(None, fn(_) { 14 }) == None } "#; let uplc = Term::equals_data() .apply( Term::var("map") .lambda("map") .apply( Term::equals_integer() .apply(Term::integer(1.into())) .apply(Term::var("subject")) .delayed_if_then_else( Term::data(Data::constr(1, vec![])), Term::constr_data() .apply(Term::integer(0.into())) .apply( Term::mk_cons() .apply( Term::i_data() .apply(Term::var("f").apply(Term::var("a"))), ) .apply(Term::empty_list()), ) .lambda("a") // "a" generic is unbound in this case thus // the aiken compiler does not unwrap the value to pass to f .apply(Term::head_list().apply(Term::var("opt_fields"))) .lambda("opt_fields") .apply(Term::var(CONSTR_FIELDS_EXPOSER).apply(Term::var("opt"))), ) .lambda("subject") .apply( Term::fst_pair().apply(Term::unconstr_data().apply(Term::var("opt"))), ) .lambda("f") .lambda("opt"), ) .apply(Term::data(Data::constr(1, vec![]))) .apply(Term::integer(14.into()).lambda("_")), ) .apply(Term::data(Data::constr(1, vec![]))) .lambda(CONSTR_FIELDS_EXPOSER) .apply( Term::snd_pair() .apply(Term::unconstr_data().apply(Term::var("x"))) .lambda("x"), ); assert_uplc(src, uplc.clone(), false, true); assert_uplc(src, uplc, false, false); } #[test] fn acceptance_test_19_map_wrap_void() { let src = r#" pub fn map(opt: Option, f: fn(a) -> result) -> Option { when opt is { None -> None Some(a) -> Some(f(a)) } } test map_1() { map(None, fn(_) { Void }) == None } "#; let uplc = Term::equals_data() .apply( Term::var("map") .lambda("map") .apply( Term::equals_integer() .apply(Term::integer(1.into())) .apply(Term::var("subject")) .delayed_if_then_else( Term::data(Data::constr(1, vec![])), Term::constr_data() .apply(Term::integer(0.into())) .apply( Term::mk_cons() .apply( Term::data(Data::constr(0, vec![])) .lambda("_") .apply(Term::var("f").apply(Term::var("a"))), ) .apply(Term::empty_list()), ) .lambda("a") // "a" generic is unbound in this case thus // the aiken compiler does not unwrap the value to pass to f .apply(Term::head_list().apply(Term::var("opt_fields"))) .lambda("opt_fields") .apply(Term::var(CONSTR_FIELDS_EXPOSER).apply(Term::var("opt"))), ) .lambda("subject") .apply( Term::fst_pair().apply(Term::unconstr_data().apply(Term::var("opt"))), ) .lambda("f") .lambda("opt"), ) .apply(Term::data(Data::constr(1, vec![]))) .apply(Term::unit().lambda("_")), ) .apply(Term::data(Data::constr(1, vec![]))) .lambda(CONSTR_FIELDS_EXPOSER) .apply( Term::snd_pair() .apply(Term::unconstr_data().apply(Term::var("x"))) .lambda("x"), ); assert_uplc(src, uplc.clone(), false, true); assert_uplc(src, uplc, false, false); } #[test] fn acceptance_test_20_map_some() { let src = r#" pub fn map(opt: Option, f: fn(a) -> result) -> Option { when opt is { None -> None Some(a) -> Some(f(a)) } } test map_1() { map(Some(14), fn(n){ n + 1 }) == Some(15) } "#; let uplc = Term::equals_data() .apply( Term::var("map") .lambda("map") .apply( Term::equals_integer() .apply(Term::integer(1.into())) .apply(Term::var("constr_index")) .delayed_if_then_else( Term::Constant(Constant::Data(Data::constr(1, vec![])).into()), Term::constr_data() .apply(Term::integer(0.into())) .apply( Term::mk_cons() .apply( Term::i_data() .apply(Term::var("f").apply(Term::var("a"))), ) .apply(Term::empty_list()), ) .lambda("a") .apply( Term::un_i_data() .apply(Term::head_list().apply(Term::var("constr_fields"))), ) .lambda("constr_fields") .apply(Term::var(CONSTR_FIELDS_EXPOSER).apply(Term::var("opt"))), ) .lambda("constr_index") .apply( Term::fst_pair().apply(Term::unconstr_data().apply(Term::var("opt"))), ) .lambda("f") .lambda("opt"), ) .apply(Term::Constant( Constant::Data(Data::constr(0, vec![Data::integer(14.into())])).into(), )) .apply( Term::add_integer() .apply(Term::var("n")) .apply(Term::integer(1.into())) .lambda("n"), ), ) .apply(Term::Constant( Constant::Data(Data::constr(0, vec![Data::integer(15.into())])).into(), )) .constr_fields_exposer(); assert_uplc(src, uplc.clone(), false, true); assert_uplc(src, uplc, false, false); } #[test] fn acceptance_test_22_filter_map() { let src = r#" pub fn foldr(xs: List, f: fn(a, b) -> b, zero: b) -> b { when xs is { [] -> zero [x, ..rest] -> f(x, foldr(rest, f, zero)) } } pub fn filter_map(xs: List, f: fn(a) -> Option) -> List { foldr( xs, fn(x, ys) { when f(x) is { None -> ys Some(y) -> [y, ..ys] } }, [], ) } test filter_map_1() { filter_map([], fn(_) { Some(42) }) == [] } "#; let uplc = Term::equals_data() .apply( Term::list_data().apply( Term::var("filter_map") .lambda("filter_map") .apply( Term::var("foldr") .apply(Term::var("xs")) .apply( Term::equals_integer() .apply(Term::integer(1.into())) .apply(Term::var("subject_index")) .delayed_if_then_else( Term::var("ys"), Term::mk_cons() .apply(Term::i_data().apply(Term::var("y"))) .apply(Term::var("ys")) .lambda("y") .apply( Term::un_i_data().apply( Term::head_list() .apply(Term::var("subject_fields")), ), ) .lambda("subject_fields") .apply( Term::var(CONSTR_FIELDS_EXPOSER) .apply(Term::var("subject")), ), ) .lambda("subject_index") .apply( Term::var(CONSTR_INDEX_EXPOSER).apply(Term::var("subject")), ) .lambda("subject") .apply(Term::var("f").apply(Term::var("x"))) .lambda("ys") .lambda("x"), ) .apply(Term::empty_list()) .lambda("f") .lambda("xs"), ) .lambda("foldr") .apply( Term::var("foldr") .apply(Term::var("foldr")) .apply(Term::var("xs")) .lambda("foldr") .apply( Term::var("xs") .delayed_choose_list( Term::var("zero"), Term::var("f") .apply(Term::var("x")) .apply( Term::var("foldr") .apply(Term::var("foldr")) .apply(Term::var("rest")), ) .lambda("rest") .apply(Term::tail_list().apply(Term::var("xs"))) .lambda("x") .apply(Term::head_list().apply(Term::var("xs"))), ) .lambda("xs") .lambda("foldr"), ) .lambda("zero") .lambda("f") .lambda("xs"), ) .apply(Term::empty_list()) .apply(Term::data(Data::constr(0, vec![Data::integer(42.into())])).lambda("_")), ), ) .apply(Term::list_data().apply(Term::empty_list())) .constr_fields_exposer() .constr_index_exposer(); assert_uplc(src, uplc.clone(), false, true); assert_uplc(src, uplc, false, false); } #[test] fn acceptance_test_23_to_list() { let src = r#" pub type Pairs = List> pub opaque type AssocList { inner: Pairs, } pub fn new() -> AssocList { AssocList { inner: [] } } pub fn to_list(m: AssocList) -> Pairs { m.inner } pub fn insert( in m: AssocList, key k: key, value v: value, ) -> AssocList { AssocList { inner: do_insert(m.inner, k, v) } } fn do_insert(elems: Pairs, k: key, v: value) -> Pairs { when elems is { [] -> [Pair(k, v)] [Pair(k2, v2), ..rest] -> if k == k2 { [Pair(k, v), ..rest] } else { [Pair(k2, v2), ..do_insert(rest, k, v)] } } } fn fixture_1() { new() |> insert("foo", 42) |> insert("bar", 14) } test to_list_2() { to_list(fixture_1()) == [Pair("foo", 42), Pair("bar", 14)] } "#; let do_insert = Term::var("elems") .delayed_choose_list( Term::mk_cons() .apply( Term::mk_pair_data() .apply(Term::b_data().apply(Term::var("k"))) .apply(Term::i_data().apply(Term::var("v"))), ) .apply(Term::empty_map()), Term::head_list() .apply(Term::var("elems")) .as_var("elem_0", |elem_0| { Term::equals_bytestring() .apply(Term::var("k")) .apply(Term::var("k2")) .delayed_if_then_else( Term::mk_cons() .apply( Term::mk_pair_data() .apply(Term::b_data().apply(Term::var("k"))) .apply(Term::i_data().apply(Term::var("v"))), ) .apply(Term::var("rest")), Term::mk_cons() .apply( Term::mk_pair_data() .apply(Term::b_data().apply(Term::var("k2"))) .apply(Term::i_data().apply(Term::var("v2"))), ) .apply( Term::var("do_insert") .apply(Term::var("do_insert")) .apply(Term::var("rest")), ), ) .lambda("rest") .lambda("v2") .lambda("k2") .apply( Term::un_b_data() .apply(Term::fst_pair().apply(Term::Var(elem_0.clone()))), ) .apply( Term::un_i_data() .apply(Term::snd_pair().apply(Term::Var(elem_0.clone()))), ) .apply(Term::tail_list().apply(Term::var("elems"))) }), ) .lambda("elems") .lambda("do_insert"); let insert = do_insert .as_var("do_insert", |do_insert| { Term::Var(do_insert.clone()) .apply(Term::Var(do_insert)) .apply(Term::var("m")) }) .lambda("v") .lambda("k") .lambda("m"); let uplc = Term::equals_data() .apply( Term::map_data().apply( insert .as_var("insert", |insert| { Term::Var(insert.clone()) .apply( Term::Var(insert) .apply(Term::empty_map()) .apply(Term::byte_string("foo".as_bytes().to_vec())) .apply(Term::integer(42.into())), ) .apply(Term::byte_string("bar".as_bytes().to_vec())) .apply(Term::integer(14.into())) .delay() }) .force(), ), ) .apply(Term::map_data().apply(Term::map_values(vec![ Constant::ProtoPair( Type::Data, Type::Data, Constant::Data(Data::bytestring("foo".as_bytes().to_vec())).into(), Constant::Data(Data::integer(42.into())).into(), ), Constant::ProtoPair( Type::Data, Type::Data, Constant::Data(Data::bytestring("bar".as_bytes().to_vec())).into(), Constant::Data(Data::integer(14.into())).into(), ), ]))); assert_uplc(src, uplc.clone(), false, true); assert_uplc(src, uplc, false, false); } #[test] fn acceptance_test_24_map_pair() { let src = r#" pub fn map2( opt_a: Option, opt_b: Option, f: fn(a, b) -> result, ) -> Option { when opt_a is { None -> None Some(a) -> when opt_b is { None -> None Some(b) -> Some(f(a, b)) } } } test map2_3() { map2(Some(14), Some(42), fn(a, b) { Pair(a, b) }) == Some(Pair(14, 42)) } "#; let uplc = Term::equals_data() .apply( Term::var("map2") .lambda("map2") .apply( Term::equals_integer() .apply(Term::integer(1.into())) .apply(Term::var("opt_a_index")) .delayed_if_then_else( Term::Constant(Constant::Data(Data::constr(1, vec![])).into()), Term::equals_integer() .apply(Term::integer(1.into())) .apply(Term::var("opt_b_index")) .delayed_if_then_else( Term::Constant(Constant::Data(Data::constr(1, vec![])).into()), Term::constr_data() .apply(Term::integer(0.into())) .apply( Term::mk_cons() .apply( Term::list_data() .apply( Term::mk_cons() .apply( Term::fst_pair() .apply(Term::var("pair")), ) .apply( Term::mk_cons() .apply( Term::snd_pair().apply( Term::var("pair"), ), ) .apply(Term::empty_list()), ), ) .lambda("pair") .apply( Term::var("f") .apply(Term::var("a")) .apply(Term::var("b")), ), ) .apply(Term::empty_list()), ) .lambda("b") .apply(Term::un_i_data().apply( Term::head_list().apply(Term::var("opt_b_fields")), )) .lambda("opt_b_fields") .apply( Term::var(CONSTR_FIELDS_EXPOSER) .apply(Term::var("opt_b")), ), ) .lambda("opt_b_index") .apply(Term::var(CONSTR_INDEX_EXPOSER).apply(Term::var("opt_b"))) .lambda("a") .apply( Term::un_i_data() .apply(Term::head_list().apply(Term::var("opt_a_fields"))), ) .lambda("opt_a_fields") .apply(Term::var(CONSTR_FIELDS_EXPOSER).apply(Term::var("opt_a"))), ) .lambda("opt_a_index") .apply(Term::var(CONSTR_INDEX_EXPOSER).apply(Term::var("opt_a"))) .lambda("f") .lambda("opt_b") .lambda("opt_a"), ) .apply(Term::Constant( Constant::Data(Data::constr(0, vec![Data::integer(14.into())])).into(), )) .apply(Term::Constant( Constant::Data(Data::constr(0, vec![Data::integer(42.into())])).into(), )) .apply( Term::mk_pair_data() .apply(Term::i_data().apply(Term::var("a"))) .apply(Term::i_data().apply(Term::var("b"))) .lambda("b") .lambda("a"), ), ) .apply(Term::Constant( Constant::Data(Data::constr( 0, vec![Data::list(vec![ Data::integer(14.into()), Data::integer(42.into()), ])], )) .into(), )) .constr_fields_exposer() .constr_index_exposer(); assert_uplc(src, uplc.clone(), false, true); assert_uplc(src, uplc, false, false); } #[test] fn acceptance_test_24_map2() { let src = r#" pub fn map2( opt_a: Option, opt_b: Option, f: fn(a, b) -> result, ) -> Option { when opt_a is { None -> None Some(a) -> when opt_b is { None -> None Some(b) -> Some(f(a, b)) } } } test map2_3() { map2(Some(14), Some(42), fn(a, b) { (a, b) }) == Some((14, 42)) } "#; let uplc = Term::equals_data() .apply( Term::var("map2") .lambda("map2") .apply( Term::equals_integer() .apply(Term::integer(1.into())) .apply(Term::var("opt_a_index")) .delayed_if_then_else( Term::Constant(Constant::Data(Data::constr(1, vec![])).into()), Term::equals_integer() .apply(Term::integer(1.into())) .apply(Term::var("opt_b_index")) .delayed_if_then_else( Term::Constant(Constant::Data(Data::constr(1, vec![])).into()), Term::constr_data() .apply(Term::integer(0.into())) .apply( Term::mk_cons() .apply( Term::list_data().apply( Term::var("f") .apply(Term::var("a")) .apply(Term::var("b")), ), ) .apply(Term::empty_list()), ) .lambda("b") .apply(Term::un_i_data().apply( Term::head_list().apply(Term::var("opt_b_fields")), )) .lambda("opt_b_fields") .apply( Term::var(CONSTR_FIELDS_EXPOSER) .apply(Term::var("opt_b")), ), ) .lambda("opt_b_index") .apply(Term::var(CONSTR_INDEX_EXPOSER).apply(Term::var("opt_b"))) .lambda("a") .apply( Term::un_i_data() .apply(Term::head_list().apply(Term::var("opt_a_fields"))), ) .lambda("opt_a_fields") .apply(Term::var(CONSTR_FIELDS_EXPOSER).apply(Term::var("opt_a"))), ) .lambda("opt_a_index") .apply(Term::var(CONSTR_INDEX_EXPOSER).apply(Term::var("opt_a"))) .lambda("f") .lambda("opt_b") .lambda("opt_a"), ) .apply(Term::Constant( Constant::Data(Data::constr(0, vec![Data::integer(14.into())])).into(), )) .apply(Term::Constant( Constant::Data(Data::constr(0, vec![Data::integer(42.into())])).into(), )) .apply( Term::mk_cons() .apply(Term::i_data().apply(Term::var("a"))) .apply( Term::mk_cons() .apply(Term::i_data().apply(Term::var("b"))) .apply(Term::empty_list()), ) .lambda("b") .lambda("a"), ), ) .apply(Term::Constant( Constant::Data(Data::constr( 0, vec![Data::list(vec![ Data::integer(14.into()), Data::integer(42.into()), ])], )) .into(), )) .constr_fields_exposer() .constr_index_exposer(); assert_uplc(src, uplc.clone(), false, true); assert_uplc(src, uplc, false, false); } #[test] fn acceptance_test_25_void_equal() { let src = r#" test nil_1() { Void == Void } "#; let uplc = Term::unit().choose_unit(Term::unit().choose_unit(Term::bool(true))); assert_uplc(src, uplc.clone(), false, true); assert_uplc(src, uplc, false, false); } #[test] fn acceptance_test_26_foldr() { let src = r#" pub fn foldr(xs: List, f: fn(a, b) -> b, zero: b) -> b { when xs is { [] -> zero [x, ..rest] -> f(x, foldr(rest, f, zero)) } } pub fn concat(left: List, right: List) -> List { foldr(left, fn(x, xs) { [x, ..xs] }, right) } pub fn flat_map(xs: List, f: fn(a) -> List) -> List { when xs is { [] -> [] [x, ..rest] -> concat(f(x), flat_map(rest, f)) } } test flat_map_2() { flat_map([1, 2, 3], fn(a) { [a, a] }) == [1, 1, 2, 2, 3, 3] } "#; let uplc = Term::equals_data() .apply( Term::list_data().apply( Term::var("flat_map") .lambda("flat_map") .apply( Term::var("flat_map") .apply(Term::var("flat_map")) .apply(Term::var("xs")) .lambda("flat_map") .apply( Term::var("xs") .delayed_choose_list( Term::empty_list(), Term::var("concat") .apply(Term::var("f").apply(Term::var("x"))) .apply( Term::var("flat_map") .apply(Term::var("flat_map")) .apply(Term::var("rest")), ) .lambda("rest") .apply(Term::tail_list().apply(Term::var("xs"))) .lambda("x") .apply(Term::un_i_data().apply( Term::head_list().apply(Term::var("xs")), )), ) .lambda("xs") .lambda("flat_map"), ) .lambda("f") .lambda("xs"), ) .lambda("concat") .apply( Term::var("foldr") .apply(Term::var("left")) .apply( Term::mk_cons() .apply(Term::i_data().apply(Term::var("x"))) .apply(Term::var("xs")) .lambda("xs") .lambda("x"), ) .apply(Term::var("right")) .lambda("right") .lambda("left"), ) .lambda("foldr") .apply( Term::var("foldr") .apply(Term::var("foldr")) .apply(Term::var("xs")) .lambda("foldr") .apply( Term::var("xs") .delayed_choose_list( Term::var("zero"), Term::var("f") .apply(Term::var("x")) .apply( Term::var("foldr") .apply(Term::var("foldr")) .apply(Term::var("rest")), ) .lambda("rest") .apply(Term::tail_list().apply(Term::var("xs"))) .lambda("x") .apply(Term::un_i_data().apply( Term::head_list().apply(Term::var("xs")), )), ) .lambda("xs") .lambda("foldr"), ) .lambda("zero") .lambda("f") .lambda("xs"), ) .apply(Term::list_values(vec![ Constant::Data(Data::integer(1.into())), Constant::Data(Data::integer(2.into())), Constant::Data(Data::integer(3.into())), ])) .apply( Term::mk_cons() .apply(Term::i_data().apply(Term::var("a"))) .apply( Term::mk_cons() .apply(Term::i_data().apply(Term::var("a"))) .apply(Term::empty_list()), ) .lambda("a"), ), ), ) .apply(Term::list_data().apply(Term::list_values(vec![ Constant::Data(Data::integer(1.into())), Constant::Data(Data::integer(1.into())), Constant::Data(Data::integer(2.into())), Constant::Data(Data::integer(2.into())), Constant::Data(Data::integer(3.into())), Constant::Data(Data::integer(3.into())), ]))); assert_uplc(src, uplc.clone(), false, true); assert_uplc(src, uplc, false, false); } #[test] fn acceptance_test_27_flat_map() { let src = r#" pub fn foldr(xs: List, f: fn(a, b) -> b, zero: b) -> b { when xs is { [] -> zero [x, ..rest] -> f(x, foldr(rest, f, zero)) } } pub fn concat(left: List, right: List) -> List { foldr(left, fn(x, xs) { [x, ..xs] }, right) } pub fn flat_map(xs: List, f: fn(a) -> List) -> List { when xs is { [] -> [] [x, ..rest] -> concat(f(x), flat_map(rest, f)) } } test flat_map_2() { flat_map([1, 2, 3], fn(a) { [a, a] }) == [1, 1, 2, 2, 3, 3] } "#; let uplc = Term::equals_data() .apply( Term::list_data().apply( Term::var("flat_map") .lambda("flat_map") .apply( Term::var("flat_map") .apply(Term::var("flat_map")) .apply(Term::var("xs")) .lambda("flat_map") .apply( Term::var("xs") .delayed_choose_list( Term::empty_list(), Term::var("concat") .apply(Term::var("f").apply(Term::var("x"))) .apply( Term::var("flat_map") .apply(Term::var("flat_map")) .apply(Term::var("rest")), ) .lambda("rest") .apply(Term::tail_list().apply(Term::var("xs"))) .lambda("x") .apply(Term::un_i_data().apply( Term::head_list().apply(Term::var("xs")), )), ) .lambda("xs") .lambda("flat_map"), ) .lambda("f") .lambda("xs"), ) .lambda("concat") .apply( Term::var("foldr") .apply(Term::var("left")) .apply( Term::mk_cons() .apply(Term::i_data().apply(Term::var("x"))) .apply(Term::var("xs")) .lambda("xs") .lambda("x"), ) .apply(Term::var("right")) .lambda("right") .lambda("left"), ) .lambda("foldr") .apply( Term::var("foldr") .apply(Term::var("foldr")) .apply(Term::var("xs")) .lambda("foldr") .apply( Term::var("xs") .delayed_choose_list( Term::var("zero"), Term::var("f") .apply(Term::var("x")) .apply( Term::var("foldr") .apply(Term::var("foldr")) .apply(Term::var("rest")), ) .lambda("rest") .apply(Term::tail_list().apply(Term::var("xs"))) .lambda("x") .apply(Term::un_i_data().apply( Term::head_list().apply(Term::var("xs")), )), ) .lambda("xs") .lambda("foldr"), ) .lambda("zero") .lambda("f") .lambda("xs"), ) .apply(Term::list_values(vec![ Constant::Data(Data::integer(1.into())), Constant::Data(Data::integer(2.into())), Constant::Data(Data::integer(3.into())), ])) .apply( Term::mk_cons() .apply(Term::i_data().apply(Term::var("a"))) .apply( Term::mk_cons() .apply(Term::i_data().apply(Term::var("a"))) .apply(Term::empty_list()), ) .lambda("a"), ), ), ) .apply(Term::list_data().apply(Term::list_values(vec![ Constant::Data(Data::integer(1.into())), Constant::Data(Data::integer(1.into())), Constant::Data(Data::integer(2.into())), Constant::Data(Data::integer(2.into())), Constant::Data(Data::integer(3.into())), Constant::Data(Data::integer(3.into())), ]))); assert_uplc(src, uplc.clone(), false, true); assert_uplc(src, uplc, false, false); } #[test] fn acceptance_test_28_unique_empty_list() { let src = r#" pub fn filter(xs: List, f: fn(a) -> Bool) -> List { when xs is { [] -> [] [x, ..rest] -> if f(x) { [x, ..filter(rest, f)] } else { filter(rest, f) } } } pub fn unique(xs: List) -> List { when xs is { [] -> [] [x, ..rest] -> [x, ..unique(filter(rest, fn(y) { y != x }))] } } test unique_1() { unique([]) == [] } "#; let uplc = Term::equals_data() .apply( Term::list_data().apply( Term::var("unique") .lambda("unique") .apply(Term::var("unique").apply(Term::var("unique"))) .lambda("unique") .apply( Term::var("xs") .delayed_choose_list( Term::empty_list(), Term::mk_cons() .apply(Term::var("x")) .apply( Term::var("unique").apply(Term::var("unique")).apply( Term::var("filter").apply(Term::var("rest")).apply( Term::equals_data() .apply(Term::var("y")) .apply(Term::var("x")) .if_then_else( Term::bool(false), Term::bool(true), ) .lambda("y"), ), ), ) .lambda("rest") .lambda("x") .apply(Term::head_list().apply(Term::var("xs"))) .apply(Term::tail_list().apply(Term::var("xs"))), ) .lambda("xs") .lambda("unique"), ) .lambda("filter") .apply( Term::var("filter") .apply(Term::var("filter")) .apply(Term::var("xs")) .lambda("filter") .apply( Term::var("xs") .delayed_choose_list( Term::empty_list(), Term::var("f") .apply(Term::var("x")) .delayed_if_then_else( Term::mk_cons().apply(Term::var("x")).apply( Term::var("filter") .apply(Term::var("filter")) .apply(Term::var("rest")), ), Term::var("filter") .apply(Term::var("filter")) .apply(Term::var("rest")), ) .lambda("rest") .lambda("x") .apply(Term::head_list().apply(Term::var("xs"))) .apply(Term::tail_list().apply(Term::var("xs"))), ) .lambda("xs") .lambda("filter"), ) .lambda("f") .lambda("xs"), ) .apply(Term::empty_list()), ), ) .apply(Term::data(Data::list(vec![]))); assert_uplc(src, uplc.clone(), false, true); assert_uplc(src, uplc, false, false); } #[test] fn acceptance_test_28_unique_list() { let src = r#" pub fn filter(xs: List, f: fn(a) -> Bool) -> List { when xs is { [] -> [] [x, ..rest] -> if f(x) { [x, ..filter(rest, f)] } else { filter(rest, f) } } } pub fn unique(xs: List) -> List { when xs is { [] -> [] [x, ..rest] -> [x, ..unique(filter(rest, fn(y) { y != x }))] } } test unique_1() { unique([1,2,3,1]) == [1,2,3] } "#; let uplc = Term::equals_data() .apply( Term::list_data().apply( Term::var("unique") .lambda("unique") .apply(Term::var("unique").apply(Term::var("unique"))) .lambda("unique") .apply( Term::var("xs") .delayed_choose_list( Term::empty_list(), Term::mk_cons() .apply(Term::i_data().apply(Term::var("x"))) .apply( Term::var("unique").apply(Term::var("unique")).apply( Term::var("filter").apply(Term::var("rest")).apply( Term::equals_integer() .apply(Term::var("y")) .apply(Term::var("x")) .if_then_else( Term::bool(false), Term::bool(true), ) .lambda("y"), ), ), ) .lambda("rest") .lambda("x") .apply( Term::un_i_data() .apply(Term::head_list().apply(Term::var("xs"))), ) .apply(Term::tail_list().apply(Term::var("xs"))), ) .lambda("xs") .lambda("unique"), ) .lambda("filter") .apply( Term::var("filter") .apply(Term::var("filter")) .apply(Term::var("xs")) .lambda("filter") .apply( Term::var("xs") .delayed_choose_list( Term::empty_list(), Term::var("f") .apply(Term::var("x")) .delayed_if_then_else( Term::mk_cons() .apply(Term::i_data().apply(Term::var("x"))) .apply( Term::var("filter") .apply(Term::var("filter")) .apply(Term::var("rest")), ), Term::var("filter") .apply(Term::var("filter")) .apply(Term::var("rest")), ) .lambda("rest") .lambda("x") .apply(Term::un_i_data().apply( Term::head_list().apply(Term::var("xs")), )) .apply(Term::tail_list().apply(Term::var("xs"))), ) .lambda("xs") .lambda("filter"), ) .lambda("f") .lambda("xs"), ) .apply(Term::list_values(vec![ Constant::Data(Data::integer(1.into())), Constant::Data(Data::integer(2.into())), Constant::Data(Data::integer(3.into())), Constant::Data(Data::integer(1.into())), ])), ), ) .apply(Term::data(Data::list(vec![ Data::integer(1.into()), Data::integer(2.into()), Data::integer(3.into()), ]))); assert_uplc(src, uplc.clone(), false, true); assert_uplc(src, uplc, false, false); } #[test] fn acceptance_test_29_union_pair() { let src = r#" pub opaque type AssocList { inner: Pairs, } const empty_list: AssocList = AssocList { inner: [] } pub fn from_list(xs: Pairs) -> AssocList { AssocList { inner: do_from_list(xs) } } fn do_from_list(xs: Pairs) -> Pairs { when xs is { [] -> [] [Pair(k, v), ..rest] -> do_insert(do_from_list(rest), k, v) } } pub fn insert( in m: AssocList, key k: key, value v: value, ) -> AssocList { AssocList { inner: do_insert(m.inner, k, v) } } fn do_insert(elems: Pairs, k: key, v: value) -> Pairs { when elems is { [] -> [Pair(k, v)] [Pair(k2, v2), ..rest] -> if k == k2 { [Pair(k, v), ..rest] } else { [Pair(k2, v2), ..do_insert(rest, k, v)] } } } pub fn union( left: AssocList, right: AssocList, ) -> AssocList { AssocList { inner: do_union(left.inner, right.inner) } } fn do_union( left: Pairs, right: Pairs, ) -> Pairs { when left is { [] -> right [Pair(k, v), ..rest] -> do_union(rest, do_insert(right, k, v)) } } const fixture_1 = { empty_list |> insert("foo", 42) |> insert("bar", 14) } test union_1() { union(fixture_1, empty_list) == fixture_1 } "#; let uplc = Term::equals_data() .apply( Term::map_data().apply( Term::var("union") .lambda("union") .apply( Term::var("do_union") .apply(Term::var("left")) .apply(Term::var("right")) .lambda("right") .lambda("left"), ) .lambda("do_union") .apply(Term::var("do_union").apply(Term::var("do_union"))) .lambda("do_union") .apply( Term::var("left") .delayed_choose_list( Term::var("right"), Term::var("do_union") .apply(Term::var("do_union")) .apply(Term::var("rest")) .apply( Term::var("do_insert") .apply(Term::var("right")) .apply(Term::var("k")) .apply(Term::var("v")), ) .lambda("rest") .lambda("v") .lambda("k") .apply( Term::un_b_data() .apply(Term::fst_pair().apply(Term::var("tuple"))), ) .apply( Term::un_i_data() .apply(Term::snd_pair().apply(Term::var("tuple"))), ) .apply(Term::tail_list().apply(Term::var("left"))) .lambda("tuple") .apply(Term::head_list().apply(Term::var("left"))), ) .lambda("right") .lambda("left") .lambda("do_union"), ) .lambda("do_insert") .apply( Term::var("do_insert") .apply(Term::var("do_insert")) .apply(Term::var("elems")) .lambda("do_insert") .apply( Term::var("elems") .delayed_choose_list( Term::mk_cons() .apply( Term::mk_pair_data() .apply(Term::b_data().apply(Term::var("k"))) .apply( Term::i_data().apply(Term::var("v")), ), ) .apply(Term::empty_map()), Term::equals_bytestring() .apply(Term::var("k")) .apply(Term::var("k2")) .delayed_if_then_else( Term::mk_cons() .apply( Term::mk_pair_data() .apply( Term::b_data() .apply(Term::var("k")), ) .apply( Term::i_data() .apply(Term::var("v")), ), ) .apply(Term::var("rest")), Term::mk_cons() .apply( Term::mk_pair_data() .apply( Term::b_data() .apply(Term::var("k2")), ) .apply( Term::i_data() .apply(Term::var("v2")), ), ) .apply( Term::var("do_insert") .apply(Term::var("do_insert")) .apply(Term::var("rest")), ), ) .lambda("rest") .lambda("v2") .lambda("k2") .apply(Term::un_b_data().apply( Term::fst_pair().apply(Term::var("tuple")), )) .apply(Term::un_i_data().apply( Term::snd_pair().apply(Term::var("tuple")), )) .apply(Term::tail_list().apply(Term::var("elems"))) .lambda("tuple") .apply(Term::head_list().apply(Term::var("elems"))), ) .lambda("elems") .lambda("do_insert"), ) .lambda("v") .lambda("k") .lambda("elems"), ) .apply(Term::map_values(vec![ Constant::ProtoPair( Type::Data, Type::Data, Constant::Data(Data::bytestring(vec![0x66, 0x6f, 0x6f])).into(), Constant::Data(Data::integer(42.into())).into(), ), Constant::ProtoPair( Type::Data, Type::Data, Constant::Data(Data::bytestring(vec![0x62, 0x61, 0x72])).into(), Constant::Data(Data::integer(14.into())).into(), ), ])) .apply(Term::empty_map()), ), ) .apply(Term::data(Data::map(vec![ ( Data::bytestring(vec![0x66, 0x6f, 0x6f]), Data::integer(42.into()), ), ( Data::bytestring(vec![0x62, 0x61, 0x72]), Data::integer(14.into()), ), ]))); assert_uplc(src, uplc.clone(), false, true); assert_uplc(src, uplc, false, false); } #[test] fn acceptance_test_29_union_tuple() { let src = r#" pub opaque type AssocList { inner: List<(key, value)>, } const empty_list = AssocList { inner: [] } pub fn from_list(xs: List<(key, value)>) -> AssocList { AssocList { inner: do_from_list(xs) } } fn do_from_list(xs: List<(key, value)>) -> List<(key, value)> { when xs is { [] -> [] [(k, v), ..rest] -> do_insert(do_from_list(rest), k, v) } } pub fn insert( in m: AssocList, key k: key, value v: value, ) -> AssocList { AssocList { inner: do_insert(m.inner, k, v) } } fn do_insert(elems: List<(key, value)>, k: key, v: value) -> List<(key, value)> { when elems is { [] -> [(k, v)] [(k2, v2), ..rest] -> if k == k2 { [(k, v), ..rest] } else { [(k2, v2), ..do_insert(rest, k, v)] } } } pub fn union( left: AssocList, right: AssocList, ) -> AssocList { AssocList { inner: do_union(left.inner, right.inner) } } fn do_union( left: List<(key, value)>, right: List<(key, value)>, ) -> List<(key, value)> { when left is { [] -> right [(k, v), ..rest] -> do_union(rest, do_insert(right, k, v)) } } const fixture_1 = { empty_list |> insert("foo", 42) |> insert("bar", 14) } test union_1() { union(fixture_1, empty_list) == fixture_1 } "#; let uplc = Term::equals_data() .apply( Term::list_data().apply( Term::var("union") .lambda("union") .apply( Term::var("do_union") .apply(Term::var("left")) .apply(Term::var("right")) .lambda("right") .lambda("left"), ) .lambda("do_union") .apply(Term::var("do_union").apply(Term::var("do_union"))) .lambda("do_union") .apply( Term::var("left") .delayed_choose_list( Term::var("right"), Term::var("do_union") .apply(Term::var("do_union")) .apply(Term::var("rest")) .apply( Term::var("do_insert") .apply(Term::var("right")) .apply(Term::var("k")) .apply(Term::var("v")), ) .lambda("rest") .lambda("v") .lambda("k") .apply( Term::un_b_data() .apply(Term::head_list().apply(Term::var("tuple"))), ) .apply( Term::un_i_data() .apply(Term::head_list().apply( Term::tail_list().apply(Term::var("tuple")), )), ) .apply(Term::tail_list().apply(Term::var("left"))) .lambda("tuple") .apply( Term::unlist_data() .apply(Term::head_list().apply(Term::var("left"))), ), ) .lambda("right") .lambda("left") .lambda("do_union"), ) .lambda("do_insert") .apply( Term::var("do_insert") .apply(Term::var("do_insert")) .apply(Term::var("elems")) .lambda("do_insert") .apply( Term::var("elems") .delayed_choose_list( Term::mk_cons() .apply( Term::list_data().apply( Term::mk_cons() .apply(Term::b_data().apply(Term::var("k"))) .apply( Term::mk_cons() .apply( Term::i_data() .apply(Term::var("v")), ) .apply(Term::empty_list()), ), ), ) .apply(Term::empty_list()), Term::equals_bytestring() .apply(Term::var("k")) .apply(Term::var("k2")) .delayed_if_then_else( Term::mk_cons() .apply( Term::list_data().apply( Term::mk_cons() .apply( Term::b_data() .apply(Term::var("k")), ) .apply( Term::mk_cons() .apply( Term::i_data().apply( Term::var("v"), ), ) .apply(Term::empty_list()), ), ), ) .apply(Term::var("rest")), Term::mk_cons() .apply( Term::list_data().apply( Term::mk_cons() .apply( Term::b_data() .apply(Term::var("k2")), ) .apply( Term::mk_cons() .apply( Term::i_data().apply( Term::var("v2"), ), ) .apply(Term::empty_list()), ), ), ) .apply( Term::var("do_insert") .apply(Term::var("do_insert")) .apply(Term::var("rest")), ), ) .lambda("rest") .lambda("v2") .lambda("k2") .apply( Term::un_b_data().apply( Term::head_list().apply(Term::var("tuple")), ), ) .apply(Term::un_i_data().apply( Term::head_list().apply( Term::tail_list().apply(Term::var("tuple")), ), )) .apply(Term::tail_list().apply(Term::var("elems"))) .lambda("tuple") .apply(Term::unlist_data().apply( Term::head_list().apply(Term::var("elems")), )), ) .lambda("elems") .lambda("do_insert"), ) .lambda("v") .lambda("k") .lambda("elems"), ) .apply(Term::list_values(vec![ Constant::Data(Data::list(vec![ Data::bytestring("foo".as_bytes().to_vec()), Data::integer(42.into()), ])), Constant::Data(Data::list(vec![ Data::bytestring("bar".as_bytes().to_vec()), Data::integer(14.into()), ])), ])) .apply(Term::empty_list()), ), ) .apply(Term::data(Data::list(vec![ Data::list(vec![ Data::bytestring("foo".as_bytes().to_vec()), Data::integer(42.into()), ]), Data::list(vec![ Data::bytestring("bar".as_bytes().to_vec()), Data::integer(14.into()), ]), ]))); assert_uplc(src, uplc.clone(), false, true); assert_uplc(src, uplc, false, false); } #[test] fn acceptance_test_30_abs() { let src = r#" pub fn abs(a: Int) -> Int { if a < 0 { -a } else { a } } test abs_1() { abs(-14) == 14 } "#; let uplc = Term::equals_integer() .apply( Term::var("abs") .lambda("abs") .apply( Term::less_than_integer() .apply(Term::var("a")) .apply(Term::integer(0.into())) .delayed_if_then_else( Term::subtract_integer() .apply(Term::integer(0.into())) .apply(Term::var("a")), Term::var("a"), ) .lambda("a"), ) .apply(Term::integer((-14).into())), ) .apply(Term::integer(14.into())); assert_uplc(src, uplc.clone(), false, true); assert_uplc(src, uplc, false, false); } #[test] fn expect_empty_list_on_filled_list() { let src = r#" test empty_list1() { let x = [1,2] expect [] = x True } "#; assert_uplc( src, Term::var("x") .delayed_choose_list( Term::bool(true), Term::Error.delayed_trace(Term::string("expect [] = x")), ) .lambda("x") .apply(Term::list_values(vec![ Constant::Data(Data::integer(1.into())), Constant::Data(Data::integer(2.into())), ])), true, true, ); } #[test] fn expect_empty_list_on_new_list() { let src = r#" test empty_list1() { let x = [] expect [] = x True } "#; assert_uplc( src, Term::var("x") .delayed_choose_list( Term::bool(true), Term::Error.delayed_trace(Term::string("expect [] = x")), ) .lambda("x") .apply(Term::list_values(vec![])), false, true, ); } #[test] fn when_bool_is_true() { let src = r#" test it() { when True is { True -> True False -> fail } } "#; assert_uplc( src, Term::var("subject") .delayed_if_then_else(Term::bool(true), Term::Error.delay().force()) .lambda("subject") .apply(Term::bool(true)), false, true, ); } #[test] fn when_bool_is_true_switched_cases() { let src = r#" test it() { when True is { False -> fail True -> True } } "#; assert_uplc( src, Term::var("subject") .delayed_if_then_else(Term::bool(true), Term::Error.delay().force()) .lambda("subject") .apply(Term::bool(true)), false, true, ); } #[test] fn when_bool_is_false() { let src = r#" test it() { when False is { False -> fail True -> True } } "#; assert_uplc( src, Term::var("subject") .delayed_if_then_else(Term::bool(true), Term::Error.delay().force()) .lambda("subject") .apply(Term::bool(false)), true, true, ); } #[test] fn always_true_validator() { let src = r#" validator foo { mint(_redeemer: Data, _policy_id: ByteArray, _transaction: Data) { True } } "#; let mint = |purpose: Rc| { Term::bool(true).lambda("__purpose_arg__").apply( Term::un_b_data() .apply(Term::head_list().apply( Term::snd_pair().apply(Term::unconstr_data().apply(Term::Var(purpose))), )), ) }; let when = |purpose: Rc| { Term::equals_integer() .apply(Term::integer(0.into())) .apply(Term::fst_pair().apply(Term::unconstr_data().apply(Term::Var(purpose)))) }; let validator = { let context = "__context__"; Term::snd_pair() .apply(Term::unconstr_data().apply(Term::var(context))) .as_var("tail_id_2", |tail_id_2| { Term::tail_list() .apply(Term::Var(tail_id_2.clone())) .as_var("tail_id_3", |tail_id_3| { Term::head_list() .apply(Term::Var(tail_id_2.clone())) .as_var("__transaction__", |_transaction| { Term::head_list() .apply(Term::Var(tail_id_3.clone())) .as_var("__redeemer__", |_redeemer| { Term::head_list() .apply(Term::tail_list().apply(Term::Var(tail_id_3))) .as_var("__purpose__", |purpose| { when(purpose.clone()).delayed_if_then_else( mint(purpose), Term::Error, ) }) }) }) }) }) .delayed_if_then_else( Term::unit(), Term::Error .apply(Term::Error.force()) .delayed_trace(Term::string("Validator returned false")), ) .lambda(context) }; assert_uplc(src, validator, false, true); } #[test] fn when_tuple_deconstruction() { let src = r#" pub type Thing { idx: Int, } pub type Datum { A(Thing) B } pub type RedSpend { Spend(Int) Buy } test foo() { when (A(Thing { idx: 42 }), Buy) is { (A(a), Spend(x)) -> (a.idx == x)? (_, _) -> True } } "#; let clause = |tuple_index_0: Rc, subject: Term, otherwise: Rc| { let match_a = Term::equals_integer().apply(Term::integer(0.into())).apply( Term::fst_pair().apply(Term::unconstr_data().apply(Term::Var(tuple_index_0.clone()))), ); let extract_a = Term::head_list() .apply(Term::snd_pair().apply(Term::unconstr_data().apply(Term::Var(tuple_index_0)))); match_a.delayed_if_then_else( { let tuple_index_1 = Term::head_list().apply(Term::tail_list().apply(subject)); let match_spend = Term::equals_integer().apply(Term::integer(0.into())).apply( Term::fst_pair().apply(Term::unconstr_data().apply(Term::var( "__subject_var_span_208_235_id_0_tail_extractfield", ))), ); match_spend .delayed_if_then_else( Term::equals_integer() .apply(Term::un_i_data().apply(Term::head_list().apply( Term::snd_pair().apply(Term::unconstr_data().apply(extract_a)), ))) .apply(Term::un_i_data().apply(Term::head_list().apply( Term::snd_pair().apply(Term::unconstr_data().apply(Term::var( "__subject_var_span_208_235_id_0_tail_extractfield", ))), ))) .delayed_if_then_else( Term::bool(true), Term::bool(false).delayed_trace(Term::string("a.idx == x ? False")), ), Term::Var(otherwise.clone()).force(), ) .lambda("__subject_var_span_208_235_id_0_tail_extractfield") .apply(tuple_index_1) }, Term::Var(otherwise.clone()).force(), ) }; let snd_clause = Term::bool(true); let subject = || { Term::list_values(vec![ Constant::Data(Data::constr( 0, vec![Data::constr(0, vec![Data::integer(42.into())])], )), Constant::Data(Data::constr(1, vec![])), ]) }; let test = snd_clause.delay().as_var("clause_then_1", |other_clause| { Term::head_list().apply(subject()).as_var( "__subject_var_span_208_235_id_0_extractfield", |tuple_index_0| clause(tuple_index_0, subject(), other_clause), ) }); assert_uplc(src, test, false, true); } #[test] fn when_tuple_empty_lists() { let src = r#" test hi() { let bucket1 = [1, 2, 3] let bucket2 = [1, 5, 6] let bye = when (bucket1, bucket2) is { ([], _) -> False (_, []) -> False ([a, ..], [b, ..]) -> a == b } bye } "#; assert_uplc( src, Term::var("bucket_tuple_fst") .delay_empty_choose_list(Term::bool(false), Term::var("delayed_clause")) .lambda("delayed_clause") .apply( Term::var("bucket_tuple_snd") .delay_empty_choose_list(Term::bool(false), Term::var("delayed_clause")) .lambda("delayed_clause") .apply( Term::equals_integer() .apply(Term::var("a")) .apply(Term::var("b")) .lambda("b") .apply( Term::un_i_data() .apply(Term::head_list().apply(Term::var("bucket_tuple_snd"))), ) .lambda("a") .apply( Term::un_i_data() .apply(Term::head_list().apply(Term::var("bucket_tuple_fst"))), ) .delay(), ) .lambda("bucket_tuple_snd") .apply(Term::unlist_data().apply( Term::head_list().apply(Term::tail_list().apply(Term::var("bucket_tuple"))), )) .delay(), ) .lambda("bucket_tuple_fst") .apply(Term::unlist_data().apply(Term::head_list().apply(Term::var("bucket_tuple")))) .lambda("bucket_tuple") .apply( Term::mk_cons() .apply(Term::list_data().apply(Term::var("bucket1"))) .apply( Term::mk_cons() .apply(Term::list_data().apply(Term::var("bucket2"))) .apply(Term::empty_list()), ), ) .lambda("bucket2") .apply(Term::list_values(vec![ Constant::Data(Data::integer(1.into())), Constant::Data(Data::integer(5.into())), Constant::Data(Data::integer(6.into())), ])) .lambda("bucket1") .apply(Term::list_values(vec![ Constant::Data(Data::integer(1.into())), Constant::Data(Data::integer(2.into())), Constant::Data(Data::integer(3.into())), ])), false, true, ); } #[test] fn generic_validator_type_test() { let src = r#" pub type A { NoA SomeA(Void, x) } pub type B { something: Void, } validator err_example { spend(_datum: Option, r: A, _output_ref: Data, _transaction: Data) -> Bool { when r is { NoA -> False SomeA(_, B(something)) -> something == Void } } } "#; let body = |redeemer: Rc| { Term::equals_integer() .apply(Term::integer(0.into())) .apply(Term::var("subject")) .delayed_if_then_else( Term::bool(false), Term::choose_unit( Term::var("something"), Term::choose_unit(Term::unit(), Term::bool(true)), ) .lambda("something") .apply( Term::unit() .lambda("_") .apply(Term::head_list().apply(Term::var("B_fields"))), ) .lambda("B_fields") .apply(Term::var(CONSTR_FIELDS_EXPOSER).apply(Term::var("field_B"))) .lambda("field_B") .apply(Term::head_list().apply(Term::var("tail_1"))) .lambda("tail_1") .apply(Term::tail_list().apply(Term::var("r_fields"))) .lambda("r_fields") .apply(Term::var(CONSTR_FIELDS_EXPOSER).apply(Term::Var(redeemer.clone()))), ) .lambda("subject") .apply(Term::var(CONSTR_INDEX_EXPOSER).apply(Term::Var(redeemer))) }; let expect_void = |target: Rc, then: Term| { Term::choose_data_constr( target.clone(), |_| { Term::equals_integer() .apply(Term::integer(0.into())) .apply( Term::fst_pair() .apply(Term::unconstr_data().apply(Term::Var(target.clone()))), ) .delay_true_if_then_else( Term::snd_pair() .apply(Term::unconstr_data().apply(Term::Var(target))) .delay_empty_choose_list(then, Term::var("r:A")), Term::var("r:A"), ) }, &Term::var("r:A"), ) }; let expect_void_no_trace = |target: Term| { Term::equals_integer() .apply(Term::integer(0.into())) .apply(Term::fst_pair().apply(Term::unconstr_data().apply(target.clone()))) .delayed_if_then_else( Term::snd_pair() .apply(Term::unconstr_data().apply(target)) .delayed_choose_list(Term::unit(), Term::Error), Term::Error, ) }; let expect_no_a = |redeemer: Rc, then_delayed: Rc, trace: bool| { Term::snd_pair() .apply(Term::unconstr_data().apply(Term::Var(redeemer))) .delay_empty_choose_list( Term::Var(then_delayed.clone()).force(), if trace { Term::var("r:A") } else { Term::Error.delay() }, ) }; let expect_b = |target: Rc, then: Term, trace: bool| { let error_trace = if trace { Term::var("r:A") } else { Term::Error.delay() }; Term::equals_integer() .apply(Term::integer(0.into())) .apply(Term::fst_pair().apply(Term::unconstr_data().apply(Term::Var(target.clone())))) .delay_true_if_then_else( Term::snd_pair() .apply(Term::unconstr_data().apply(Term::Var(target))) .as_var("tail_id_8", |tail_id_8| { if trace { Term::Var(tail_id_8.clone()).delay_filled_choose_list( error_trace.clone(), Term::head_list() .apply(Term::Var(tail_id_8.clone())) .as_var("__val", |val| { expect_void( val, Term::tail_list() .apply(Term::Var(tail_id_8)) .delay_empty_choose_list( then.force(), error_trace.clone(), ), ) }), ) } else { Term::head_list() .apply(Term::Var(tail_id_8.clone())) .as_var("__val", |val| expect_void_no_trace(Term::Var(val))) .as_var("something", |_| { Term::tail_list() .apply(Term::Var(tail_id_8)) .delayed_choose_list(then.force(), Term::Error) }) } }), error_trace, ) }; let expect_some_a = |redeemer: Rc, then_delayed: Rc, trace: bool| { let trace_error = if trace { Term::var("r:A") } else { Term::Error.delay() }; Term::snd_pair() .apply(Term::unconstr_data().apply(Term::Var(redeemer))) .as_var("tail_id_5", |tail_id_5| { let inner = if trace { Term::head_list().apply(Term::Var(tail_id_5.clone())) } else { Term::head_list() .apply(Term::Var(tail_id_5.clone())) .as_var("val", |val| expect_void_no_trace(Term::Var(val))) } .as_var("__val", |val| { if trace { expect_void( val, Term::tail_list() .apply(Term::Var(tail_id_5.clone())) .as_var("tail_id_6", |tail_id_6| { Term::Var(tail_id_6.clone()).delay_filled_choose_list( trace_error.clone(), Term::head_list() .apply(Term::Var(tail_id_6.clone())) .as_var("__val", |val| { Term::choose_data_constr( val.clone(), |_| { Term::tail_list() .apply(Term::Var(tail_id_6)) .delay_empty_choose_list( expect_b( val, Term::Var(then_delayed), trace, ), trace_error.clone(), ) }, &trace_error, ) }), ) }), ) } else { Term::tail_list() .apply(Term::Var(tail_id_5.clone())) .as_var("tail_id_6", |tail_id_6| { Term::tail_list() .apply(Term::Var(tail_id_6.clone())) .delayed_choose_list( Term::head_list() .apply(Term::Var(tail_id_6)) .as_var("__val", |val| { expect_b(val, Term::Var(then_delayed), trace) }), Term::Error, ) }) } }); if trace { Term::Var(tail_id_5.clone()) .delay_filled_choose_list(trace_error.clone(), inner) } else { inner } }) }; let when_constr_arity_2 = |redeemer: Rc, then_1st: Term, then_2nd: Term, trace: bool| { Term::fst_pair() .apply(Term::unconstr_data().apply(Term::Var(redeemer.clone()))) .as_var("__subject_span_0_0", |subject_span_0_0| { let when_constructor = |ix: usize| { Term::equals_integer() .apply(Term::integer(ix.into())) .apply(Term::Var(subject_span_0_0.clone())) }; when_constructor(0).delayed_if_then_else( then_1st, when_constructor(1).delay_true_if_then_else( then_2nd, if trace { Term::var("r:A") } else { Term::Error.delay() }, ), ) }) }; let choose_purpose = |redeemer: Rc, purpose: Rc, trace: bool| { Term::equals_integer() .apply(Term::integer(1.into())) .apply(Term::fst_pair().apply(Term::unconstr_data().apply(Term::Var(purpose.clone())))) .delayed_if_then_else( Term::snd_pair() .apply(Term::unconstr_data().apply(Term::Var(purpose))) .as_var("tail_id_10", |tail_id_10| { let body_part = body(redeemer.clone()) .delay() .as_var("then_delayed", |then_delayed| { when_constr_arity_2( redeemer.clone(), expect_no_a(redeemer.clone(), then_delayed.clone(), trace), expect_some_a( redeemer.clone(), then_delayed.clone(), trace, ), trace, ) }); if trace { Term::choose_data_constr( redeemer.clone(), |_| body_part, &Term::var("r:A"), ) } else { body_part } .lambda("__datum__") .lambda("__purpose_arg__") .apply(Term::head_list().apply(Term::Var(tail_id_10.clone()))) .apply( Term::head_list() .apply(Term::tail_list().apply(Term::Var(tail_id_10.clone()))), ) }), Term::Error, ) }; let validator = |trace: bool| { let context = "__context__"; Term::snd_pair() .apply(Term::unconstr_data().apply(Term::var(context))) .as_var("tail_id_13", |tail_id_13| { Term::tail_list() .apply(Term::Var(tail_id_13.clone())) .as_var("tail_id_14", |tail_id_14| { Term::head_list() .apply(Term::Var(tail_id_13.clone())) .as_var("__transaction__", |_transaction| { Term::head_list() .apply(Term::Var(tail_id_14.clone())) .as_var("__redeemer__", |redeemer| { Term::head_list() .apply(Term::tail_list().apply(Term::Var(tail_id_14))) .as_var("__purpose__", |purpose| { choose_purpose(redeemer, purpose, trace) }) }) }) }) }) .delayed_if_then_else( Term::unit(), if trace { Term::Error .apply(Term::Error.force()) .delayed_trace(Term::string("Validator returned false")) } else { Term::Error.apply(Term::Error.force()) }, ) .lambda(context) }; assert_uplc( src, validator(true) .lambda("r:A") .apply(Term::Error.delayed_trace(Term::string("r: A")).delay()), false, true, ); assert_uplc(src, validator(false), false, false); } #[test] fn pass_constr_as_function() { let src = r#" type Make { a: Int, b: SubMake } type SubMake { c: Int } fn hi(sm: SubMake, to_make: fn (Int, SubMake) -> Make) -> Make { to_make(3, sm) } test cry() { Make(3, SubMake(1)) == hi(SubMake(1), Make) } "#; assert_uplc( src, Term::equals_data() .apply(Term::data(Data::constr( 0, vec![ Data::integer(3.into()), Data::constr(0, vec![Data::integer(1.into())]), ], ))) .apply( Term::var("hi") .lambda("hi") .apply( Term::var("to_make") .apply(Term::integer(3.into())) .apply(Term::var("sm")) .lambda("to_make") .lambda("sm"), ) .apply(Term::data(Data::constr(0, vec![Data::integer(1.into())]))) .apply( Term::constr_data() .apply(Term::integer(0.into())) .apply( Term::mk_cons() .apply(Term::i_data().apply(Term::var("a"))) .apply( Term::mk_cons() .apply(Term::var("b")) .apply(Term::empty_list()), ), ) .lambda("b") .lambda("a"), ), ), false, true, ); } #[test] fn record_update_output_2_vals() { let src = r#" type Address { thing: ByteArray, } type Datum { NoDatum InlineDatum(Data) } type Output { address: Address, value: List>>>, datum: Datum, script_ref: Option, } type MyDatum { a: Int, } test huh() { let prev_output = Output { address: Address { thing: "script_hash_0" }, value: [], datum: InlineDatum(MyDatum{a: 3}), script_ref: None, } let next_output = Output { ..prev_output, value: [], datum: prev_output.datum } prev_output == next_output } "#; assert_uplc( src, Term::equals_data() .apply(Term::var("prev_output")) .apply(Term::var("next_output")) .lambda("next_output") .apply( Term::constr_data() .apply(Term::integer(0.into())) .apply( Term::mk_cons() .apply(Term::head_list().apply(Term::var("record_fields"))) .apply( Term::mk_cons() .apply(Term::map_data().apply(Term::empty_map())) .apply( Term::mk_cons() .apply( Term::head_list() .apply( Term::tail_list().apply( Term::tail_list() .apply(Term::var("__fields")), ), ) .lambda("__fields") .apply( Term::var(CONSTR_FIELDS_EXPOSER) .apply(Term::var("prev_output")), ), ) .apply(Term::var("tail_index_3")), ), ), ) .lambda("tail_index_3") .apply( Term::tail_list().apply( Term::tail_list() .apply(Term::tail_list().apply(Term::var("record_fields"))), ), ) .lambda("record_fields") .apply(Term::var(CONSTR_FIELDS_EXPOSER).apply(Term::var("prev_output"))), ) .lambda("prev_output") .apply(Term::data(Data::constr( 0, vec![ Data::constr( 0, vec![Data::bytestring("script_hash_0".as_bytes().to_vec())], ), Data::map(vec![]), Data::constr(1, vec![Data::constr(0, vec![Data::integer(3.into())])]), Data::constr(1, vec![]), ], ))) .constr_fields_exposer(), false, true, ); } #[test] fn record_update_output_1_val() { let src = r#" type Address { thing: ByteArray, } type Datum { NoDatum InlineDatum(Data) } type Output { address: Address, value: List>>>, datum: Datum, script_ref: Option, } type MyDatum { a: Int, } test huh() { let prev_output = Output { address: Address { thing: "script_hash_0" }, value: [], datum: InlineDatum(MyDatum{a: 3}), script_ref: None, } let next_output = Output { ..prev_output, datum: prev_output.datum } prev_output == next_output } "#; assert_uplc( src, Term::equals_data() .apply(Term::var("prev_output")) .apply(Term::var("next_output")) .lambda("next_output") .apply( Term::constr_data() .apply(Term::integer(0.into())) .apply( Term::mk_cons() .apply(Term::head_list().apply(Term::var("record_fields"))) .apply( Term::mk_cons() .apply(Term::head_list().apply(Term::var("tail_index_1"))) .apply( Term::mk_cons() .apply( Term::head_list() .apply( Term::tail_list().apply( Term::tail_list() .apply(Term::var("__fields")), ), ) .lambda("__fields") .apply( Term::var(CONSTR_FIELDS_EXPOSER) .apply(Term::var("prev_output")), ), ) .apply(Term::var("tail_index_3")), ), ), ) .lambda("tail_index_3") .apply( Term::tail_list().apply(Term::tail_list().apply(Term::var("tail_index_1"))), ) .lambda("tail_index_1") .apply(Term::tail_list().apply(Term::var("record_fields"))) .lambda("record_fields") .apply(Term::var(CONSTR_FIELDS_EXPOSER).apply(Term::var("prev_output"))), ) .lambda("prev_output") .apply(Term::data(Data::constr( 0, vec![ Data::constr( 0, vec![Data::bytestring("script_hash_0".as_bytes().to_vec())], ), Data::map(vec![]), Data::constr(1, vec![Data::constr(0, vec![Data::integer(3.into())])]), Data::constr(1, vec![]), ], ))) .constr_fields_exposer(), false, true, ); } #[test] fn record_update_output_first_last_val() { let src = r#" type Address { thing: ByteArray, } type Datum { NoDatum InlineDatum(Data) } type Output { address: Address, value: List>>>, datum: Datum, script_ref: Option, } type MyDatum { a: Int, } test huh() { let prev_output = Output { address: Address { thing: "script_hash_0" }, value: [], datum: InlineDatum(MyDatum{a: 3}), script_ref: None, } let next_output = Output { ..prev_output, script_ref: None, address: Address{thing: "script_hash_0"} } prev_output == next_output } "#; assert_uplc( src, Term::equals_data() .apply(Term::var("prev_output")) .apply(Term::var("next_output")) .lambda("next_output") .apply( Term::constr_data() .apply(Term::integer(0.into())) .apply( Term::mk_cons() .apply(Term::data(Data::constr( 0, vec![Data::bytestring("script_hash_0".as_bytes().to_vec())], ))) .apply( Term::mk_cons() .apply(Term::head_list().apply(Term::var("tail_index_1"))) .apply( Term::mk_cons() .apply( Term::head_list().apply(Term::var("tail_index_2")), ) .apply( Term::mk_cons() .apply(Term::data(Data::constr(1, vec![]))) .apply(Term::var("tail_index_4")), ), ), ), ) .lambda("tail_index_4") .apply(Term::empty_list()) .lambda("tail_index_2") .apply(Term::tail_list().apply(Term::var("tail_index_1"))) .lambda("tail_index_1") .apply(Term::tail_list().apply(Term::var("record_fields"))) .lambda("record_fields") .apply(Term::var(CONSTR_FIELDS_EXPOSER).apply(Term::var("prev_output"))), ) .lambda("prev_output") .apply(Term::data(Data::constr( 0, vec![ Data::constr( 0, vec![Data::bytestring("script_hash_0".as_bytes().to_vec())], ), Data::map(vec![]), Data::constr(1, vec![Data::constr(0, vec![Data::integer(3.into())])]), Data::constr(1, vec![]), ], ))) .constr_fields_exposer(), false, true, ); } #[test] fn list_fields_unwrap() { let src = r#" type Fields { a: ByteArray, b: Int, } fn data_fields(){ [ Fields{a: #"", b: 14}, Fields{a: #"AA", b: 0} ] } test list_fields_unwr_0() { when data_fields() is { [Fields { b, .. }, ..] -> b > 0 _ -> False } == True } "#; assert_uplc( src, Term::var("field_list") .delayed_choose_list( Term::bool(false), Term::less_than_integer() .apply(Term::integer(0.into())) .apply(Term::var("b")) .lambda("b") .apply( Term::un_i_data() .apply(Term::head_list().apply(Term::var("record_index_1"))), ) .lambda("record_index_1") .apply(Term::tail_list().apply(Term::var("item_1_fields"))) .lambda("item_1_fields") .apply(Term::var(CONSTR_FIELDS_EXPOSER).apply(Term::var("item_1"))) .lambda("item_1") .apply(Term::head_list().apply(Term::var("field_list"))), ) .lambda("field_list") .apply(Term::list_values(vec![ Constant::Data(Data::constr( 0, vec![Data::bytestring(vec![]), Data::integer(14.into())], )), Constant::Data(Data::constr( 0, vec![Data::bytestring(vec![170]), Data::integer(0.into())], )), ])) .delayed_if_then_else( Term::bool(true), Term::bool(true).if_then_else(Term::bool(false), Term::bool(true)), ) .constr_fields_exposer(), false, true, ); } #[test] fn foldl_type_mismatch() { let src = r#" type Address { payment_credential: ByteArray, stake_credential: Option, } type Output { address: Address, value: List, datum: Option, reference_script: Option, } pub fn foldl(self: List, with: fn(a, b) -> b, zero: b) -> b { when self is { [] -> zero [x, ..xs] -> foldl(xs, with, with(x, zero)) } } test hi() { let addr1 = Address { payment_credential: "adff", stake_credential: None } let out = Output { address: addr1, value: [], datum: None, reference_script: None } let outputs: List = [out, out, out] let cry = foldl( outputs, fn(o: Output, mb_b: Option) -> Option { when mb_b is { None -> if o.address == addr1 { Some(o) } else { None } otherwise -> otherwise } }, None, ) cry == cry } "#; assert_uplc( src, Term::equals_data() .apply(Term::var("cry")) .apply(Term::var("cry")) .lambda("cry") .apply( Term::var("foldl") .lambda("foldl") .apply( Term::var("foldl") .apply(Term::var("foldl")) .apply(Term::var("self")) .apply(Term::var("zero")) .lambda("foldl") .apply( Term::var("self") .delayed_choose_list( Term::var("zero"), Term::var("foldl") .apply(Term::var("foldl")) .apply(Term::var("xs")) .apply( Term::var("with") .apply(Term::var("x")) .apply(Term::var("zero")), ) .lambda("xs") .apply(Term::tail_list().apply(Term::var("self"))) .lambda("x") .apply(Term::head_list().apply(Term::var("self"))), ) .lambda("zero") .lambda("self") .lambda("foldl"), ) .lambda("zero") .lambda("with") .lambda("self"), ) .apply(Term::var("outputs")) .apply( Term::equals_integer() .apply(Term::integer(1.into())) .apply(Term::var("mb_b_index")) .delayed_if_then_else( Term::equals_data() .apply(Term::head_list().apply( Term::var(CONSTR_FIELDS_EXPOSER).apply(Term::var("o")), )) .apply(Term::var("addr1")) .delayed_if_then_else( Term::constr_data().apply(Term::integer(0.into())).apply( Term::mk_cons() .apply(Term::var("o")) .apply(Term::empty_list()), ), Term::Constant( Constant::Data(Data::constr(1, vec![])).into(), ), ), Term::var("mb_b"), ) .lambda("mb_b_index") .apply(Term::var(CONSTR_INDEX_EXPOSER).apply(Term::var("mb_b"))) .lambda("mb_b") .lambda("o"), ) .apply(Term::Constant( Constant::Data(Data::constr(1, vec![])).into(), )), ) .lambda("outputs") .apply( Term::mk_cons().apply(Term::var("out")).apply( Term::mk_cons().apply(Term::var("out")).apply( Term::mk_cons() .apply(Term::var("out")) .apply(Term::empty_list()), ), ), ) .lambda("out") .apply( Term::constr_data().apply(Term::integer(0.into())).apply( Term::mk_cons().apply(Term::var("addr1")).apply( Term::mk_cons() .apply(Term::list_data().apply(Term::empty_list())) .apply( Term::mk_cons() .apply(Term::Constant( Constant::Data(Data::constr(1, vec![])).into(), )) .apply( Term::mk_cons() .apply(Term::data(Data::constr(1, vec![]))) .apply(Term::empty_list()), ), ), ), ), ) .lambda("addr1") .apply(Term::data(Data::constr( 0, vec![ Data::bytestring("adff".as_bytes().to_vec()), Data::constr(1, vec![]), ], ))) .constr_fields_exposer() .constr_index_exposer(), false, true, ); } #[test] fn expect_head_discard_tail() { let src = r#" test hi() { let a = [1, 2, 3] expect [h, ..] = a h == h } "#; assert_uplc( src, Term::var("a") .delayed_choose_list( Term::Error.delayed_trace(Term::var("expect[h,..]=a")), Term::equals_integer() .apply(Term::var("h")) .apply(Term::var("h")) .lambda("h") .apply(Term::un_i_data().apply(Term::head_list().apply(Term::var("a")))), ) .lambda("a") .apply(Term::list_values(vec![ Constant::Data(Data::integer(1.into())), Constant::Data(Data::integer(2.into())), Constant::Data(Data::integer(3.into())), ])) .lambda("expect[h,..]=a") .apply(Term::string("expect [h, ..] = a")), false, true, ); } #[test] fn expect_head_no_tail() { let src = r#" test hi() { let a = [1, 2, 3] expect [h] = a h == h } "#; assert_uplc( src, Term::var("a") .delay_filled_choose_list( Term::var("expect[h]=a"), Term::tail_list() .apply(Term::var("a")) .delay_empty_choose_list( Term::equals_integer() .apply(Term::var("h")) .apply(Term::var("h")), Term::var("expect[h]=a"), ) .lambda("h") .apply(Term::un_i_data().apply(Term::head_list().apply(Term::var("a")))), ) .lambda("a") .apply(Term::list_values(vec![ Constant::Data(Data::integer(1.into())), Constant::Data(Data::integer(2.into())), Constant::Data(Data::integer(3.into())), ])) .lambda("expect[h]=a") .apply( Term::Error .delayed_trace(Term::string("expect [h] = a")) .delay(), ), true, true, ); } #[test] fn expect_head3_no_tail() { let src = r#" test hi() { let a = [1, 2, 3] expect [h, i, j] = a (h == h && i == i) && j == j } "#; assert_uplc( src, Term::var("a") .delay_filled_choose_list( Term::var("expect[h,i,j]=a"), Term::var("tail_1") .delay_filled_choose_list( Term::var("expect[h,i,j]=a"), Term::var("tail_2") .delay_filled_choose_list( Term::var("expect[h,i,j]=a"), Term::tail_list() .apply(Term::var("tail_2")) .delay_empty_choose_list( Term::equals_integer() .apply(Term::var("h")) .apply(Term::var("h")) .delayed_if_then_else( Term::equals_integer() .apply(Term::var("i")) .apply(Term::var("i")), Term::bool(false), ) .delayed_if_then_else( Term::equals_integer() .apply(Term::var("j")) .apply(Term::var("j")), Term::bool(false), ), Term::var("expect[h,i,j]=a"), ) .lambda("j") .apply( Term::un_i_data() .apply(Term::head_list().apply(Term::var("tail_2"))), ), ) .lambda("tail_2") .apply(Term::tail_list().apply(Term::var("tail_1"))) .lambda("i") .apply( Term::un_i_data() .apply(Term::head_list().apply(Term::var("tail_1"))), ), ) .lambda("tail_1") .apply(Term::tail_list().apply(Term::var("a"))) .lambda("h") .apply(Term::un_i_data().apply(Term::head_list().apply(Term::var("a")))), ) .lambda("a") .apply(Term::list_values(vec![ Constant::Data(Data::integer(1.into())), Constant::Data(Data::integer(2.into())), Constant::Data(Data::integer(3.into())), ])) .lambda(Term::var("expect[h,i,j]=a")) .apply( Term::Error .delayed_trace(Term::string("expect [h, i, j] = a")) .delay(), ), false, true, ); } #[test] fn expect_head3_cast_data_no_tail() { let src = r#" test hi() { let a: Data = [1, 2, 3] expect [h, i, j]: List = a (h == h && i ==i) && j == j } "#; let otherwise_expect = &Term::var("expect[h,i,j]:List=a"); let otherwise_msg: Term = Term::Error .delayed_trace(Term::string("expect [h, i, j]: List = a")) .delay(); let then = Term::equals_integer() .apply(Term::var("h")) .apply(Term::var("h")) .delayed_if_then_else( Term::equals_integer() .apply(Term::var("i")) .apply(Term::var("i")), Term::bool(false), ) .delayed_if_then_else( Term::equals_integer() .apply(Term::var("j")) .apply(Term::var("j")), Term::bool(false), ); let unwrap_third = |head, tail: Term| { Term::choose_data_integer( head, |head_int| { head_int.as_var("j", |_| { tail.delay_empty_choose_list(then, otherwise_expect.clone()) }) }, otherwise_expect, ) }; let unwrap_second = |head, tail: Term| { Term::choose_data_integer( head, |head_int| { head_int.as_var("i", |_| { tail.as_var("tail_id_2", |tail| { Term::unwrap_tail_or( tail.clone(), |tail2| { Term::head_list() .apply(Term::Var(tail)) .as_var("head2", |head2| unwrap_third(head2, tail2)) }, otherwise_expect, ) }) }) }, otherwise_expect, ) }; let unwrap_first = |head, tail: Term| { Term::choose_data_integer( head, |head_int| { head_int.as_var("h", |_| { tail.as_var("tail_id_1", |tail| { Term::unwrap_tail_or( tail.clone(), |tail2| { Term::head_list() .apply(Term::Var(tail)) .as_var("head2", |head2| unwrap_second(head2, tail2)) }, otherwise_expect, ) }) }) }, otherwise_expect, ) }; let data_values = Term::data(Data::list(vec![ Data::integer(1.into()), Data::integer(2.into()), Data::integer(3.into()), ])); assert_uplc( src, data_values .as_var("a", |a| { Term::choose_data_list( a, |list| { list.as_var("list", |list_var| { Term::unwrap_tail_or( list_var.clone(), |tail| { Term::head_list() .apply(Term::Var(list_var)) .as_var("__val", |head| unwrap_first(head, tail)) }, otherwise_expect, ) }) }, otherwise_expect, ) }) .lambda("expect[h,i,j]:List=a") .apply(otherwise_msg), false, true, ); } #[test] fn expect_head_cast_data_no_tail() { let src = r#" test hi() { let a: Data = [1, 2, 3] expect [h]: List = a h == h } "#; assert_uplc( src, Term::data(Data::list(vec![ Data::integer(1.into()), Data::integer(2.into()), Data::integer(3.into()), ])) .choose_data( Term::var("expect[h]:List=a"), Term::var("expect[h]:List=a"), Term::list_values(vec![ Constant::Data(Data::integer(1.into())), Constant::Data(Data::integer(2.into())), Constant::Data(Data::integer(3.into())), ]) .delay_filled_choose_list( Term::var("expect[h]:List=a"), Term::var("__var") .choose_data( Term::var("expect[h]:List=a"), Term::var("expect[h]:List=a"), Term::var("expect[h]:List=a"), Term::tail_list() .apply(Term::list_values(vec![ Constant::Data(Data::integer(1.into())), Constant::Data(Data::integer(2.into())), Constant::Data(Data::integer(3.into())), ])) .delay_empty_choose_list( Term::equals_integer() .apply(Term::var("h")) .apply(Term::var("h")), Term::var("expect[h]:List=a"), ) .lambda("h") .apply(Term::un_i_data().apply(Term::var("__var"))) .delay(), Term::var("expect[h]:List=a"), ) .force() .lambda("__var") .apply(Term::head_list().apply(Term::list_values(vec![ Constant::Data(Data::integer(1.into())), Constant::Data(Data::integer(2.into())), Constant::Data(Data::integer(3.into())), ]))), ) .delay(), Term::var("expect[h]:List=a"), Term::var("expect[h]:List=a"), ) .force() .lambda("expect[h]:List=a") .apply( Term::Error .delayed_trace(Term::string("expect [h]: List = a")) .delay(), ), true, true, ); } #[test] fn expect_head_cast_data_with_tail() { let src = r#" test hi() { let a: Data = [1, 2, 3] expect [h, j, ..]: List = a h == h && j == j } "#; let expect_on_list = Term::var(EXPECT_ON_LIST) .apply(Term::var(EXPECT_ON_LIST)) .apply(Term::var("__list")) .lambda(EXPECT_ON_LIST) .apply( Term::var("check_with") .apply(Term::var("__list")) .apply(Term::var(EXPECT_ON_LIST).apply(Term::var(EXPECT_ON_LIST))) .lambda("__list") .lambda(EXPECT_ON_LIST) .lambda("__no_inline__"), ) .lambda("check_with") .lambda("__list") .lambda("__no_inline__"); let values = Term::list_values(vec![ Constant::Data(Data::integer(1.into())), Constant::Data(Data::integer(2.into())), Constant::Data(Data::integer(3.into())), ]); let then = Term::equals_integer() .apply(Term::var("h")) .apply(Term::var("h")) .delayed_if_then_else( Term::equals_integer() .apply(Term::var("j")) .apply(Term::var("j")), Term::bool(false), ); let check_with = Term::var("__list") .delayed_choose_list( then, Term::choose_data_integer( Name::text("__head").into(), |v| { Term::var("__curried_expect_on_list") .apply(Term::tail_list().apply(Term::var("__list"))) .lambda("_") .apply(v) }, &Term::var("expect[h,j,..]:List=a"), ) .lambda("__head") .apply(Term::head_list().apply(Term::var("__list"))), ) .lambda("__curried_expect_on_list") .lambda("__list") .lambda("__no_inline__"); let on_list = values.clone().delay_filled_choose_list( Term::var("expect[h,j,..]:List=a"), Term::var("__val") .choose_data( Term::var("expect[h,j,..]:List=a"), Term::var("expect[h,j,..]:List=a"), Term::var("expect[h,j,..]:List=a"), Term::var("tail_1") .delay_filled_choose_list( Term::var("expect[h,j,..]:List=a"), Term::var("__val") .choose_data( Term::var("expect[h,j,..]:List=a"), Term::var("expect[h,j,..]:List=a"), Term::var("expect[h,j,..]:List=a"), Term::var(EXPECT_ON_LIST) .lambda(EXPECT_ON_LIST) .apply(expect_on_list) .apply(Term::tail_list().apply(Term::var("tail_1"))) .apply(check_with) .lambda("j") .apply(Term::un_i_data().apply(Term::var("__val"))) .delay(), Term::var("expect[h,j,..]:List=a"), ) .force() .lambda("__val") .apply(Term::head_list().apply(Term::var("tail_1"))), ) .lambda("tail_1") .apply(Term::tail_list().apply(values.clone())) .lambda("h") .apply(Term::un_i_data().apply(Term::var("__val"))) .delay(), Term::var("expect[h,j,..]:List=a"), ) .force() .lambda("__val") .apply(Term::head_list().apply(values)), ); assert_uplc( src, Term::choose_data_list( Name::text("__val").into(), |_| on_list, &Term::var("expect[h,j,..]:List=a"), ) .lambda("__val") .apply(Term::data(Data::list(vec![ Data::integer(1.into()), Data::integer(2.into()), Data::integer(3.into()), ]))) .lambda("expect[h,j,..]:List=a") .apply( Term::Error .delayed_trace(Term::string("expect [h, j, ..]: List = a")) .delay(), ), false, true, ); } #[test] fn test_init_3() { let src = r#" pub fn init(self: List) -> Option> { when self is { [] -> None _ -> Some(do_init(self)) } } fn do_init(self: List) -> List { when self is { [] -> fail @"unreachable" [_] -> [] [x, ..xs] -> [x, ..do_init(xs)] } } test init_3() { init([1, 2, 3, 4]) == Some([1, 2, 3]) } "#; assert_uplc( src, Term::equals_data() .apply( Term::var("init") .lambda("init") .apply( Term::var("self") .delayed_choose_list( Term::data(Data::constr(1, vec![])), Term::constr_data().apply(Term::integer(0.into())).apply( Term::mk_cons() .apply( Term::list_data().apply( Term::var("do_init").apply(Term::var("self")), ), ) .apply(Term::empty_list()), ), ) .lambda("self"), ) .lambda("do_init") .apply(Term::var("do_init").apply(Term::var("do_init"))) .lambda("do_init") .apply( Term::var("self") .delayed_choose_list( Term::Error.delayed_trace(Term::string("unreachable")), Term::var("tail_1") .delayed_choose_list( Term::empty_list(), Term::mk_cons() .apply(Term::i_data().apply(Term::var("x"))) .apply( Term::var("do_init") .apply(Term::var("do_init")) .apply(Term::var("tail_1")), ) .lambda("x") .apply( Term::un_i_data().apply( Term::head_list().apply(Term::var("self")), ), ), ) .lambda("tail_1") .apply(Term::tail_list().apply(Term::var("self"))), ) .lambda("self") .lambda("do_init"), ) .apply(Term::list_values(vec![ Constant::Data(Data::integer(1.into())), Constant::Data(Data::integer(2.into())), Constant::Data(Data::integer(3.into())), Constant::Data(Data::integer(4.into())), ])), ) .apply(Term::data(Data::constr( 0, vec![Data::list(vec![ Data::integer(1.into()), Data::integer(2.into()), Data::integer(3.into()), ])], ))), false, true, ); } #[test] fn list_clause_with_assign() { let src = r#" fn do_init(self: List>) -> List> { when self is { [] -> fail @"unreachable" [_] as a -> a [Some(_) as n, x] -> { [n] } [a, x] -> { [x] } [a, b, ..c] -> { c } } } test init_3() { do_init([Some(1), None]) == [Some(1)] } "#; assert_uplc( src, Term::equals_data() .apply( Term::list_data().apply( Term::var("do_init") .lambda("do_init") .apply( Term::var("self") .delayed_choose_list( Term::Error.delayed_trace(Term::string("unreachable")), Term::var("tail_1") .delayed_choose_list( Term::var("self"), Term::var("tail_2") .delayed_choose_list( Term::equals_integer() .apply(Term::integer(0.into())) .apply( Term::var(CONSTR_INDEX_EXPOSER) .apply(Term::var("n")), ) .delayed_if_then_else( Term::mk_cons() .apply(Term::var("n")) .apply(Term::empty_list()) .lambda("x") .apply( Term::head_list() .apply(Term::var("tail_1")), ), Term::mk_cons() .apply(Term::var("x")) .apply(Term::empty_list()) .lambda("x") .apply( Term::head_list() .apply(Term::var("tail_1")), ), ) .lambda("n") .apply( Term::head_list() .apply(Term::var("self")), ), Term::var("c") .lambda("c") .apply(Term::var("tail_2")) .lambda("b") .lambda("a") .apply( Term::head_list() .apply(Term::var("self")), ) .apply( Term::head_list() .apply(Term::var("tail_1")), ), ) .lambda("tail_2") .apply( Term::tail_list().apply(Term::var("tail_1")), ), ) .lambda("tail_1") .apply(Term::tail_list().apply(Term::var("self"))), ) .lambda("self"), ) .apply(Term::list_values(vec![ Constant::Data(Data::constr(0, vec![Data::integer(1.into())])), Constant::Data(Data::constr(1, vec![])), ])), ), ) .apply(Term::data(Data::list(vec![Data::constr( 0, vec![Data::integer(1.into())], )]))) .constr_index_exposer(), false, true, ); } #[test] fn opaque_value_in_test() { let src = r#" const dat: Dat = { let v = Value { inner: Dict { inner: [Pair("", [Pair(#"aa", 4)] |> Dict)] } } Dat { c: 0, a: v } } pub opaque type Value { inner: Dict> } pub opaque type Dict { inner: List> } pub type Dat { c: Int, a: Value } test spend() { let val = dat.a expect [Pair(_, amount)] = val.inner.inner let final_amount = [Pair(#"AA", 4)] |> Dict final_amount == amount } "#; let expect_otherwise = &Term::var("expect[Pair(_,amount)]=val.inner.inner"); let expect_delay_error: Term = Term::Error .delayed_trace(Term::string("expect [Pair(_, amount)] = val.inner.inner")) .delay(); let final_comparison = Term::equals_data() .apply(Term::map_data().apply(Term::var("final_amount"))) .apply(Term::map_data().apply(Term::var("amount"))); let assignments_body = Term::unmap_data() .apply(Term::head_list().apply( Term::tail_list().apply(Term::var(CONSTR_FIELDS_EXPOSER).apply(Term::var("dat"))), )) .as_var("val", |val| { Term::unwrap_tail_or( val.clone(), |tail| { Term::head_list() .apply(Term::Var(val)) .as_var("elem_0", |elem| { tail.delay_empty_choose_list( Term::unmap_data() .apply(Term::snd_pair().apply(Term::Var(elem))) .as_var("amount", |_| { Term::map_values(vec![Constant::ProtoPair( Type::Data, Type::Data, Constant::Data(Data::bytestring(vec![170])).into(), Constant::Data(Data::integer(4.into())).into(), )]) .as_var("final_amount", |_| final_comparison) }), expect_otherwise.clone(), ) }) }, expect_otherwise, ) }); assert_uplc( src, assignments_body .lambda("dat") .apply(Term::data(Data::constr( 0, vec![ Data::integer(0.into()), Data::map(vec![( Data::bytestring(vec![]), Data::map(vec![(Data::bytestring(vec![170]), Data::integer(4.into()))]), )]), ], ))) .constr_fields_exposer() .lambda("expect[Pair(_,amount)]=val.inner.inner") .apply(expect_delay_error) .lambda("dat:Dat") .apply(Term::Error.delayed_trace(Term::string("dat: Dat")).delay()), false, true, ); } #[test] fn expect_none() { let src = r#" test exp_none() { let x = None expect None = x True } "#; assert_uplc( src, Term::equals_integer() .apply(Term::integer(1.into())) .apply(Term::var(CONSTR_INDEX_EXPOSER).apply(Term::var("x"))) .delayed_if_then_else( Term::bool(true), Term::Error.delayed_trace(Term::string("expect None = x")), ) .lambda("x") .apply(Term::Constant( Constant::Data(Data::constr(1, vec![])).into(), )) .constr_index_exposer(), false, true, ); } #[test] fn head_list_on_map() { let src = r#" use aiken/builtin test exp_none() { let x = [Pair(1, ""), Pair(2, #"aa")] builtin.head_list(x) == Pair(1, "") } "#; assert_uplc( src, Term::equals_data() .apply( Term::map_data().apply( Term::mk_cons() .apply(Term::head_list().apply(Term::var("x"))) .apply(Term::empty_map()), ), ) .apply( Term::map_data().apply( Term::mk_cons() .apply(Term::pair_values( Constant::Data(Data::integer(1.into())), Constant::Data(Data::bytestring(vec![])), )) .apply(Term::empty_map()), ), ) .lambda("x") .apply(Term::map_values(vec![ Constant::ProtoPair( Type::Data, Type::Data, Constant::Data(Data::integer(1.into())).into(), Constant::Data(Data::bytestring(vec![])).into(), ), Constant::ProtoPair( Type::Data, Type::Data, Constant::Data(Data::integer(2.into())).into(), Constant::Data(Data::bytestring(vec![170])).into(), ), ])), false, true, ); } #[test] fn tuple_2_match() { let src = r#" type CurveInt { ECI { ec: (Int, Int)} Infinity } fn equivalence(ec1: CurveInt, ec2: CurveInt) -> Bool { let input = (ec1, ec2) when input is { (ECI { ec: (x1, y1)}, ECI { ec: (x2, y2)}) -> { x2 - x1 == 0 && y2 - y1 == 0 } (Infinity, Infinity) -> { True } (Infinity, ECI { ec: _}) -> { False } (ECI { ec: _}, Infinity) -> { False } } } test equivalence1() { equivalence(Infinity, Infinity) == True } "#; assert_uplc( src, Term::var("equivalence") .lambda("equivalence") .apply( Term::equals_integer() .apply(Term::integer(0.into())) .apply(Term::var(CONSTR_INDEX_EXPOSER).apply(Term::var("tuple_index_0"))) .delayed_if_then_else( Term::equals_integer() .apply(Term::integer(0.into())) .apply( Term::var(CONSTR_INDEX_EXPOSER).apply(Term::var("tuple_index_1")), ) .delayed_if_then_else( Term::equals_integer() .apply( Term::subtract_integer() .apply(Term::var("x2")) .apply(Term::var("x1")), ) .apply(Term::integer(0.into())) .delayed_if_then_else( Term::equals_integer() .apply( Term::subtract_integer() .apply(Term::var("y2")) .apply(Term::var("y1")), ) .apply(Term::integer(0.into())), Term::bool(false), ) .lambda("y2") .lambda("x2") .lambda("y1") .lambda("x1") .apply( Term::un_i_data().apply( Term::head_list().apply(Term::var("field_0_pair")), ), ) .apply(Term::un_i_data().apply( Term::head_list().apply( Term::tail_list().apply(Term::var("field_0_pair")), ), )) .apply( Term::un_i_data().apply( Term::head_list().apply(Term::var("field_1_pair")), ), ) .apply(Term::un_i_data().apply( Term::head_list().apply( Term::tail_list().apply(Term::var("field_1_pair")), ), )) .lambda("field_1_pair") .apply(Term::unlist_data().apply( Term::head_list().apply(Term::var("tuple_index_1_fields")), )) .lambda("tuple_index_1_fields") .apply( Term::var(CONSTR_FIELDS_EXPOSER) .apply(Term::var("tuple_index_1")), ) .lambda("field_0_pair") .apply(Term::unlist_data().apply( Term::head_list().apply(Term::var("tuple_index_0_fields")), )) .lambda("tuple_index_0_fields") .apply( Term::var(CONSTR_FIELDS_EXPOSER) .apply(Term::var("tuple_index_0")), ), Term::bool(false), ) .lambda("tuple_index_1") .apply( Term::head_list() .apply(Term::tail_list().apply(Term::var("input"))), ), Term::equals_integer() .apply(Term::integer(1.into())) .apply( Term::var(CONSTR_INDEX_EXPOSER).apply(Term::var("tuple_index_1")), ) .delayed_if_then_else(Term::bool(true), Term::bool(false)) .lambda("tuple_index_1") .apply( Term::head_list() .apply(Term::tail_list().apply(Term::var("input"))), ), ) .lambda("tuple_index_0") .apply(Term::head_list().apply(Term::var("input"))) .lambda("input") .apply( Term::mk_cons().apply(Term::var("ec1")).apply( Term::mk_cons() .apply(Term::var("ec2")) .apply(Term::empty_list()), ), ) .lambda("ec2") .lambda("ec1"), ) .apply(Term::data(Data::constr(1, vec![]))) .apply(Term::data(Data::constr(1, vec![]))) .delayed_if_then_else( Term::bool(true), Term::bool(true).if_then_else(Term::bool(false), Term::bool(true)), ) .constr_index_exposer() .constr_fields_exposer(), false, true, ); } #[test] fn bls12_381_elements_to_data_conversion() { let src = r#" pub type Proof { piA: G1Element, piB: G2Element, } test thing() { let pk = Proof { piA: #"b28cb29bc282be68df977b35eb9d8e98b3a0a3fc7c372990bddc50419ca86693e491755338fed4fb42231a7c081252ce", piB: #"b9215e5bc481ba6552384c89c23d45bd650b69462868248bfbb83aee7060579404dba41c781dec7c2bec5fccec06842e0e66ad6d86c7c76c468a32c9c0080eea0219d0953b44b1c4f5605afb1e5a3193264ff730222e94f55207628235f3b423", } pk == pk } "#; let constant = Term::Constant( Constant::Data(Data::constr( 0, vec![ Data::bytestring(vec![ 0xb2, 0x8c, 0xb2, 0x9b, 0xc2, 0x82, 0xbe, 0x68, 0xdf, 0x97, 0x7b, 0x35, 0xeb, 0x9d, 0x8e, 0x98, 0xb3, 0xa0, 0xa3, 0xfc, 0x7c, 0x37, 0x29, 0x90, 0xbd, 0xdc, 0x50, 0x41, 0x9c, 0xa8, 0x66, 0x93, 0xe4, 0x91, 0x75, 0x53, 0x38, 0xfe, 0xd4, 0xfb, 0x42, 0x23, 0x1a, 0x7c, 0x08, 0x12, 0x52, 0xce, ]), Data::bytestring(vec![ 0xb9, 0x21, 0x5e, 0x5b, 0xc4, 0x81, 0xba, 0x65, 0x52, 0x38, 0x4c, 0x89, 0xc2, 0x3d, 0x45, 0xbd, 0x65, 0x0b, 0x69, 0x46, 0x28, 0x68, 0x24, 0x8b, 0xfb, 0xb8, 0x3a, 0xee, 0x70, 0x60, 0x57, 0x94, 0x04, 0xdb, 0xa4, 0x1c, 0x78, 0x1d, 0xec, 0x7c, 0x2b, 0xec, 0x5f, 0xcc, 0xec, 0x06, 0x84, 0x2e, 0x0e, 0x66, 0xad, 0x6d, 0x86, 0xc7, 0xc7, 0x6c, 0x46, 0x8a, 0x32, 0xc9, 0xc0, 0x08, 0x0e, 0xea, 0x02, 0x19, 0xd0, 0x95, 0x3b, 0x44, 0xb1, 0xc4, 0xf5, 0x60, 0x5a, 0xfb, 0x1e, 0x5a, 0x31, 0x93, 0x26, 0x4f, 0xf7, 0x30, 0x22, 0x2e, 0x94, 0xf5, 0x52, 0x07, 0x62, 0x82, 0x35, 0xf3, 0xb4, 0x23, ]), ], )) .into(), ); assert_uplc( src, Term::equals_data().apply(constant.clone()).apply(constant), false, true, ) } #[test] fn bls12_381_elements_from_data_conversion() { let src = r#" pub type Proof { piA: G1Element, piB: G2Element, } test thing() { let pk = Proof { piA: #"b28cb29bc282be68df977b35eb9d8e98b3a0a3fc7c372990bddc50419ca86693e491755338fed4fb42231a7c081252ce", piB: #"b9215e5bc481ba6552384c89c23d45bd650b69462868248bfbb83aee7060579404dba41c781dec7c2bec5fccec06842e0e66ad6d86c7c76c468a32c9c0080eea0219d0953b44b1c4f5605afb1e5a3193264ff730222e94f55207628235f3b423", } pk.piA == #"b28cb29bc282be68df977b35eb9d8e98b3a0a3fc7c372990bddc50419ca86693e491755338fed4fb42231a7c081252ce" } "#; let bytes = vec![ 0xb2, 0x8c, 0xb2, 0x9b, 0xc2, 0x82, 0xbe, 0x68, 0xdf, 0x97, 0x7b, 0x35, 0xeb, 0x9d, 0x8e, 0x98, 0xb3, 0xa0, 0xa3, 0xfc, 0x7c, 0x37, 0x29, 0x90, 0xbd, 0xdc, 0x50, 0x41, 0x9c, 0xa8, 0x66, 0x93, 0xe4, 0x91, 0x75, 0x53, 0x38, 0xfe, 0xd4, 0xfb, 0x42, 0x23, 0x1a, 0x7c, 0x08, 0x12, 0x52, 0xce, ]; let g1 = Term::Constant( Constant::Bls12_381G1Element(blst::blst_p1::uncompress(&bytes).unwrap().into()).into(), ); let constant = Term::Constant( Constant::Data(Data::constr( 0, vec![ Data::bytestring(bytes), Data::bytestring(vec![ 0xb9, 0x21, 0x5e, 0x5b, 0xc4, 0x81, 0xba, 0x65, 0x52, 0x38, 0x4c, 0x89, 0xc2, 0x3d, 0x45, 0xbd, 0x65, 0x0b, 0x69, 0x46, 0x28, 0x68, 0x24, 0x8b, 0xfb, 0xb8, 0x3a, 0xee, 0x70, 0x60, 0x57, 0x94, 0x04, 0xdb, 0xa4, 0x1c, 0x78, 0x1d, 0xec, 0x7c, 0x2b, 0xec, 0x5f, 0xcc, 0xec, 0x06, 0x84, 0x2e, 0x0e, 0x66, 0xad, 0x6d, 0x86, 0xc7, 0xc7, 0x6c, 0x46, 0x8a, 0x32, 0xc9, 0xc0, 0x08, 0x0e, 0xea, 0x02, 0x19, 0xd0, 0x95, 0x3b, 0x44, 0xb1, 0xc4, 0xf5, 0x60, 0x5a, 0xfb, 0x1e, 0x5a, 0x31, 0x93, 0x26, 0x4f, 0xf7, 0x30, 0x22, 0x2e, 0x94, 0xf5, 0x52, 0x07, 0x62, 0x82, 0x35, 0xf3, 0xb4, 0x23, ]), ], )) .into(), ); assert_uplc( src, Term::bls12_381_g1_equal() .apply(Term::bls12_381_g1_uncompress().apply( Term::un_b_data().apply( Term::head_list().apply( Term::snd_pair().apply(Term::unconstr_data().apply(constant.clone())), ), ), )) .apply(g1), false, true, ) } #[test] fn qualified_prelude_functions() { let src = r#" use aiken test foo() { aiken.identity(True) && identity(True) } "#; let constant_true = Term::Constant(Constant::Bool(true).into()); let constant_false = Term::Constant(Constant::Bool(false).into()); assert_uplc( src, constant_true .clone() .delayed_if_then_else(constant_true, constant_false), false, true, ) } #[test] fn mk_cons_direct_invoke_1() { let src = r#" use aiken/builtin test mk_cons_1() { builtin.cons_list(1, []) == [1] } "#; assert_uplc( src, Term::equals_data() .apply( Term::list_data().apply( Term::mk_cons() .apply(Term::data(Data::integer(1.into()))) .apply(Term::empty_list()), ), ) .apply(Term::data(Data::list(vec![Data::integer(1.into())]))), false, true, ) } #[test] fn mk_cons_direct_invoke_2() { let src = r#" use aiken/builtin.{cons_list} test mk_cons_2() { cons_list(Some(42), [None]) == [Some(42), None] } "#; let none = Data::constr(1, Vec::new()); let some = Data::constr(0, vec![Data::integer(42.into())]); assert_uplc( src, Term::equals_data() .apply( Term::list_data().apply(Term::mk_cons().apply(Term::data(some.clone())).apply( Term::Constant( Constant::ProtoList(Type::Data, vec![Constant::Data(none.clone())]).into(), ), )), ) .apply(Term::data(Data::list(vec![some, none]))), false, true, ) } #[test] fn mk_cons_direct_invoke_3() { let src = r#" use aiken/builtin.{cons_list, i_data, new_pairs} test mk_cons_3() { cons_list(Pair(i_data(1), i_data(1)), new_pairs()) == [ Pair(i_data(1), i_data(1)), ] } "#; assert_uplc( src, Term::equals_data() .apply( Term::map_data().apply( Term::mk_cons() .apply(Term::Constant( Constant::ProtoPair( Type::Data, Type::Data, Constant::Data(Data::integer(1.into())).into(), Constant::Data(Data::integer(1.into())).into(), ) .into(), )) .apply(Term::mk_nil_pair_data().apply(Term::unit())), ), ) .apply(Term::data(Data::map(vec![( Data::integer(1.into()), Data::integer(1.into()), )]))), false, true, ) } #[test] fn mk_nil_pair_data() { let src = r#" use aiken/builtin.{new_pairs} test nil_equals() { new_pairs() == new_pairs() } "#; assert_uplc( src, Term::equals_data() .apply(Term::map_data().apply(Term::mk_nil_pair_data().apply(Term::unit()))) .apply(Term::map_data().apply(Term::mk_nil_pair_data().apply(Term::unit()))), false, true, ) } #[test] fn mk_nil_list_data() { let src = r#" use aiken/builtin.{new_list} test nil_equals() { new_list() == new_list() } "#; assert_uplc( src, Term::equals_data() .apply(Term::list_data().apply(Term::mk_nil_data().apply(Term::unit()))) .apply(Term::list_data().apply(Term::mk_nil_data().apply(Term::unit()))), false, true, ) } #[test] fn mk_pair_data() { let src = r#" use aiken/builtin.{i_data} test nil_equals() { builtin.new_pair(i_data(1), i_data(2)).1st == i_data(1) } "#; assert_uplc( src, Term::equals_data() .apply( Term::fst_pair().apply( Term::mk_pair_data() .apply(Term::Constant( Constant::Data(Data::integer(1.into())).into(), )) .apply(Term::Constant( Constant::Data(Data::integer(2.into())).into(), )), ), ) .apply(Term::Constant( Constant::Data(Data::integer(1.into())).into(), )), false, true, ) } #[test] fn pattern_bytearray() { let src = r#" test pattern_bytearray() { let bytes = "foo" when bytes is { "bar" -> False #[0x66, 0x6f, 0x6f] -> True _ -> False } } "#; let snd_clause = Term::equals_bytestring() .apply(Term::byte_string(vec![0x66, 0x6f, 0x6f])) .apply(Term::var("__subject")) .delayed_if_then_else(Term::bool(true), Term::bool(false)); let fst_clause = Term::equals_bytestring() .apply(Term::byte_string(vec![0x62, 0x61, 0x72])) .apply(Term::var("__subject")) .delayed_if_then_else(Term::bool(false), snd_clause); let when_clause = fst_clause .lambda("__subject") .apply(Term::var("__when_var")) .lambda("__when_var") .apply(Term::var("bytes")); let program = when_clause .lambda("bytes") .apply(Term::byte_string(vec![0x66, 0x6f, 0x6f])) // Not sure what this extra lambda is or do? .lambda("???") .apply(Term::Error.delay()); assert_uplc(src, program, false, true) } #[test] fn cast_never() { let src = r#" test never_ok_cast() { let none: Option = None let data: Data = none expect _: Never = data } "#; let none_or_never = || Term::Constant(Constant::Data(Data::constr(1, vec![])).into()); let expect_otherwise = Term::Error .delayed_trace(Term::string("expect _: Never = data")) .delay(); let assert_constr_index = Term::equals_integer() .apply(Term::integer(1.into())) .apply(Term::fst_pair().apply(Term::unconstr_data().apply(none_or_never()))); let assert_empty_fields = |then: Term, expect_otherwise: Rc| { Term::snd_pair() .apply(Term::unconstr_data().apply(none_or_never())) .delay_empty_choose_list(then, Term::Var(expect_otherwise)) }; let program = expect_otherwise.as_var("expect_:Never=data", |expect_otherwise| { let otherwise = Term::Var(expect_otherwise.clone()); let when_constr = assert_constr_index.delay_true_if_then_else( assert_empty_fields(Term::unit(), expect_otherwise.clone()), Term::Var(expect_otherwise), ); none_or_never() .choose_data( when_constr.delay(), otherwise.clone(), otherwise.clone(), otherwise.clone(), otherwise, ) .force() }); assert_uplc(src, program, false, true) } #[test] fn hard_soft_cast() { let src = r#" type Foo{ Bar Bax } test hard_soft_cast() { let x: Data = Bar if x is Foo { True } else { expect y: Foo = x y == y } } "#; let otherwise = &Term::var("otherwise"); let soft_cast = Term::fst_pair() .apply(Term::unconstr_data().apply(Term::var("__param_0"))) .as_var("index", |index| { Term::equals_integer() .apply(Term::integer(0.into())) .apply(Term::Var(index.clone())) .delayed_if_then_else( Term::snd_pair() .apply(Term::unconstr_data().apply(Term::var("__param_0"))) .delay_empty_choose_list(Term::var("then").force(), otherwise.clone()), Term::equals_integer() .apply(Term::integer(1.into())) .apply(Term::Var(index.clone())) .delay_true_if_then_else( Term::snd_pair() .apply(Term::unconstr_data().apply(Term::var("__param_0"))) .delay_empty_choose_list( Term::var("then").force(), otherwise.clone(), ), otherwise.clone(), ), ) }) .lambda("otherwise") .lambda("then") .lambda("__param_0"); let hard_cast = Term::fst_pair() .apply(Term::unconstr_data().apply(Term::var("__param_0"))) .as_var("index", |index| { Term::equals_integer() .apply(Term::integer(0.into())) .apply(Term::Var(index.clone())) .delayed_if_then_else( Term::snd_pair() .apply(Term::unconstr_data().apply(Term::var("__param_0"))) .delayed_choose_list(Term::var("then").force(), Term::Error), Term::equals_integer() .apply(Term::integer(1.into())) .apply(Term::Var(index.clone())) .delayed_if_then_else( Term::snd_pair() .apply(Term::unconstr_data().apply(Term::var("__param_0"))) .delayed_choose_list(Term::var("then").force(), Term::Error), Term::Error, ), ) }) .lambda("then") .lambda("__param_0"); let program_traces = Term::data(Data::constr(0, vec![])) .as_var("x", |x| { Term::choose_data_constr( x.clone(), |val| { Term::var("__Foo__otherwise") .apply(val.clone()) .apply( Term::equals_data() .apply(Term::var("y")) .apply(Term::var("y")) .lambda("y") .apply(val) .delay(), ) .apply(Term::var("expecty:Foo=x")) }, &Term::var("expecty:Foo=x"), ) .delay() .as_var("acc", |acc| { Term::choose_data_constr( x, |val| { Term::var("__Foo__otherwise") .apply(val) .apply(Term::bool(true).delay()) .apply(Term::Var(acc.clone())) }, &Term::Var(acc.clone()), ) }) }) .lambda("__Foo__otherwise") .apply(soft_cast.clone()) .lambda("expecty:Foo=x") .apply( Term::Error .delayed_trace(Term::string("expect y: Foo = x")) .delay(), ); assert_uplc(src, program_traces, false, true); let program = Term::data(Data::constr(0, vec![])) .as_var("x", |x| { Term::var("__Foo_") .lambda("__Foo_") .apply(hard_cast) .apply(Term::Var(x.clone())) .apply( Term::equals_data() .apply(Term::var("y")) .apply(Term::var("y")) .lambda("y") .apply(Term::Var(x.clone())) .delay(), ) .delay() .as_var("acc", |acc| { Term::choose_data_constr( x, |val| { Term::var("__Foo__otherwise") .apply(val) .apply(Term::bool(true).delay()) .apply(Term::Var(acc.clone())) }, &Term::Var(acc.clone()), ) }) }) .lambda("__Foo__otherwise") .apply(soft_cast.clone()); assert_uplc(src, program, false, false); } #[test] fn dangling_trace_expect_standalone() { let src = r#" test foo() { trace @"foo" expect True } "#; let program = Term::bool(true) .delayed_if_then_else( Term::unit(), Term::Error.delayed_trace(Term::string("expect True")), ) .delayed_trace(Term::string("foo")); assert_uplc(src, program, false, true) } #[test] fn dangling_trace_expect_in_sequence() { let src = r#" test foo() { let predicate = True trace @"foo" expect predicate } "#; let program = Term::bool(true) .delayed_if_then_else( Term::unit(), Term::Error.delayed_trace(Term::string("expect predicate")), ) .delayed_trace(Term::string("foo")); assert_uplc(src, program, false, true) } #[test] fn dangling_trace_expect_in_trace() { let src = r#" test foo() { trace @"foo" trace @"bar" expect True } "#; let program = Term::bool(true) .delayed_if_then_else( Term::unit(), Term::Error.delayed_trace(Term::string("expect True")), ) .delayed_trace(Term::string("bar")) .delayed_trace(Term::string("foo")); assert_uplc(src, program, false, true) }