Start work on revamping assignment so we can handle soft casting properly

This commit is contained in:
microproofs 2024-07-28 16:14:41 -04:00 committed by Kasey
parent 9ea54afd12
commit dd5badd884
4 changed files with 529 additions and 268 deletions

View File

@ -38,6 +38,7 @@ use crate::{
},
IdGenerator,
};
use builder::unknown_data_to_type;
use indexmap::{IndexMap, IndexSet};
use itertools::Itertools;
use petgraph::{algo, Graph};
@ -271,17 +272,16 @@ impl<'a> CodeGenerator<'a> {
let msg_func_name = msg.split_whitespace().join("");
if msg_func_name.is_empty() {
None
} else {
self.special_functions.insert_new_function(
msg_func_name.clone(),
if msg.is_empty() {
Term::Error.delay()
} else {
Term::Error.delayed_trace(Term::string(msg)).delay()
},
Term::Error.delayed_trace(Term::string(msg)).delay(),
void(),
);
self.special_functions.use_function_tree(msg_func_name)
Some(self.special_functions.use_function_tree(msg_func_name))
}
};
let (then, context) = context.split_first().unwrap();
@ -569,7 +569,7 @@ impl<'a> CodeGenerator<'a> {
kind: AssignmentKind::let_(),
remove_unused: false,
full_check: false,
otherwise: AirTree::error(void(), false),
otherwise: None,
},
)
} else {
@ -625,12 +625,14 @@ impl<'a> CodeGenerator<'a> {
//
// if <expr:condition> is <pattern>: <annotation> { <expr:body> }
// [(builtin ifThenElse) (condition is pattern) (body) (else) ]
TypedExpr::If {
a @ TypedExpr::If {
branches,
final_else,
tipo,
..
} => branches.iter().rfold(
} => {
println!("A: {:#?}", a);
branches.iter().rfold(
self.build(final_else, module_build_name, &[]),
|acc, branch| {
let condition = self.build(&branch.condition, module_build_name, &[]);
@ -638,6 +640,8 @@ impl<'a> CodeGenerator<'a> {
match &branch.is {
Some((pattern, tipo)) => {
println!("PATTERN TYPE: {:#?}", pattern);
println!("Branch TYPE: {:#?}", tipo);
AirTree::let_assignment(
"acc_var",
// use anon function as a delay to avoid evaluating the acc
@ -660,8 +664,8 @@ impl<'a> CodeGenerator<'a> {
None => AirTree::if_branch(tipo.clone(), condition, body, acc),
}
},
),
)
}
TypedExpr::RecordAccess {
tipo,
index,
@ -890,7 +894,7 @@ impl<'a> CodeGenerator<'a> {
pub fn assignment(
&mut self,
pattern: &TypedPattern,
mut value: AirTree,
value: AirTree,
then: AirTree,
tipo: &Rc<Type>,
props: AssignmentProperties,
@ -906,41 +910,90 @@ impl<'a> CodeGenerator<'a> {
);
// Cast value to or from data so we don't have to worry from this point onward
let assign_casted_value = |name, value, then| {
if props.value_type.is_data() && props.kind.is_expect() && !tipo.is_data() {
value = AirTree::cast_from_data(value, tipo.clone(), props.otherwise.clone(), true);
} else if !props.value_type.is_data() && tipo.is_data() {
value = AirTree::cast_to_data(value, props.value_type.clone());
if props.otherwise.is_some() {
AirTree::soft_cast_assignment(
name,
tipo.clone(),
value,
then,
props.otherwise.as_ref().unwrap().clone(),
)
} else {
AirTree::let_assignment(
name,
AirTree::cast_from_data(value, tipo.clone(), true),
then,
)
}
} else if !props.value_type.is_data() && tipo.is_data() {
AirTree::let_assignment(
name,
AirTree::cast_to_data(value, props.value_type.clone()),
then,
)
} else {
AirTree::let_assignment(name, value, then)
}
};
let otherwise = match &props.otherwise {
Some(x) => x.clone(),
// (delay (error ))
None => AirTree::anon_func(vec![], AirTree::error(void(), false), true),
};
match pattern {
Pattern::Int {
value: expected_int,
location,
..
} => AirTree::assign_literal_pattern(
format!(
} => {
let name = format!(
"__expected_by_{}_span_{}_{}",
expected_int, location.start, location.end
),
);
let expect = AirTree::binop(
BinOp::Eq,
bool(),
AirTree::int(expected_int),
value,
AirTree::local_var(&name, int()),
int(),
props,
then,
),
);
assign_casted_value(
name,
value,
AirTree::assert_bool(true, expect, then, otherwise),
)
}
Pattern::ByteArray {
location,
value: expected_bytes,
location,
..
} => AirTree::assign_literal_pattern(
format!("__expected_bytes_span_{}_{}", location.start, location.end),
AirTree::byte_array(expected_bytes.clone()),
} => {
let name = format!(
"__expected_bytes_span_{}_{}",
location.start, location.end
);
let expect = AirTree::binop(
BinOp::Eq,
bool(),
AirTree::byte_array(expected_bytes),
AirTree::local_var(&name, byte_string()),
byte_string(),
);
assign_casted_value(
name,
value,
byte_array(),
props,
then,
),
AirTree::assert_bool(true, expect, then, otherwise),
)
}
Pattern::Var { name, .. } => {
if props.full_check {
@ -951,26 +1004,28 @@ impl<'a> CodeGenerator<'a> {
let val = AirTree::local_var(name, tipo.clone());
if non_opaque_tipo.is_primitive() {
AirTree::let_assignment(name, value, then)
assign_casted_value(name.clone(), value, then)
} else {
let expect = self.expect_type_assign(
assign_casted_value(
name.clone(),
value,
self.expect_type_assign(
&non_opaque_tipo,
val,
&mut index_map,
pattern.location(),
props.otherwise,
);
let assign_expect = AirTree::let_assignment("_", expect, then);
AirTree::let_assignment(name, value, assign_expect)
then,
otherwise,
),
)
}
} else {
AirTree::let_assignment(name, value, then)
assign_casted_value(name.clone(), value, then)
}
}
Pattern::Assign { name, pattern, .. } => {
// Don't need any data casting for Assign
let inner_pattern = self.assignment(
pattern,
AirTree::local_var(name, tipo.clone()),
@ -983,30 +1038,31 @@ impl<'a> CodeGenerator<'a> {
Pattern::Discard { name, .. } => {
if props.full_check {
let name = &format!("__discard_expect_{}", name);
let name = format!("__discard_expect_{}", name);
let mut index_map = IndexMap::new();
let non_opaque_tipo = convert_opaque_type(tipo, &self.data_types, true);
let val = AirTree::local_var(name, tipo.clone());
let val = AirTree::local_var(&name, tipo.clone());
if non_opaque_tipo.is_primitive() {
AirTree::let_assignment(name, value, then)
assign_casted_value(name, value, then)
} else {
let expect = self.expect_type_assign(
assign_casted_value(
name,
value,
self.expect_type_assign(
&non_opaque_tipo,
val,
&mut index_map,
pattern.location(),
props.otherwise,
);
let assignment = AirTree::let_assignment("_", expect, then);
AirTree::let_assignment(name, value, assignment)
then,
otherwise,
),
)
}
} else if !props.remove_unused {
AirTree::let_assignment(name, value, then)
assign_casted_value(name.clone(), value, then)
} else {
then
}
@ -1030,6 +1086,7 @@ impl<'a> CodeGenerator<'a> {
Some(tail) => {
let tail_name = match tail.as_ref() {
Pattern::Var { name, .. } => name.to_string(),
// This Pattern one doesn't even make sense
Pattern::Assign { name, .. } => name.to_string(),
Pattern::Discard { name, .. } => {
if props.full_check {
@ -1038,6 +1095,7 @@ impl<'a> CodeGenerator<'a> {
"_".to_string()
}
}
// This should be unreachable
_ => format!(
"tail_span_{}_{}",
tail.location().start,
@ -1056,6 +1114,9 @@ impl<'a> CodeGenerator<'a> {
AssignmentProperties {
value_type: tipo.clone(),
kind: props.kind,
// The reason the top level of recursion might have remove_unused
// false is to deal with expect _ = thing
// next_thing
remove_unused: true,
full_check: props.full_check,
otherwise: props.otherwise.clone(),
@ -1120,21 +1181,37 @@ impl<'a> CodeGenerator<'a> {
elems.reverse();
let name = format!(
"__List_span_{}_{}",
pattern.location().start,
pattern.location().end
);
let casted_var = AirTree::local_var(&name, tipo.clone());
if elements.is_empty() {
AirTree::list_empty(value, then, props.otherwise.clone())
assign_casted_value(
name,
value,
AirTree::list_empty(casted_var, then, otherwise),
)
} else {
assign_casted_value(
name,
value,
AirTree::list_access(
elems,
tipo.clone(),
tail.is_some(),
value,
casted_var,
if props.full_check {
ExpectLevel::Full
} else {
ExpectLevel::Items
},
then,
props.otherwise.clone(),
otherwise,
),
)
}
}
@ -1214,8 +1291,7 @@ impl<'a> CodeGenerator<'a> {
// This `value` is either value param that was passed in or
// local var
let constructor_name = format!(
"__constructor_{}_span_{}_{}",
"Pair",
"Pair_span_{}_{}",
pattern.location().start,
pattern.location().end
);
@ -1238,11 +1314,11 @@ impl<'a> CodeGenerator<'a> {
local_value,
props.full_check,
then,
props.otherwise.clone(),
otherwise,
)
};
AirTree::let_assignment(constructor_name, value, then)
assign_casted_value(constructor_name, value, then)
}
Pattern::Constructor {
@ -1251,13 +1327,25 @@ impl<'a> CodeGenerator<'a> {
} if tipo.is_bool() => {
assert!(props.kind.is_expect());
AirTree::assert_bool(name == "True", value, then, props.otherwise.clone())
let name_var = format!(
"__Bool_{}_{}",
pattern.location().start,
pattern.location().end
);
let local_var = AirTree::local_var(&name_var, tipo.clone());
assign_casted_value(
name_var,
value,
AirTree::assert_bool(name == "True", local_var, then, otherwise),
)
}
Pattern::Constructor { .. } if tipo.is_void() => {
// Void type is checked when casting from data
// So we just assign the value and move on
AirTree::let_assignment("_", value, then)
assign_casted_value("_".to_string(), value, then)
}
Pattern::Constructor {
@ -1376,7 +1464,7 @@ impl<'a> CodeGenerator<'a> {
local_value,
props.full_check,
then,
props.otherwise.clone(),
otherwise.clone(),
)
};
@ -1404,7 +1492,7 @@ impl<'a> CodeGenerator<'a> {
index,
AirTree::local_var(&subject_name, tipo.clone()),
then,
props.otherwise.clone(),
otherwise,
),
)
} else {
@ -1415,7 +1503,7 @@ impl<'a> CodeGenerator<'a> {
then
};
AirTree::let_assignment(constructor_name, value, then)
assign_casted_value(constructor_name, value, then)
}
Pattern::Tuple {
@ -1487,13 +1575,25 @@ impl<'a> CodeGenerator<'a> {
// This `value` is either value param that was passed in or local var
let name = format!(
"__Tuple_span_{}_{}",
pattern.location().start,
pattern.location().end
);
let local_var = AirTree::local_var(&name, tipo.clone());
assign_casted_value(
name,
value,
AirTree::tuple_access(
fields,
tipo.clone(),
value,
local_var,
props.full_check,
then,
props.otherwise.clone(),
otherwise,
),
)
}
}
@ -1505,6 +1605,7 @@ impl<'a> CodeGenerator<'a> {
value: AirTree,
defined_data_types: &mut IndexMap<String, u64>,
location: Span,
then: AirTree,
otherwise: AirTree,
) -> AirTree {
assert!(tipo.get_generic().is_none());
@ -1621,9 +1722,11 @@ impl<'a> CodeGenerator<'a> {
let mut tuple_expect_items = vec![];
let then = tuple_inner_types.iter().enumerate().rfold(
AirTree::void(),
|then, (index, arg)| {
let then =
tuple_inner_types
.iter()
.enumerate()
.rfold(then, |then, (index, arg)| {
let tuple_index_name = format!(
"__tuple_index_{}_span_{}_{}",
index, location.start, location.end
@ -1634,14 +1737,14 @@ impl<'a> CodeGenerator<'a> {
AirTree::local_var(&tuple_index_name, arg.clone()),
defined_data_types,
location,
then,
otherwise.clone(),
);
tuple_expect_items.push(tuple_index_name);
AirTree::let_assignment("_", expect_tuple_item, then)
},
);
expect_tuple_item
});
tuple_expect_items.reverse();
@ -1663,11 +1766,53 @@ impl<'a> CodeGenerator<'a> {
let inner_list_type = &tipo.get_inner_types()[0];
if inner_list_type.is_data() {
value
then
} else {
let list_name = format!("__list_span_{}_{}", location.start, location.end);
let item_name = format!("__item_span_{}_{}", location.start, location.end);
let g = AirTree::anon_func(
vec!["__list".to_string(), "__curried_expect_on_list".to_string()],
AirTree::list_empty(
AirTree::local_var("__list", tipo.clone()),
then,
AirTree::let_assignment(
&item_name,
AirTree::builtin(
DefaultFunction::HeadList,
data(),
vec![AirTree::local_var("__list", tipo.clone())],
),
AirTree::let_assignment(
&item_name,
self.expect_type_assign(
inner_list_type,
AirTree::cast_from_data(
AirTree::local_var(item_name, data()),
inner_list_type.clone(),
otherwise.clone(),
true,
),
defined_data_types,
location,
AirTree::void(),
otherwise,
),
AirTree::call(
AirTree::local_var("__curried_expect_on_list", void()),
void(),
vec![AirTree::builtin(
DefaultFunction::TailList,
list(data()),
vec![AirTree::local_var("__list", tipo.clone())],
)],
),
),
),
),
false,
);
let expect_item = self.expect_type_assign(
inner_list_type,
AirTree::cast_from_data(
@ -4051,7 +4196,7 @@ impl<'a> CodeGenerator<'a> {
if otherwise == Term::Error.delay() {
builder::unknown_data_to_type(term, tipo)
} else {
builder::unknown_data_to_type_otherwise(term, tipo, otherwise)
builder::softcast_data_to_type_otherwise(term, tipo, otherwise)
}
};
@ -4766,14 +4911,8 @@ impl<'a> CodeGenerator<'a> {
Air::CastFromData { tipo, full_cast } => {
let mut term = arg_stack.pop().unwrap();
let otherwise = if full_cast {
arg_stack.pop().unwrap()
} else {
Term::Error.delay()
};
term = if full_cast {
convert_data_to_type(term, &tipo, otherwise)
unknown_data_to_type(term, &tipo)
} else {
known_data_to_type(term, &tipo)
};

View File

@ -101,6 +101,10 @@ pub enum Air {
Let {
name: String,
},
SoftCastLet {
name: String,
tipo: Rc<Type>,
},
CastFromData {
tipo: Rc<Type>,
full_cast: bool,

View File

@ -70,7 +70,7 @@ pub struct AssignmentProperties {
pub kind: TypedAssignmentKind,
pub remove_unused: bool,
pub full_check: bool,
pub otherwise: AirTree,
pub otherwise: Option<AirTree>,
}
#[derive(Clone, Debug)]
@ -948,9 +948,11 @@ pub fn unknown_data_to_type(term: Term<Name>, field_type: &Type) -> Term<Name> {
/// Due to the nature of the types BLS12_381_G1Element and BLS12_381_G2Element and String coming from bytearray
/// We don't have error handling if the bytearray is not properly aligned to the type. Oh well lol
/// For BLS12_381_G1Element and BLS12_381_G2Element, hash to group exists so just adopt that.
pub fn unknown_data_to_type_otherwise(
term: Term<Name>,
pub fn softcast_data_to_type_otherwise(
value: Term<Name>,
name: &String,
field_type: &Type,
then: Term<Name>,
otherwise_delayed: Term<Name>,
) -> Term<Name> {
let uplc_type = field_type.get_uplc_type();
@ -961,59 +963,67 @@ pub fn unknown_data_to_type_otherwise(
otherwise_delayed.clone(),
otherwise_delayed.clone(),
otherwise_delayed.clone(),
Term::un_i_data().apply(Term::var("__val")).delay(),
then.lambda(name)
.apply(Term::un_i_data().apply(Term::var("__val")))
.delay(),
otherwise_delayed.clone(),
)
.force()
.lambda("__val")
.apply(term),
.apply(value),
Some(UplcType::ByteString) => Term::var("__val")
.choose_data(
otherwise_delayed.clone(),
otherwise_delayed.clone(),
otherwise_delayed.clone(),
otherwise_delayed.clone(),
Term::un_b_data().apply(Term::var("__val")).delay(),
then.lambda(name)
.apply(Term::un_b_data().apply(Term::var("__val")))
.delay(),
)
.force()
.lambda("__val")
.apply(term),
.apply(value),
Some(UplcType::String) => Term::var("__val")
.choose_data(
otherwise_delayed.clone(),
otherwise_delayed.clone(),
otherwise_delayed.clone(),
otherwise_delayed.clone(),
Term::decode_utf8()
.apply(Term::un_b_data().apply(Term::var("__val")))
then.lambda(name)
.apply(Term::decode_utf8().apply(Term::un_b_data().apply(Term::var("__val"))))
.delay(),
)
.force()
.lambda("__val")
.apply(term),
.apply(value),
Some(UplcType::List(_)) if field_type.is_map() => Term::var("__val")
.choose_data(
otherwise_delayed.clone(),
Term::unmap_data().apply(Term::var("__val")).delay(),
then.lambda(name)
.apply(Term::unmap_data().apply(Term::var("__val")))
.delay(),
otherwise_delayed.clone(),
otherwise_delayed.clone(),
otherwise_delayed.clone(),
)
.force()
.lambda("__val")
.apply(term),
.apply(value),
Some(UplcType::List(_)) => Term::var("__val")
.choose_data(
otherwise_delayed.clone(),
otherwise_delayed.clone(),
Term::unlist_data().apply(Term::var("__val")).delay(),
then.lambda(name)
.apply(Term::unlist_data().apply(Term::var("__val")))
.delay(),
otherwise_delayed.clone(),
otherwise_delayed.clone(),
)
.force()
.lambda("__val")
.apply(term),
.apply(value),
Some(UplcType::Bls12_381G1Element) => Term::var("__val")
.choose_data(
@ -1021,26 +1031,32 @@ pub fn unknown_data_to_type_otherwise(
otherwise_delayed.clone(),
otherwise_delayed.clone(),
otherwise_delayed.clone(),
then.lambda(name)
.apply(
Term::bls12_381_g1_uncompress()
.apply(Term::un_b_data().apply(Term::var("__val")))
.apply(Term::un_b_data().apply(Term::var("__val"))),
)
.delay(),
)
.force()
.lambda("__val")
.apply(term),
.apply(value),
Some(UplcType::Bls12_381G2Element) => Term::var("__val")
.choose_data(
otherwise_delayed.clone(),
otherwise_delayed.clone(),
otherwise_delayed.clone(),
otherwise_delayed.clone(),
then.lambda(name)
.apply(
Term::bls12_381_g2_uncompress()
.apply(Term::un_b_data().apply(Term::var("__val")))
.apply(Term::un_b_data().apply(Term::var("__val"))),
)
.delay(),
)
.force()
.lambda("__val")
.apply(term),
.apply(value),
Some(UplcType::Bls12_381MlResult) => panic!("ML Result not supported"),
Some(UplcType::Pair(_, _)) => Term::var("__val")
.choose_data(
@ -1055,11 +1071,18 @@ pub fn unknown_data_to_type_otherwise(
Term::tail_list()
.apply(Term::var("__tail"))
.choose_list(
then.lambda(name)
.apply(
Term::mk_pair_data()
.apply(
Term::head_list().apply(Term::var("__list_data")),
Term::head_list()
.apply(Term::var("__list_data")),
)
.apply(
Term::head_list()
.apply(Term::var("__tail")),
),
)
.apply(Term::head_list().apply(Term::var("__tail")))
.delay(),
otherwise_delayed.clone(),
)
@ -1080,25 +1103,22 @@ pub fn unknown_data_to_type_otherwise(
)
.force()
.lambda("__val")
.apply(term),
.apply(value),
Some(UplcType::Bool) => Term::var("__val")
.choose_data(
Term::snd_pair()
.apply(Term::var("__pair__"))
.choose_list(
Term::equals_integer()
.apply(Term::integer(1.into()))
Term::less_than_equals_integer()
.apply(Term::integer(2.into()))
.apply(Term::fst_pair().apply(Term::var("__pair__")))
.delayed_if_then_else(
Term::bool(true),
Term::equals_integer()
.apply(Term::integer(0.into()))
.apply(Term::fst_pair().apply(Term::var("__pair__")))
.if_then_else(
Term::bool(false).delay(),
otherwise_delayed.clone(),
)
.force(),
then.lambda(name).apply(
Term::equals_integer()
.apply(Term::fst_pair().apply(Term::var("__pair__")))
.apply(Term::integer(1.into())),
),
)
.delay(),
otherwise_delayed.clone(),
@ -1114,7 +1134,7 @@ pub fn unknown_data_to_type_otherwise(
)
.force()
.lambda("__val")
.apply(term),
.apply(value),
Some(UplcType::Unit) => Term::var("__val")
.choose_data(
Term::equals_integer()
@ -1123,7 +1143,10 @@ pub fn unknown_data_to_type_otherwise(
.if_then_else(
Term::snd_pair()
.apply(Term::unconstr_data().apply(Term::var("__val")))
.choose_list(Term::unit().delay(), otherwise_delayed.clone())
.choose_list(
then.lambda(name).apply(Term::unit()).delay(),
otherwise_delayed.clone(),
)
.force()
.delay(),
otherwise_delayed.clone(),
@ -1137,13 +1160,13 @@ pub fn unknown_data_to_type_otherwise(
)
.force()
.lambda("__val")
.apply(term),
.apply(value),
Some(UplcType::Data) => term,
Some(UplcType::Data) => then.lambda(name).apply(value),
// constr type
None => Term::var("__val")
.choose_data(
Term::var("__val").delay(),
then.lambda(name).apply(Term::var("__val")).delay(),
otherwise_delayed.clone(),
otherwise_delayed.clone(),
otherwise_delayed.clone(),
@ -1151,7 +1174,7 @@ pub fn unknown_data_to_type_otherwise(
)
.force()
.lambda("__val")
.apply(term),
.apply(value),
}
}
@ -1354,30 +1377,32 @@ pub fn list_access_to_uplc(
let tail_name = |id| format!("tail_id_{}", id);
let head_item = |name, tipo: &Rc<Type>, tail_name: &str| {
let head_item = |name, tipo: &Rc<Type>, tail_name: &str, then: Term<Name>| {
if name == "_" {
Term::unit()
then
} else if tipo.is_pair() && is_list_accessor {
Term::head_list().apply(Term::var(tail_name.to_string()))
} else if matches!(expect_level, ExpectLevel::Full) {
// Expect level is full so we have an unknown piece of data to cast
if otherwise_delayed == Term::Error.delay() {
unknown_data_to_type(
then.lambda(name).apply(unknown_data_to_type(
Term::head_list().apply(Term::var(tail_name.to_string())),
&tipo.to_owned(),
)
))
} else {
unknown_data_to_type_otherwise(
softcast_data_to_type_otherwise(
Term::head_list().apply(Term::var(tail_name.to_string())),
name,
&tipo.to_owned(),
then,
otherwise_delayed.clone(),
)
}
} else {
known_data_to_type(
then.lambda(name).apply(known_data_to_type(
Term::head_list().apply(Term::var(tail_name.to_string())),
&tipo.to_owned(),
)
))
}
};
@ -1399,44 +1424,52 @@ pub fn list_access_to_uplc(
// case for no tail, but last item
let tail_name = tail_name(id);
let head_item = head_item(name, tipo, &tail_name);
// let head_item = head_item(name, tipo, &tail_name);
match expect_level {
ExpectLevel::None => acc.lambda(name).apply(head_item).lambda(tail_name),
ExpectLevel::None => {
head_item(name, tipo, &tail_name, acc).lambda(tail_name)
}
ExpectLevel::Full | ExpectLevel::Items => {
if otherwise_delayed == Term::Error.delay() && tail_present {
// No need to check last item if tail was present
acc.lambda(name).apply(head_item).lambda(tail_name)
head_item(name, tipo, &tail_name, acc).lambda(tail_name)
} else if tail_present {
// Custom error instead of trying to do head_item on a possibly empty list.
Term::var(tail_name.to_string())
.choose_list(
otherwise_delayed.clone(),
acc.lambda(name).apply(head_item).delay(),
head_item(name, tipo, &tail_name, acc).delay(),
)
.force()
.lambda(tail_name)
} else if otherwise_delayed == Term::Error.delay() {
// Check head is last item in this list
head_item(
name,
tipo,
&tail_name,
Term::tail_list()
.apply(Term::var(tail_name.to_string()))
.choose_list(acc.delay(), Term::Error.delay())
.force()
.lambda(name)
.apply(head_item)
.force(),
)
.lambda(tail_name)
} else {
// Custom error if list is not empty after this head
head_item(
name,
tipo,
&tail_name,
Term::var(tail_name.to_string())
.choose_list(
otherwise_delayed.clone(),
Term::tail_list()
.apply(Term::var(tail_name.to_string()))
.choose_list(acc.delay(), otherwise_delayed.clone())
.force()
.lambda(name)
.apply(head_item)
.force(),
)
.delay(),
)
.force()
@ -1450,26 +1483,33 @@ pub fn list_access_to_uplc(
// case for every item except the last item
let tail_name = tail_name(id);
let head_item = head_item(name, tipo, &tail_name);
// let head_item = head_item(name, tipo, &tail_name);
if matches!(expect_level, ExpectLevel::None)
|| otherwise_delayed == Term::Error.delay()
{
acc.apply(Term::tail_list().apply(Term::var(tail_name.to_string())))
.lambda(name)
.apply(head_item)
head_item(
name,
tipo,
&tail_name,
acc.apply(Term::tail_list().apply(Term::var(tail_name.to_string()))),
)
.lambda(tail_name)
} else {
// case for a custom error if the list is empty at this point
Term::var(tail_name.to_string())
.choose_list(
otherwise_delayed.clone(),
head_item(
name,
tipo,
&tail_name,
acc.apply(
Term::tail_list().apply(Term::var(tail_name.to_string())),
)
.lambda(name)
.apply(head_item)
.delay(),
),
)
.force()
.lambda(tail_name)

View File

@ -125,6 +125,13 @@ pub enum AirTree {
value: Box<AirTree>,
then: Box<AirTree>,
},
SoftCastLet {
name: String,
tipo: Rc<Type>,
value: Box<AirTree>,
then: Box<AirTree>,
otherwise: Box<AirTree>,
},
DefineFunc {
func_name: String,
module_name: String,
@ -312,7 +319,6 @@ pub enum AirTree {
CastFromData {
tipo: Rc<Type>,
value: Box<AirTree>,
otherwise: Box<AirTree>,
full_cast: bool,
},
CastToData {
@ -589,39 +595,26 @@ impl AirTree {
}
}
pub fn assign_literal_pattern(
name: String,
pattern: AirTree,
rhs: AirTree,
pub fn soft_cast_assignment(
name: impl ToString,
tipo: Rc<Type>,
props: AssignmentProperties,
value: AirTree,
then: AirTree,
otherwise: AirTree,
) -> AirTree {
assert!(props.kind.is_expect());
let expect = AirTree::binop(
BinOp::Eq,
bool(),
pattern,
AirTree::local_var(&name, tipo.clone()),
AirTree::SoftCastLet {
name: name.to_string(),
tipo,
);
let expr = AirTree::let_assignment(name, rhs, expect);
AirTree::assert_bool(true, expr, then, props.otherwise.clone())
value: value.into(),
then: then.into(),
otherwise: otherwise.into(),
}
}
pub fn cast_from_data(
value: AirTree,
tipo: Rc<Type>,
otherwise: AirTree,
full_cast: bool,
) -> AirTree {
pub fn cast_from_data(value: AirTree, tipo: Rc<Type>, full_cast: bool) -> AirTree {
AirTree::CastFromData {
tipo,
value: value.into(),
otherwise: otherwise.into(),
full_cast,
}
}
@ -888,7 +881,6 @@ impl AirTree {
vec![list_of_fields],
),
tipo.clone(),
AirTree::error(void(), false),
false,
)
}
@ -994,7 +986,6 @@ impl AirTree {
vec![tuple],
),
tipo.clone(),
AirTree::error(void(), false),
false,
)
}
@ -1045,6 +1036,32 @@ impl AirTree {
}
}
pub fn expect_on_list2() -> AirTree {
let expect_on_list = AirTree::var(
ValueConstructor::public(
void(),
ValueConstructorVariant::ModuleFn {
name: EXPECT_ON_LIST.to_string(),
field_map: None,
module: "".to_string(),
arity: 1,
location: Span::empty(),
builtin: None,
},
),
EXPECT_ON_LIST,
"",
);
let list_var = AirTree::local_var("__list_to_check", list(data()));
AirTree::call(
AirTree::local_var("__check_with", void()),
void(),
vec![list_var, expect_on_list],
)
}
pub fn expect_on_list() -> AirTree {
let list_var = AirTree::local_var("__list_to_check", list(data()));
@ -1108,6 +1125,21 @@ impl AirTree {
value.create_air_vec(air_vec);
then.create_air_vec(air_vec);
}
AirTree::SoftCastLet {
name,
tipo,
value,
then,
otherwise,
} => {
air_vec.push(Air::SoftCastLet {
name: name.clone(),
tipo: tipo.clone(),
});
value.create_air_vec(air_vec);
then.create_air_vec(air_vec);
otherwise.create_air_vec(air_vec);
}
AirTree::DefineFunc {
func_name,
module_name,
@ -1457,7 +1489,6 @@ impl AirTree {
AirTree::CastFromData {
tipo,
value,
otherwise,
full_cast,
} => {
air_vec.push(Air::CastFromData {
@ -1466,9 +1497,6 @@ impl AirTree {
});
value.create_air_vec(air_vec);
if *full_cast {
otherwise.create_air_vec(air_vec);
}
}
AirTree::CastToData { tipo, value } => {
air_vec.push(Air::CastToData { tipo: tipo.clone() });
@ -1670,6 +1698,7 @@ impl AirTree {
| AirTree::PairClause { then, .. }
| AirTree::Finally { then, .. }
| AirTree::Let { then, .. }
| AirTree::SoftCastLet { then, .. }
| AirTree::DefineFunc { then, .. }
| AirTree::DefineCyclicFuncs { then, .. }
| AirTree::AssertConstr { then, .. }
@ -1715,7 +1744,8 @@ impl AirTree {
| AirTree::Constr { tipo, .. }
| AirTree::ErrorTerm { tipo, .. }
| AirTree::Trace { tipo, .. }
| AirTree::Pair { tipo, .. } => vec![tipo],
| AirTree::Pair { tipo, .. }
| AirTree::SoftCastLet { tipo, .. } => vec![tipo],
AirTree::FieldsExpose { indices, .. } => {
let mut types = vec![];
@ -1821,6 +1851,30 @@ impl AirTree {
);
}
AirTree::SoftCastLet {
name: _,
tipo: _,
value,
then: _,
otherwise,
} => {
value.do_traverse_tree_with(
tree_path,
current_depth + 1,
Fields::ThirdField,
with,
apply_with_func_last,
);
otherwise.do_traverse_tree_with(
tree_path,
current_depth + 1,
Fields::FifthField,
with,
apply_with_func_last,
);
}
AirTree::AssertConstr {
constr_index: _,
constr,
@ -2291,7 +2345,6 @@ impl AirTree {
AirTree::CastFromData {
tipo: _,
value,
otherwise: _,
full_cast: _,
} => {
value.do_traverse_tree_with(
@ -2560,6 +2613,21 @@ impl AirTree {
apply_with_func_last,
);
}
AirTree::SoftCastLet {
name: _,
tipo: _,
value: _,
then,
otherwise: _,
} => {
then.do_traverse_tree_with(
tree_path,
current_depth + 1,
Fields::FourthField,
with,
apply_with_func_last,
);
}
AirTree::AssertConstr {
constr_index: _,
constr: _,
@ -2806,6 +2874,18 @@ impl AirTree {
Fields::ThirdField => then.as_mut().do_find_air_tree_node(tree_path_iter),
_ => panic!("Tree Path index outside tree children nodes"),
},
AirTree::SoftCastLet {
name: _,
tipo: _,
value,
then,
otherwise,
} => match field {
Fields::ThirdField => value.as_mut().do_find_air_tree_node(tree_path_iter),
Fields::FourthField => then.as_mut().do_find_air_tree_node(tree_path_iter),
Fields::FifthField => otherwise.as_mut().do_find_air_tree_node(tree_path_iter),
_ => panic!("Tree Path index outside tree children nodes"),
},
AirTree::AssertConstr {
constr_index: _,
constr,
@ -3011,11 +3091,9 @@ impl AirTree {
AirTree::CastFromData {
tipo: _,
value,
otherwise,
full_cast: _,
} => match field {
Fields::SecondField => value.as_mut().do_find_air_tree_node(tree_path_iter),
Fields::ThirdField => otherwise.as_mut().do_find_air_tree_node(tree_path_iter),
_ => panic!("Tree Path index outside tree children nodes"),
},
AirTree::CastToData { tipo: _, value } => match field {