diff --git a/crates/aiken-lang/src/gen_uplc/builder.rs b/crates/aiken-lang/src/gen_uplc/builder.rs index e57e0b11..4eb9472b 100644 --- a/crates/aiken-lang/src/gen_uplc/builder.rs +++ b/crates/aiken-lang/src/gen_uplc/builder.rs @@ -941,203 +941,79 @@ pub fn softcast_data_to_type_otherwise( ) -> Term { let uplc_type = field_type.get_uplc_type(); - let then = then.lambda(name); + let just_then = then.clone(); - match uplc_type { - Some(UplcType::Integer) => Term::var("__val") - .choose_data( - otherwise_delayed.clone(), - otherwise_delayed.clone(), - otherwise_delayed.clone(), - then.apply(Term::un_i_data().apply(Term::var("__val"))) - .delay(), - otherwise_delayed.clone(), - ) - .force() - .lambda("__val") - .apply(value), - Some(UplcType::ByteString) => Term::var("__val") - .choose_data( - otherwise_delayed.clone(), - otherwise_delayed.clone(), - otherwise_delayed.clone(), - otherwise_delayed.clone(), - then.apply(Term::un_b_data().apply(Term::var("__val"))) - .delay(), - ) - .force() - .lambda("__val") - .apply(value), - Some(UplcType::String) => Term::var("__val") - .choose_data( - otherwise_delayed.clone(), - otherwise_delayed.clone(), - otherwise_delayed.clone(), - otherwise_delayed.clone(), - then.apply(Term::decode_utf8().apply(Term::un_b_data().apply(Term::var("__val")))) - .delay(), - ) - .force() - .lambda("__val") - .apply(value), + let then_delayed = |v| then.lambda(name).apply(v).delay(); - Some(UplcType::List(_)) if field_type.is_map() => Term::var("__val") - .choose_data( - otherwise_delayed.clone(), - then.apply(Term::unmap_data().apply(Term::var("__val"))) - .delay(), - otherwise_delayed.clone(), - otherwise_delayed.clone(), - otherwise_delayed.clone(), - ) - .force() - .lambda("__val") - .apply(value), - Some(UplcType::List(_)) => Term::var("__val") - .choose_data( - otherwise_delayed.clone(), - otherwise_delayed.clone(), - then.apply(Term::unlist_data().apply(Term::var("__val"))) - .delay(), - otherwise_delayed.clone(), - otherwise_delayed.clone(), - ) - .force() - .lambda("__val") - .apply(value), + value.as_var("__val", |val| match uplc_type { + None => val + .choose_data_constr(then_delayed, &otherwise_delayed) + .force(), - Some(UplcType::Bls12_381G1Element) => Term::var("__val") - .choose_data( - otherwise_delayed.clone(), - otherwise_delayed.clone(), - otherwise_delayed.clone(), - otherwise_delayed.clone(), - then.apply( - Term::bls12_381_g1_uncompress() - .apply(Term::un_b_data().apply(Term::var("__val"))), - ) - .delay(), - ) - .force() - .lambda("__val") - .apply(value), - Some(UplcType::Bls12_381G2Element) => Term::var("__val") - .choose_data( - otherwise_delayed.clone(), - otherwise_delayed.clone(), - otherwise_delayed.clone(), - otherwise_delayed.clone(), - then.apply( - Term::bls12_381_g2_uncompress() - .apply(Term::un_b_data().apply(Term::var("__val"))), - ) - .delay(), - ) - .force() - .lambda("__val") - .apply(value), - Some(UplcType::Bls12_381MlResult) => panic!("ML Result not supported"), - Some(UplcType::Pair(_, _)) => Term::var("__val") - .choose_data( - otherwise_delayed.clone(), - otherwise_delayed.clone(), - Term::var("__list_data") - .choose_list( - otherwise_delayed.clone(), - Term::var("__tail") - .choose_list( - otherwise_delayed.clone(), - Term::tail_list() - .apply(Term::var("__tail")) - .choose_list( - then.apply( - Term::mk_pair_data() - .apply( - Term::head_list() - .apply(Term::var("__list_data")), - ) - .apply( - Term::head_list().apply(Term::var("__tail")), - ), - ) - .delay(), - otherwise_delayed.clone(), - ) - .force() - .delay(), - ) - .force() - .lambda("__tail") - .apply(Term::tail_list().apply(Term::var("__list_data"))) - .delay(), - ) - .force() - .lambda("__list_data") - .apply(Term::unlist_data().apply(Term::var("__val"))) - .delay(), - otherwise_delayed.clone(), - otherwise_delayed.clone(), - ) - .force() - .lambda("__val") - .apply(value), - Some(UplcType::Bool) => Term::var("__val") - .choose_data( - Term::unwrap_bool_or( - Term::var("__val"), - |result| then.apply(result), - &otherwise_delayed, - ) - .delay(), - otherwise_delayed.clone(), - otherwise_delayed.clone(), - otherwise_delayed.clone(), - otherwise_delayed.clone(), - ) - .force() - .lambda("__val") - .apply(value), - Some(UplcType::Unit) => Term::var("__val") - .choose_data( - Term::equals_integer() - .apply(Term::integer(0.into())) - .apply(Term::fst_pair().apply(Term::unconstr_data().apply(Term::var("__val")))) - .if_then_else( - Term::snd_pair() - .apply(Term::unconstr_data().apply(Term::var("__val"))) - .choose_list( - then.apply(Term::unit()).delay(), - otherwise_delayed.clone(), - ) - .force() - .delay(), - otherwise_delayed.clone(), - ) - .force() - .delay(), - otherwise_delayed.clone(), - otherwise_delayed.clone(), - otherwise_delayed.clone(), - otherwise_delayed.clone(), - ) - .force() - .lambda("__val") - .apply(value), + Some(UplcType::Data) => just_then, - Some(UplcType::Data) => then.apply(value), - // constr type - None => Term::var("__val") - .choose_data( - then.apply(Term::var("__val")).delay(), - otherwise_delayed.clone(), - otherwise_delayed.clone(), - otherwise_delayed.clone(), - otherwise_delayed.clone(), + Some(UplcType::Bls12_381MlResult) => { + unreachable!("attempted to cast Data into Bls12_381MlResult ?!") + } + + Some(UplcType::Integer) => val + .choose_data_integer(then_delayed, &otherwise_delayed) + .force(), + + Some(UplcType::ByteString) => val + .choose_data_bytearray(then_delayed, &otherwise_delayed) + .force(), + + Some(UplcType::String) => val + .choose_data_bytearray( + |bytes| then_delayed(Term::decode_utf8().apply(bytes)), + &otherwise_delayed, ) - .force() - .lambda("__val") - .apply(value), - } + .force(), + + Some(UplcType::List(_)) if field_type.is_map() => val + .choose_data_map(then_delayed, &otherwise_delayed) + .force(), + + Some(UplcType::List(_)) => val + .choose_data_list(then_delayed, &otherwise_delayed) + .force(), + + Some(UplcType::Bls12_381G1Element) => val + .choose_data_bytearray( + |bytes| then_delayed(Term::bls12_381_g1_uncompress().apply(bytes)), + &otherwise_delayed, + ) + .force(), + + Some(UplcType::Bls12_381G2Element) => val + .choose_data_bytearray( + |bytes| then_delayed(Term::bls12_381_g2_uncompress().apply(bytes)), + &otherwise_delayed, + ) + .force(), + + Some(UplcType::Pair(_, _)) => val + .choose_data_list( + |list| list.unwrap_pair_or(then_delayed, &otherwise_delayed), + &otherwise_delayed, + ) + .force(), + + Some(UplcType::Bool) => val + .choose_data_constr( + |constr| constr.unwrap_bool_or(then_delayed, &otherwise_delayed), + &otherwise_delayed, + ) + .force(), + + Some(UplcType::Unit) => val + .choose_data_constr( + |constr| constr.unwrap_void_or(then_delayed, &otherwise_delayed), + &otherwise_delayed, + ) + .force(), + }) } pub fn convert_constants_to_data(constants: Vec>) -> Vec { diff --git a/crates/uplc/src/builder.rs b/crates/uplc/src/builder.rs index f0f330ab..aedcd665 100644 --- a/crates/uplc/src/builder.rs +++ b/crates/uplc/src/builder.rs @@ -485,34 +485,190 @@ impl Term { ) } + /// Introduce a let-binding for a given term. The callback receives a Term::Var + /// whose name matches the given 'var_name'. Handy to re-use a same var across + /// multiple lambda expressions. + /// + /// ## Example + /// + /// ``` + /// value.as_var("__val", |val| { + /// val.do_something() + /// .do_another_thing() + /// }) + /// ``` + pub fn as_var(self, var_name: &str, callback: F) -> Term + where + F: FnOnce(Term) -> Term, + { + callback(Term::var(var_name)).lambda(var_name).apply(self) + } + + /// Continue a computation provided that the current term is a Data-wrapped integer. + /// The 'callback' receives an integer constant Term as argument. + pub fn choose_data_integer(self, callback: F, otherwise: &Term) -> Self + where + F: FnOnce(Term) -> Term, + { + self.clone().choose_data( + otherwise.clone(), + otherwise.clone(), + otherwise.clone(), + callback(Term::un_i_data().apply(self)), + otherwise.clone(), + ) + } + + /// Continue a computation provided that the current term is a Data-wrapped + /// bytearray. The 'callback' receives a bytearray constant Term as argument. + pub fn choose_data_bytearray(self, callback: F, otherwise: &Term) -> Self + where + F: FnOnce(Term) -> Term, + { + self.clone().choose_data( + otherwise.clone(), + otherwise.clone(), + otherwise.clone(), + otherwise.clone(), + callback(Term::un_b_data().apply(self)), + ) + } + + /// Continue a computation provided that the current term is a Data-wrapped + /// list. The 'callback' receives a ProtoList Term as argument. + pub fn choose_data_list(self, callback: F, otherwise: &Term) -> Self + where + F: FnOnce(Term) -> Term, + { + self.clone().choose_data( + otherwise.clone(), + otherwise.clone(), + callback(Term::unlist_data().apply(self)), + otherwise.clone(), + otherwise.clone(), + ) + } + + /// Continue a computation provided that the current term is a Data-wrapped + /// list. The 'callback' receives a ProtoMap Term as argument. + pub fn choose_data_map(self, callback: F, otherwise: &Term) -> Self + where + F: FnOnce(Term) -> Term, + { + self.clone().choose_data( + otherwise.clone(), + callback(Term::unmap_data().apply(self)), + otherwise.clone(), + otherwise.clone(), + otherwise.clone(), + ) + } + + /// Continue a computation provided that the current term is a Data-wrapped + /// constr. The 'callback' receives a Data as argument. + pub fn choose_data_constr(self, callback: F, otherwise: &Term) -> Self + where + F: FnOnce(Term) -> Term, + { + self.clone().choose_data( + callback(self), + otherwise.clone(), + otherwise.clone(), + otherwise.clone(), + otherwise.clone(), + ) + } + /// 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. - pub fn unwrap_bool_or(term: Term, callback: F, otherwise: &Term) -> Term + pub fn unwrap_bool_or(self, callback: F, otherwise: &Term) -> Term where F: FnOnce(Term) -> Term, { - Term::snd_pair() - .apply(Term::var("__pair__")) - .choose_list( - Term::less_than_equals_integer() - .apply(Term::integer(2.into())) - .apply(Term::fst_pair().apply(Term::var("__pair__"))) - .delayed_if_then_else( + Term::unconstr_data() + .apply(self) + .as_var("__pair__", |pair| { + Term::snd_pair() + .apply(pair.clone()) + .choose_list( + Term::less_than_equals_integer() + .apply(Term::integer(2.into())) + .apply(Term::fst_pair().apply(pair.clone())) + .if_then_else( + otherwise.clone(), + callback( + Term::equals_integer() + .apply(Term::fst_pair().apply(pair)) + .apply(Term::integer(1.into())), + ), + ), otherwise.clone(), - callback( - Term::equals_integer() - .apply(Term::fst_pair().apply(Term::var("__pair__"))) - .apply(Term::integer(1.into())), - ), ) + .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. + pub fn unwrap_void_or(self, callback: F, otherwise: &Term) -> Term + where + F: FnOnce(Term) -> Term, + { + Term::equals_integer() + .apply(Term::integer(0.into())) + .apply(Term::fst_pair().apply(Term::unconstr_data().apply(self.clone()))) + .if_then_else( + Term::snd_pair() + .apply(Term::unconstr_data().apply(self)) + .choose_list(callback(Term::unit()), otherwise.clone()) + .force() .delay(), otherwise.clone(), ) .force() - .lambda("__pair__") - .apply(Term::unconstr_data().apply(term)) + } + + /// 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. + 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(list.clone()); + list.unwrap_tail_or( + |tail| { + tail.as_var("__tail", |tail| { + let right = Term::head_list().apply(tail.clone()); + tail.unwrap_tail_or( + |_| otherwise.clone(), + &callback(Term::mk_pair_data().apply(left).apply(right)), + ) + }) + }, + otherwise, + ) + }) + } + + /// Continue with the tail of a list, if any; or fallback 'otherwise'. + /// + /// Note that the 'otherwise' term as well as the callback's result are expected + /// to be delayed terms. + pub fn unwrap_tail_or(self, callback: F, otherwise: &Term) -> Term + where + F: FnOnce(Term) -> Term, + { + self.clone() + .choose_list(otherwise.clone(), callback(Term::tail_list().apply(self))) } }