diff --git a/crates/aiken-lang/src/gen_uplc/builder.rs b/crates/aiken-lang/src/gen_uplc/builder.rs index e761c527..c5ace45e 100644 --- a/crates/aiken-lang/src/gen_uplc/builder.rs +++ b/crates/aiken-lang/src/gen_uplc/builder.rs @@ -932,12 +932,8 @@ pub fn unknown_data_to_type(term: Term, field_type: &Type) -> Term { ) .lambda("__list_data") .apply(Term::unlist_data().apply(term)), - Some(UplcType::Bool) => { - Term::unwrap_bool_or(term, |result| result.delay(), &Term::Error.delay()) - } - Some(UplcType::Unit) => { - Term::unwrap_void_or(term, |result| result.delay(), &Term::Error.delay()) - } + Some(UplcType::Bool) => Term::unwrap_bool_or(term, |result| result, &Term::Error.delay()), + Some(UplcType::Unit) => Term::unwrap_void_or(term, |result| result, &Term::Error.delay()), Some(UplcType::Data) | None => term, } diff --git a/crates/aiken-project/src/tests/gen_uplc.rs b/crates/aiken-project/src/tests/gen_uplc.rs index 6fc14e1c..681b498e 100644 --- a/crates/aiken-project/src/tests/gen_uplc.rs +++ b/crates/aiken-project/src/tests/gen_uplc.rs @@ -5112,121 +5112,75 @@ fn expect_head3_cast_data_no_tail() { } "#; + 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), + ); + assert_uplc( src, - Term::var("tail_0") + 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())), + ]) .choose_list( - Term::var("expect[h,i,j]:List=a"), - Term::var("tail_1") - .choose_list( - Term::var("expect[h,i,j]:List=a"), - Term::var("tail_2") + 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())), + ])) .choose_list( - Term::var("expect[h,i,j]:List=a"), - Term::tail_list() - .apply(Term::var("tail_2")) - .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), - ) - .delay(), - Term::var("expect[h,i,j]:List=a"), - ) - .force() - .lambda("j") - .apply( - Term::var("__var") - .choose_data( - Term::var("expect[h,i,j]:List=a"), - Term::var("expect[h,i,j]:List=a"), - Term::var("expect[h,i,j]:List=a"), - Term::un_i_data().apply(Term::var("__var")).delay(), - Term::var("expect[h,i,j]:List=a"), - ) - .force() - .lambda("__var") - .apply(Term::head_list().apply(Term::var("tail_2"))), - ) - .delay(), + Term::var("expect[h]:List=a"), + then.lambda("__var").apply(todo!()).delay(), ) .force() - .lambda("tail_2") - .apply(Term::tail_list().apply(Term::var("tail_1"))) - .lambda("i") - .apply( - Term::var("__var") - .choose_data( - Term::var("expect[h,i,j]:List=a"), - Term::var("expect[h,i,j]:List=a"), - Term::var("expect[h,i,j]:List=a"), - Term::un_i_data().apply(Term::var("__var")).delay(), - Term::var("expect[h,i,j]:List=a"), - ) - .force() - .lambda("__var") - .apply(Term::head_list().apply(Term::var("tail_1"))), - ) + .lambda("h") + .apply(Term::un_i_data().apply(Term::var("__var"))) .delay(), + Term::var("expect[h]:List=a"), ) .force() - .lambda("tail_1") - .apply(Term::tail_list().apply(Term::var("tail_0"))) - .lambda("h") - .apply( - Term::var("__var") - .choose_data( - Term::var("expect[h,i,j]:List=a"), - Term::var("expect[h,i,j]:List=a"), - Term::var("expect[h,i,j]:List=a"), - Term::un_i_data().apply(Term::var("__var")).delay(), - Term::var("expect[h,i,j]:List=a"), - ) - .force() - .lambda("__var") - .apply(Term::head_list().apply(Term::var("tail_0"))), - ) - .delay(), - ) - .force() - .lambda("tail_0") - .apply( - Term::data(Data::list(vec![ - Data::integer(1.into()), - Data::integer(2.into()), - Data::integer(3.into()), - ])) - .choose_data( - Term::var("expect[h,i,j]:List=a"), - Term::var("expect[h,i,j]:List=a"), - Term::list_values(vec![ + .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,i,j]:List=a"), - Term::var("expect[h,i,j]:List=a"), - ) - .force(), ) - .lambda("expect[h,i,j]:List=a") - .apply( - Term::Error - .delayed_trace(Term::string("expect [h, i, j]: List = a")) - .delay(), - ), + .force() + .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(), + ), false, ); } diff --git a/crates/uplc/src/builder.rs b/crates/uplc/src/builder.rs index 741d9d74..6cb35dfb 100644 --- a/crates/uplc/src/builder.rs +++ b/crates/uplc/src/builder.rs @@ -421,6 +421,28 @@ impl Term { .force() } + /// Note the otherwise is expected to be a delayed term cast to a Var + pub fn delay_empty_choose_list(self, empty: Self, otherwise: Self) -> Self { + Term::Builtin(DefaultFunction::ChooseList) + .force() + .force() + .apply(self) + .apply(empty.delay()) + .apply(otherwise) + .force() + } + + /// Note the otherwise is expected to be a delayed term cast to a Var + pub fn delay_filled_choose_list(self, otherwise: Self, filled: Self) -> Self { + Term::Builtin(DefaultFunction::ChooseList) + .force() + .force() + .apply(self) + .apply(otherwise) + .apply(filled.delay()) + .force() + } + pub fn delayed_choose_unit(self, then_term: Self) -> Self { Term::Builtin(DefaultFunction::ChooseUnit) .force() @@ -438,6 +460,26 @@ impl Term { .force() } + /// Note the otherwise is expected to be a delayed term cast to a Var + pub fn delay_true_if_then_else(self, then: Self, otherwise: Self) -> Self { + Term::Builtin(DefaultFunction::IfThenElse) + .force() + .apply(self) + .apply(then.delay()) + .apply(otherwise) + .force() + } + + /// Note the otherwise is expected to be a delayed term cast to a Var + pub fn delay_false_if_then_else(self, otherwise: Self, alternative: Self) -> Self { + Term::Builtin(DefaultFunction::IfThenElse) + .force() + .apply(self) + .apply(otherwise) + .apply(alternative.delay()) + .force() + } + pub fn delayed_trace(self, msg_term: Self) -> Self { Term::Builtin(DefaultFunction::Trace) .force() @@ -596,8 +638,8 @@ impl Term { /// Convert an arbitrary 'term' into a bool term and pass it into a 'callback'. /// Continue the execution 'otherwise' with a different branch. /// - /// Note that the 'otherwise' term as well as the callback's result are expected - /// to be delayed terms. + /// Note that the 'otherwise' term is expected + /// to be a delayed term. pub fn unwrap_bool_or(self, callback: F, otherwise: &Term) -> Term where F: FnOnce(Term) -> Term, @@ -605,29 +647,30 @@ impl Term { Term::unconstr_data() .apply(self) .as_var("__pair__", |pair| { - Term::snd_pair().apply(Term::Var(pair.clone())).choose_list( - Term::less_than_equals_integer() - .apply(Term::integer(2.into())) - .apply(Term::fst_pair().apply(Term::Var(pair.clone()))) - .if_then_else( - otherwise.clone(), - callback( - Term::equals_integer() - .apply(Term::integer(1.into())) - .apply(Term::fst_pair().apply(Term::Var(pair))), + Term::snd_pair() + .apply(Term::Var(pair.clone())) + .delay_empty_choose_list( + Term::less_than_equals_integer() + .apply(Term::integer(2.into())) + .apply(Term::fst_pair().apply(Term::Var(pair.clone()))) + .delay_false_if_then_else( + otherwise.clone(), + callback( + Term::equals_integer() + .apply(Term::integer(1.into())) + .apply(Term::fst_pair().apply(Term::Var(pair))), + ), ), - ), - otherwise.clone(), - ) + otherwise.clone(), + ) }) - .force() } /// Convert an arbitrary 'term' into a unit term and pass it into a 'callback'. /// Continue the execution 'otherwise' with a different branch. /// - /// Note that the 'otherwise' term as well as the callback's result are expected - /// to be delayed terms. + /// Note that the 'otherwise' term is expected + /// to be a delayed term. pub fn unwrap_void_or(self, callback: F, otherwise: &Term) -> Term where F: FnOnce(Term) -> Term, @@ -636,63 +679,58 @@ impl Term { Term::equals_integer() .apply(Term::integer(0.into())) .apply(Term::fst_pair().apply(Term::unconstr_data().apply(self.clone()))) - .if_then_else( + .delay_true_if_then_else( Term::snd_pair() .apply(Term::unconstr_data().apply(self)) - .choose_list(callback(Term::unit()), otherwise.clone()) - .force() - .delay(), + .delay_empty_choose_list(callback(Term::unit()), otherwise.clone()), otherwise.clone(), ) - .force() } /// Convert an arbitrary 'term' into a pair and pass it into a 'callback'. /// Continue the execution 'otherwise' with a different branch. /// - /// Note that the 'otherwise' term as well as the callback's result are expected - /// to be delayed terms. + /// Note that the 'otherwise' term is expected + /// to be a delayed term. pub fn unwrap_pair_or(self, callback: F, otherwise: &Term) -> Term where F: FnOnce(Term) -> Term, { self.as_var("__list_data", |list| { let left = Term::head_list().apply(Term::Var(list.clone())); + Term::unwrap_tail_or( list, |tail| { tail.as_var("__tail", |tail| { let right = Term::head_list().apply(Term::Var(tail.clone())); + Term::unwrap_tail_or( tail, |leftovers| { - leftovers - .choose_list( - callback(Term::mk_pair_data().apply(left).apply(right)), - otherwise.clone(), - ) - .force() - .delay() + leftovers.delay_empty_choose_list( + callback(Term::mk_pair_data().apply(left).apply(right)), + otherwise.clone(), + ) }, otherwise, ) }) - .force() - .delay() }, otherwise, ) - .force() }) - .force() } /// Continue with the tail of a list, if any; or fallback 'otherwise'. + /// + /// Note that the 'otherwise' term is expected + /// to be a delayed term. fn unwrap_tail_or(var: Rc, callback: F, otherwise: &Term) -> Term where F: FnOnce(Term) -> Term, { - Term::Var(var.clone()).choose_list( + Term::Var(var.clone()).delay_filled_choose_list( otherwise.clone(), callback(Term::tail_list().apply(Term::Var(var))), ) @@ -722,7 +760,7 @@ mod tests { #[test] fn unwrap_bool_or_false() { let result = quick_eval( - Term::data(Data::constr(0, vec![])).unwrap_bool_or(|b| b.delay(), &Term::Error.delay()), + Term::data(Data::constr(0, vec![])).unwrap_bool_or(|b| b, &Term::Error.delay()), ); assert_eq!(result, Ok(Term::bool(false))); @@ -731,7 +769,7 @@ mod tests { #[test] fn unwrap_bool_or_true() { let result = quick_eval( - Term::data(Data::constr(1, vec![])).unwrap_bool_or(|b| b.delay(), &Term::Error.delay()), + Term::data(Data::constr(1, vec![])).unwrap_bool_or(|b| b, &Term::Error.delay()), ); assert_eq!(result, Ok(Term::bool(true))); @@ -741,7 +779,7 @@ mod tests { fn unwrap_bool_or_extra_args() { let result = quick_eval( Term::data(Data::constr(1, vec![Data::integer(42.into())])) - .unwrap_bool_or(|b| b.delay(), &Term::Error.delay()), + .unwrap_bool_or(|b| b, &Term::Error.delay()), ); assert_eq!(result, Err(Error::EvaluationFailure)); @@ -750,7 +788,7 @@ mod tests { #[test] fn unwrap_bool_or_invalid_constr_hi() { let result = quick_eval( - Term::data(Data::constr(2, vec![])).unwrap_bool_or(|b| b.delay(), &Term::Error.delay()), + Term::data(Data::constr(2, vec![])).unwrap_bool_or(|b| b, &Term::Error.delay()), ); assert_eq!(result, Err(Error::EvaluationFailure)); @@ -759,7 +797,7 @@ mod tests { #[test] fn unwrap_tail_or_0_elems() { let result = quick_eval(Term::list_values(vec![]).as_var("__tail", |tail| { - Term::unwrap_tail_or(tail, |p| p.delay(), &Term::Error.delay()).force() + Term::unwrap_tail_or(tail, |p| p, &Term::Error.delay()) })); assert_eq!(result, Err(Error::EvaluationFailure)); @@ -770,7 +808,7 @@ mod tests { let result = quick_eval( Term::list_values(vec![Constant::Data(Data::integer(1.into()))]) .as_var("__tail", |tail| { - Term::unwrap_tail_or(tail, |p| p.delay(), &Term::Error.delay()).force() + Term::unwrap_tail_or(tail, |p| p, &Term::Error.delay()) }), ); @@ -785,7 +823,7 @@ mod tests { Constant::Data(Data::integer(2.into())), ]) .as_var("__tail", |tail| { - Term::unwrap_tail_or(tail, |p| p.delay(), &Term::Error.delay()).force() + Term::unwrap_tail_or(tail, |p| p, &Term::Error.delay()) }), ); @@ -804,7 +842,7 @@ mod tests { Constant::Data(Data::integer(14.into())), Constant::Data(Data::bytestring(vec![1, 2, 3])), ]) - .unwrap_pair_or(|p| p.delay(), &Term::Error.delay()), + .unwrap_pair_or(|p| p, &Term::Error.delay()), ); assert_eq!( @@ -820,7 +858,7 @@ mod tests { fn unwrap_pair_or_not_enough_args_1() { let result = quick_eval( Term::list_values(vec![Constant::Data(Data::integer(1.into()))]) - .unwrap_pair_or(|p| p.delay(), &Term::Error.delay()), + .unwrap_pair_or(|p| p, &Term::Error.delay()), ); assert_eq!(result, Err(Error::EvaluationFailure)); @@ -828,9 +866,8 @@ mod tests { #[test] fn unwrap_pair_or_not_enough_args_0() { - let result = quick_eval( - Term::list_values(vec![]).unwrap_pair_or(|p| p.delay(), &Term::Error.delay()), - ); + let result = + quick_eval(Term::list_values(vec![]).unwrap_pair_or(|p| p, &Term::Error.delay())); assert_eq!(result, Err(Error::EvaluationFailure)); } @@ -843,7 +880,7 @@ mod tests { Constant::Data(Data::integer(2.into())), Constant::Data(Data::integer(3.into())), ]) - .unwrap_pair_or(|p| p.delay(), &Term::Error.delay()), + .unwrap_pair_or(|p| p, &Term::Error.delay()), ); assert_eq!(result, Err(Error::EvaluationFailure)); @@ -853,7 +890,7 @@ mod tests { fn unwrap_void_or_happy() { let result = quick_eval( Term::data(Data::constr(0, vec![])).as_var("__unit", |unit| { - Term::Var(unit).unwrap_void_or(|u| u.delay(), &Term::Error.delay()) + Term::Var(unit).unwrap_void_or(|u| u, &Term::Error.delay()) }), ); @@ -864,7 +901,7 @@ mod tests { fn unwrap_void_or_wrong_constr() { let result = quick_eval( Term::data(Data::constr(14, vec![])).as_var("__unit", |unit| { - Term::Var(unit).unwrap_void_or(|u| u.delay(), &Term::Error.delay()) + Term::Var(unit).unwrap_void_or(|u| u, &Term::Error.delay()) }), ); @@ -875,7 +912,7 @@ mod tests { fn unwrap_void_or_too_many_args() { let result = quick_eval( Term::data(Data::constr(0, vec![Data::integer(0.into())])).as_var("__unit", |unit| { - Term::Var(unit).unwrap_void_or(|u| u.delay(), &Term::Error.delay()) + Term::Var(unit).unwrap_void_or(|u| u, &Term::Error.delay()) }), );