feat: code gen support for if/is

Co-authored-by: Kasey White <kwhitemsg@gmail.com>
This commit is contained in:
rvcas 2024-06-11 19:29:55 -04:00 committed by Lucas
parent 1b8805825b
commit 5024bd3f9c
4 changed files with 267 additions and 262 deletions

View File

@ -5,7 +5,7 @@ pub mod tree;
use self::{ use self::{
air::Air, air::Air,
builder::{ builder::{
air_holds_msg, cast_validator_args, constants_ir, convert_type_to_data, extract_constant, cast_validator_args, constants_ir, convert_type_to_data, extract_constant,
modify_cyclic_calls, modify_self_calls, rearrange_list_clauses, AssignmentProperties, modify_cyclic_calls, modify_self_calls, rearrange_list_clauses, AssignmentProperties,
ClauseProperties, CodeGenSpecialFuncs, CycleFunctionNames, HoistableFunction, Variant, ClauseProperties, CodeGenSpecialFuncs, CycleFunctionNames, HoistableFunction, Variant,
}, },
@ -42,6 +42,7 @@ use indexmap::{IndexMap, IndexSet};
use itertools::Itertools; use itertools::Itertools;
use petgraph::{algo, Graph}; use petgraph::{algo, Graph};
use std::{collections::HashMap, rc::Rc}; use std::{collections::HashMap, rc::Rc};
use uplc::{ use uplc::{
ast::{Constant as UplcConstant, Name, NamedDeBruijn, Program, Term, Type as UplcType}, ast::{Constant as UplcConstant, Name, NamedDeBruijn, Program, Term, Type as UplcType},
builder::{CONSTR_FIELDS_EXPOSER, CONSTR_INDEX_EXPOSER, EXPECT_ON_LIST}, builder::{CONSTR_FIELDS_EXPOSER, CONSTR_INDEX_EXPOSER, EXPECT_ON_LIST},
@ -258,18 +259,23 @@ impl<'a> CodeGenerator<'a> {
let air_value = self.build(value, module_build_name, &[]); let air_value = self.build(value, module_build_name, &[]);
let msg_func = match self.tracing { let otherwise = match (self.tracing, kind) {
TraceLevel::Silent => None, (
TraceLevel::Verbose | TraceLevel::Compact => { TraceLevel::Silent,
if kind.is_expect() { AssignmentKind::Let { .. } | AssignmentKind::Expect { .. },
) => AirTree::error(void(), false),
(TraceLevel::Compact | TraceLevel::Verbose, AssignmentKind::Let { .. }) => {
AirTree::error(void(), false)
}
(TraceLevel::Verbose | TraceLevel::Compact, AssignmentKind::Expect { .. }) => {
let msg = match self.tracing { let msg = match self.tracing {
TraceLevel::Silent => unreachable!("excluded from pattern guards"), TraceLevel::Silent => unreachable!("excluded from pattern guards"),
TraceLevel::Compact => get_line_columns_by_span( TraceLevel::Compact => {
module_build_name, get_line_columns_by_span(module_build_name, location, &self.module_src)
location, .to_string()
&self.module_src, }
)
.to_string(),
TraceLevel::Verbose => { TraceLevel::Verbose => {
get_src_code_by_span(module_build_name, location, &self.module_src) get_src_code_by_span(module_build_name, location, &self.module_src)
} }
@ -283,10 +289,10 @@ impl<'a> CodeGenerator<'a> {
string(), string(),
); );
Some(self.special_functions.use_function_msg(msg_func_name)) let msg_string =
} else { AirTree::string(self.special_functions.use_function_string(msg_func_name));
None
} AirTree::trace(msg_string, void(), AirTree::error(void(), false))
} }
}; };
@ -304,7 +310,7 @@ impl<'a> CodeGenerator<'a> {
kind: *kind, kind: *kind,
remove_unused: kind.is_let(), remove_unused: kind.is_let(),
full_check: !tipo.is_data() && value.tipo().is_data() && kind.is_expect(), full_check: !tipo.is_data() && value.tipo().is_data() && kind.is_expect(),
msg_func, otherwise,
}, },
) )
} else { } else {
@ -574,7 +580,7 @@ impl<'a> CodeGenerator<'a> {
kind: AssignmentKind::let_(), kind: AssignmentKind::let_(),
remove_unused: false, remove_unused: false,
full_check: false, full_check: false,
msg_func: None, otherwise: AirTree::error(void(), false),
}, },
) )
} else { } else {
@ -625,24 +631,39 @@ impl<'a> CodeGenerator<'a> {
) )
} }
} }
// let pattern = branch.condition
// branch.body
//
// if <expr:condition> is <pattern>: <annotation> { <expr:body> }
// [(builtin ifThenElse) (condition is pattern) (body) (else) ]
TypedExpr::If { TypedExpr::If {
branches, branches,
final_else, final_else,
tipo, tipo,
.. ..
} => AirTree::if_branches( } => branches.iter().rfold(
branches
.iter()
.map(|branch| {
(
self.build(&branch.condition, module_build_name, &[]),
self.build(&branch.body, module_build_name, &[]),
)
})
.collect_vec(),
tipo.clone(),
self.build(final_else, module_build_name, &[]), self.build(final_else, module_build_name, &[]),
|acc, branch| {
let condition = self.build(&branch.condition, module_build_name, &[]);
let body = self.build(&branch.body, module_build_name, &[]);
match &branch.is {
Some(pattern) => self.assignment(
pattern,
condition,
body,
&pattern.tipo(&branch.condition).unwrap(),
AssignmentProperties {
value_type: branch.condition.tipo(),
kind: AssignmentKind::Expect { backpassing: () },
remove_unused: false,
full_check: true,
otherwise: acc,
},
),
None => AirTree::if_branch(tipo.clone(), condition, body, acc),
}
},
), ),
TypedExpr::RecordAccess { TypedExpr::RecordAccess {
@ -878,7 +899,7 @@ impl<'a> CodeGenerator<'a> {
// Cast value to or from data so we don't have to worry from this point onward // Cast value to or from data so we don't have to worry from this point onward
if props.value_type.is_data() && props.kind.is_expect() && !tipo.is_data() { if props.value_type.is_data() && props.kind.is_expect() && !tipo.is_data() {
value = AirTree::cast_from_data(value, tipo.clone(), props.msg_func.clone()); value = AirTree::cast_from_data(value, tipo.clone(), props.otherwise.clone());
} else if !props.value_type.is_data() && tipo.is_data() { } else if !props.value_type.is_data() && tipo.is_data() {
value = AirTree::cast_to_data(value, props.value_type.clone()); value = AirTree::cast_to_data(value, props.value_type.clone());
} }
@ -906,7 +927,7 @@ impl<'a> CodeGenerator<'a> {
let expr = AirTree::let_assignment(name, value, expect); let expr = AirTree::let_assignment(name, value, expect);
AirTree::assert_bool(true, expr, props.msg_func.clone(), then) AirTree::assert_bool(true, expr, then, props.otherwise.clone())
} }
Pattern::Var { name, .. } => { Pattern::Var { name, .. } => {
@ -925,7 +946,7 @@ impl<'a> CodeGenerator<'a> {
val, val,
&mut index_map, &mut index_map,
pattern.location(), pattern.location(),
props.msg_func, props.otherwise,
); );
let assign_expect = AirTree::let_assignment("_", expect, then); let assign_expect = AirTree::let_assignment("_", expect, then);
@ -965,7 +986,7 @@ impl<'a> CodeGenerator<'a> {
val, val,
&mut index_map, &mut index_map,
pattern.location(), pattern.location(),
props.msg_func, props.otherwise,
); );
let assignment = AirTree::let_assignment("_", expect, then); let assignment = AirTree::let_assignment("_", expect, then);
@ -975,7 +996,7 @@ impl<'a> CodeGenerator<'a> {
} else if !props.remove_unused { } else if !props.remove_unused {
AirTree::let_assignment(name, value, then) AirTree::let_assignment(name, value, then)
} else { } else {
AirTree::no_op(then) then
} }
} }
@ -1025,7 +1046,7 @@ impl<'a> CodeGenerator<'a> {
kind: props.kind, kind: props.kind,
remove_unused: true, remove_unused: true,
full_check: props.full_check, full_check: props.full_check,
msg_func: props.msg_func.clone(), otherwise: props.otherwise.clone(),
}, },
) )
} else { } else {
@ -1073,7 +1094,7 @@ impl<'a> CodeGenerator<'a> {
kind: props.kind, kind: props.kind,
remove_unused: true, remove_unused: true,
full_check: props.full_check, full_check: props.full_check,
msg_func: props.msg_func.clone(), otherwise: props.otherwise.clone(),
}, },
) )
} else { } else {
@ -1088,20 +1109,20 @@ impl<'a> CodeGenerator<'a> {
elems.reverse(); elems.reverse();
if elements.is_empty() { if elements.is_empty() {
AirTree::list_empty(value, props.msg_func, then) AirTree::list_empty(value, then, props.otherwise.clone())
} else { } else {
AirTree::list_access( AirTree::list_access(
elems, elems,
tipo.clone(), tipo.clone(),
tail.is_some(), tail.is_some(),
value, value,
props.msg_func,
if props.full_check { if props.full_check {
ExpectLevel::Full ExpectLevel::Full
} else { } else {
ExpectLevel::Items ExpectLevel::Items
}, },
then, then,
props.otherwise.clone(),
) )
} }
} }
@ -1164,7 +1185,7 @@ impl<'a> CodeGenerator<'a> {
kind: props.kind, kind: props.kind,
remove_unused: true, remove_unused: true,
full_check: props.full_check, full_check: props.full_check,
msg_func: props.msg_func.clone(), otherwise: props.otherwise.clone(),
}, },
) )
} else { } else {
@ -1190,11 +1211,6 @@ impl<'a> CodeGenerator<'a> {
let local_value = AirTree::local_var(&constructor_name, tipo.clone()); let local_value = AirTree::local_var(&constructor_name, tipo.clone());
let then = { let then = {
let (is_expect, msg) = if props.full_check {
(true, props.msg_func.clone())
} else {
(false, None)
};
assert!(fields.len() == 2); assert!(fields.len() == 2);
AirTree::pair_access( AirTree::pair_access(
@ -1208,9 +1224,9 @@ impl<'a> CodeGenerator<'a> {
.unwrap(), .unwrap(),
tipo.clone(), tipo.clone(),
local_value, local_value,
msg, props.full_check,
is_expect,
then, then,
props.otherwise.clone(),
) )
}; };
@ -1223,7 +1239,7 @@ impl<'a> CodeGenerator<'a> {
} if tipo.is_bool() => { } if tipo.is_bool() => {
assert!(props.kind.is_expect()); assert!(props.kind.is_expect());
AirTree::assert_bool(name == "True", value, props.msg_func, then) AirTree::assert_bool(name == "True", value, then, props.otherwise.clone())
} }
Pattern::Constructor { .. } if tipo.is_void() => { Pattern::Constructor { .. } if tipo.is_void() => {
@ -1308,7 +1324,7 @@ impl<'a> CodeGenerator<'a> {
kind: props.kind, kind: props.kind,
remove_unused: true, remove_unused: true,
full_check: props.full_check, full_check: props.full_check,
msg_func: props.msg_func.clone(), otherwise: props.otherwise.clone(),
}, },
) )
} else { } else {
@ -1336,12 +1352,13 @@ impl<'a> CodeGenerator<'a> {
let then = if check_replaceable_opaque_type(tipo, &self.data_types) { let then = if check_replaceable_opaque_type(tipo, &self.data_types) {
AirTree::let_assignment(&fields[0].1, local_value, then) AirTree::let_assignment(&fields[0].1, local_value, then)
} else { } else {
let (is_expect, msg) = if props.full_check { AirTree::fields_expose(
(true, props.msg_func.clone()) fields,
} else { local_value,
(false, None) props.full_check,
}; then,
AirTree::fields_expose(fields, local_value, msg, is_expect, then) props.otherwise.clone(),
)
}; };
let data_type = lookup_data_type_by_tipo(&self.data_types, tipo) let data_type = lookup_data_type_by_tipo(&self.data_types, tipo)
@ -1362,10 +1379,11 @@ impl<'a> CodeGenerator<'a> {
AirTree::assert_constr_index( AirTree::assert_constr_index(
index, index,
AirTree::local_var(&constructor_name, tipo.clone()), AirTree::local_var(&constructor_name, tipo.clone()),
props.msg_func,
then, then,
props.otherwise.clone(),
) )
} else { } else {
assert!(data_type.constructors.len() == 1);
then then
}; };
@ -1425,7 +1443,7 @@ impl<'a> CodeGenerator<'a> {
kind: props.kind, kind: props.kind,
remove_unused: true, remove_unused: true,
full_check: props.full_check, full_check: props.full_check,
msg_func: props.msg_func.clone(), otherwise: props.otherwise.clone(),
}, },
) )
} else { } else {
@ -1439,15 +1457,16 @@ impl<'a> CodeGenerator<'a> {
fields.reverse(); fields.reverse();
let (is_expect, msg) = if props.full_check {
(true, props.msg_func)
} else {
(false, None)
};
// This `value` is either value param that was passed in or local var // This `value` is either value param that was passed in or local var
AirTree::tuple_access(fields, tipo.clone(), value, msg, is_expect, then) AirTree::tuple_access(
fields,
tipo.clone(),
value,
props.full_check,
then,
props.otherwise.clone(),
)
} }
} }
} }
@ -1458,7 +1477,7 @@ impl<'a> CodeGenerator<'a> {
value: AirTree, value: AirTree,
defined_data_types: &mut IndexMap<String, u64>, defined_data_types: &mut IndexMap<String, u64>,
location: Span, location: Span,
msg_func: Option<AirMsg>, otherwise: AirTree,
) -> AirTree { ) -> AirTree {
assert!(tipo.get_generic().is_none()); assert!(tipo.get_generic().is_none());
// Shouldn't be needed but still here just in case // Shouldn't be needed but still here just in case
@ -1468,6 +1487,7 @@ impl<'a> CodeGenerator<'a> {
match uplc_type { match uplc_type {
// primitives // primitives
// Untyped Data
Some( Some(
UplcType::Integer UplcType::Integer
| UplcType::String | UplcType::String
@ -1476,10 +1496,9 @@ impl<'a> CodeGenerator<'a> {
| UplcType::Unit | UplcType::Unit
| UplcType::Bls12_381G1Element | UplcType::Bls12_381G1Element
| UplcType::Bls12_381G2Element | UplcType::Bls12_381G2Element
| UplcType::Bls12_381MlResult, | UplcType::Bls12_381MlResult
| UplcType::Data,
) => value, ) => value,
// Untyped Data
Some(UplcType::Data) => value,
// Map type // Map type
Some(UplcType::List(_)) if tipo.is_map() => { Some(UplcType::List(_)) if tipo.is_map() => {
@ -1500,7 +1519,7 @@ impl<'a> CodeGenerator<'a> {
AirTree::local_var(fst_name.clone(), inner_pair_types[0].clone()), AirTree::local_var(fst_name.clone(), inner_pair_types[0].clone()),
defined_data_types, defined_data_types,
location, location,
msg_func.clone(), otherwise.clone(),
); );
let expect_snd = self.expect_type_assign( let expect_snd = self.expect_type_assign(
@ -1508,7 +1527,7 @@ impl<'a> CodeGenerator<'a> {
AirTree::local_var(snd_name.clone(), inner_pair_types[1].clone()), AirTree::local_var(snd_name.clone(), inner_pair_types[1].clone()),
defined_data_types, defined_data_types,
location, location,
msg_func.clone(), otherwise.clone(),
); );
let anon_func_body = AirTree::pair_access( let anon_func_body = AirTree::pair_access(
@ -1516,9 +1535,9 @@ impl<'a> CodeGenerator<'a> {
Some(snd_name), Some(snd_name),
inner_list_type.clone(), inner_list_type.clone(),
AirTree::local_var(&pair_name, inner_list_type.clone()), AirTree::local_var(&pair_name, inner_list_type.clone()),
msg_func.clone(),
true, true,
AirTree::let_assignment("_", expect_fst, expect_snd), AirTree::let_assignment("_", expect_fst, expect_snd),
otherwise.clone(),
); );
let unwrap_function = AirTree::anon_func(vec![pair_name], anon_func_body); let unwrap_function = AirTree::anon_func(vec![pair_name], anon_func_body);
@ -1587,7 +1606,7 @@ impl<'a> CodeGenerator<'a> {
AirTree::local_var(&tuple_index_name, arg.clone()), AirTree::local_var(&tuple_index_name, arg.clone()),
defined_data_types, defined_data_types,
location, location,
msg_func.clone(), otherwise.clone(),
); );
tuple_expect_items.push(tuple_index_name); tuple_expect_items.push(tuple_index_name);
@ -1602,9 +1621,9 @@ impl<'a> CodeGenerator<'a> {
tuple_expect_items, tuple_expect_items,
tipo.clone(), tipo.clone(),
AirTree::local_var(&tuple_name, tipo.clone()), AirTree::local_var(&tuple_name, tipo.clone()),
msg_func,
true, true,
then, then,
otherwise,
); );
AirTree::let_assignment(&tuple_name, value, tuple_access) AirTree::let_assignment(&tuple_name, value, tuple_access)
@ -1626,11 +1645,11 @@ impl<'a> CodeGenerator<'a> {
AirTree::cast_from_data( AirTree::cast_from_data(
AirTree::local_var(&item_name, data()), AirTree::local_var(&item_name, data()),
inner_list_type.clone(), inner_list_type.clone(),
msg_func.clone(), otherwise.clone(),
), ),
defined_data_types, defined_data_types,
location, location,
msg_func, otherwise,
); );
let anon_func_body = expect_item; let anon_func_body = expect_item;
@ -1701,7 +1720,7 @@ impl<'a> CodeGenerator<'a> {
AirTree::local_var(fst_name.clone(), tuple_inner_types[0].clone()), AirTree::local_var(fst_name.clone(), tuple_inner_types[0].clone()),
defined_data_types, defined_data_types,
location, location,
msg_func.clone(), otherwise.clone(),
); );
let expect_snd = self.expect_type_assign( let expect_snd = self.expect_type_assign(
@ -1709,7 +1728,7 @@ impl<'a> CodeGenerator<'a> {
AirTree::local_var(snd_name.clone(), tuple_inner_types[1].clone()), AirTree::local_var(snd_name.clone(), tuple_inner_types[1].clone()),
defined_data_types, defined_data_types,
location, location,
msg_func.clone(), otherwise.clone(),
); );
let pair_access = AirTree::pair_access( let pair_access = AirTree::pair_access(
@ -1717,9 +1736,9 @@ impl<'a> CodeGenerator<'a> {
Some(snd_name.clone()), Some(snd_name.clone()),
tipo.clone(), tipo.clone(),
AirTree::local_var(&pair_name, tipo.clone()), AirTree::local_var(&pair_name, tipo.clone()),
msg_func.clone(),
true, true,
AirTree::let_assignment("_", expect_fst, expect_snd), AirTree::let_assignment("_", expect_fst, expect_snd),
otherwise,
); );
AirTree::let_assignment(&pair_name, value, pair_access) AirTree::let_assignment(&pair_name, value, pair_access)
@ -1800,7 +1819,7 @@ impl<'a> CodeGenerator<'a> {
AirTree::local_var(arg_name, arg_tipo), AirTree::local_var(arg_name, arg_tipo),
defined_data_types, defined_data_types,
location, location,
msg_term.clone(), otherwise.clone(),
), ),
then, then,
) )
@ -1830,9 +1849,9 @@ impl<'a> CodeGenerator<'a> {
), ),
tipo.clone(), tipo.clone(),
), ),
msg_term.clone(),
true, true,
constr_then, constr_then,
otherwise.clone(),
) )
}; };
@ -1895,12 +1914,7 @@ impl<'a> CodeGenerator<'a> {
let args = match self.tracing { let args = match self.tracing {
TraceLevel::Silent => vec![value], TraceLevel::Silent => vec![value],
TraceLevel::Compact | TraceLevel::Verbose => vec![ TraceLevel::Compact | TraceLevel::Verbose => vec![value, otherwise],
value,
msg_func
.expect("should be unreachable: no msg func with tracing enabled.")
.to_air_tree(),
],
}; };
let module_fn = ValueConstructorVariant::ModuleFn { let module_fn = ValueConstructorVariant::ModuleFn {
@ -2459,9 +2473,9 @@ impl<'a> CodeGenerator<'a> {
// One special usage of list access here // One special usage of list access here
// So for the msg we pass in empty string if tracing is on // So for the msg we pass in empty string if tracing is on
// Since check_last_item is false this will never get added to the final uplc anyway // Since check_last_item is false this will never get added to the final uplc anyway
None,
ExpectLevel::None, ExpectLevel::None,
elems_then, elems_then,
AirTree::error(void(), false),
) )
} else { } else {
assert!(defined_tails.len() >= elems.len()); assert!(defined_tails.len() >= elems.len());
@ -2540,9 +2554,9 @@ impl<'a> CodeGenerator<'a> {
name_index_assigns[1].0.clone(), name_index_assigns[1].0.clone(),
subject_tipo.clone(), subject_tipo.clone(),
AirTree::local_var(props.clause_var_name.clone(), subject_tipo.clone()), AirTree::local_var(props.clause_var_name.clone(), subject_tipo.clone()),
None,
false, false,
next_then, next_then,
AirTree::error(void(), false),
) )
}; };
@ -2667,9 +2681,9 @@ impl<'a> CodeGenerator<'a> {
AirTree::fields_expose( AirTree::fields_expose(
fields, fields,
AirTree::local_var(props.clause_var_name.clone(), subject_tipo.clone()), AirTree::local_var(props.clause_var_name.clone(), subject_tipo.clone()),
None,
false, false,
next_then, next_then,
AirTree::error(void(), false),
) )
}; };
@ -2796,9 +2810,9 @@ impl<'a> CodeGenerator<'a> {
names, names,
subject_tipo.clone(), subject_tipo.clone(),
AirTree::local_var(&props.clause_var_name, subject_tipo.clone()), AirTree::local_var(&props.clause_var_name, subject_tipo.clone()),
None,
false, false,
tuple_name_assigns, tuple_name_assigns,
AirTree::error(void(), false),
), ),
) )
} else { } else {
@ -3034,8 +3048,8 @@ impl<'a> CodeGenerator<'a> {
let actual_type = convert_opaque_type(&arg.tipo, &self.data_types, true); let actual_type = convert_opaque_type(&arg.tipo, &self.data_types, true);
let msg_func = match self.tracing { let otherwise = match self.tracing {
TraceLevel::Silent => None, TraceLevel::Silent => AirTree::error(void(), false),
TraceLevel::Compact | TraceLevel::Verbose => { TraceLevel::Compact | TraceLevel::Verbose => {
let msg = match self.tracing { let msg = match self.tracing {
TraceLevel::Silent => { TraceLevel::Silent => {
@ -3059,7 +3073,11 @@ impl<'a> CodeGenerator<'a> {
string(), string(),
); );
Some(self.special_functions.use_function_msg(msg_func_name)) let msg_string = AirTree::string(
self.special_functions.use_function_string(msg_func_name),
);
AirTree::trace(msg_string, void(), AirTree::error(void(), false))
} }
}; };
@ -3076,7 +3094,7 @@ impl<'a> CodeGenerator<'a> {
kind: AssignmentKind::expect(), kind: AssignmentKind::expect(),
remove_unused: false, remove_unused: false,
full_check: true, full_check: true,
msg_func, otherwise,
}, },
); );
@ -4017,24 +4035,11 @@ impl<'a> CodeGenerator<'a> {
} }
fn gen_uplc(&mut self, ir: Air, arg_stack: &mut Vec<Term<Name>>) -> Option<Term<Name>> { fn gen_uplc(&mut self, ir: Air, arg_stack: &mut Vec<Term<Name>>) -> Option<Term<Name>> {
// Going to mark the changes made to code gen after air tree implementation let convert_data_to_type = |term, tipo, otherwise| {
let error_term = match self.tracing { if otherwise == Term::Error {
TraceLevel::Silent => Term::Error,
TraceLevel::Compact | TraceLevel::Verbose => {
if air_holds_msg(&ir) {
let msg = arg_stack.pop().unwrap();
Term::Error.delayed_trace(msg)
} else {
Term::Error
}
}
};
let convert_data_to_type = |term, tipo| {
if error_term == Term::Error {
builder::unknown_data_to_type(term, tipo) builder::unknown_data_to_type(term, tipo)
} else { } else {
builder::unknown_data_to_type_debug(term, tipo, error_term.clone()) builder::unknown_data_to_type_otherwise(term, tipo, otherwise)
} }
}; };
@ -4337,6 +4342,12 @@ impl<'a> CodeGenerator<'a> {
let mut term = arg_stack.pop().unwrap(); let mut term = arg_stack.pop().unwrap();
let otherwise = if matches!(expect_level, ExpectLevel::Full | ExpectLevel::Items) {
arg_stack.pop().unwrap()
} else {
Term::Error
};
let list_id = self.id_gen.next(); let list_id = self.id_gen.next();
let mut id_list = vec![]; let mut id_list = vec![];
@ -4362,7 +4373,7 @@ impl<'a> CodeGenerator<'a> {
term, term,
true, true,
expect_level, expect_level,
error_term, otherwise,
) )
.apply(value); .apply(value);
@ -4774,8 +4785,9 @@ impl<'a> CodeGenerator<'a> {
} }
Air::CastFromData { tipo, .. } => { Air::CastFromData { tipo, .. } => {
let mut term = arg_stack.pop().unwrap(); let mut term = arg_stack.pop().unwrap();
let otherwise = arg_stack.pop().unwrap();
term = convert_data_to_type(term, &tipo); term = convert_data_to_type(term, &tipo, otherwise);
if extract_constant(&term).is_some() { if extract_constant(&term).is_some() {
let mut program: Program<Name> = Program { let mut program: Program<Name> = Program {
@ -4834,6 +4846,7 @@ impl<'a> CodeGenerator<'a> {
let constr = arg_stack.pop().unwrap(); let constr = arg_stack.pop().unwrap();
let mut term = arg_stack.pop().unwrap(); let mut term = arg_stack.pop().unwrap();
let otherwise = arg_stack.pop().unwrap();
term = Term::equals_integer() term = Term::equals_integer()
.apply(Term::integer(constr_index.into())) .apply(Term::integer(constr_index.into()))
@ -4844,7 +4857,7 @@ impl<'a> CodeGenerator<'a> {
) )
.apply(constr), .apply(constr),
) )
.delayed_if_then_else(term, error_term); .delayed_if_then_else(term, otherwise);
Some(term) Some(term)
} }
@ -4852,11 +4865,12 @@ impl<'a> CodeGenerator<'a> {
let value = arg_stack.pop().unwrap(); let value = arg_stack.pop().unwrap();
let mut term = arg_stack.pop().unwrap(); let mut term = arg_stack.pop().unwrap();
let otherwise = arg_stack.pop().unwrap();
if is_true { if is_true {
term = value.delayed_if_then_else(term, error_term) term = value.delayed_if_then_else(term, otherwise)
} else { } else {
term = value.delayed_if_then_else(error_term, term) term = value.delayed_if_then_else(otherwise, term)
} }
Some(term) Some(term)
} }
@ -5286,6 +5300,13 @@ impl<'a> CodeGenerator<'a> {
let value = arg_stack.pop().unwrap(); let value = arg_stack.pop().unwrap();
let mut term = arg_stack.pop().unwrap(); let mut term = arg_stack.pop().unwrap();
let otherwise = if is_expect {
arg_stack.pop().unwrap()
} else {
Term::Error
};
let list_id = self.id_gen.next(); let list_id = self.id_gen.next();
id_list.push(list_id); id_list.push(list_id);
@ -5313,7 +5334,7 @@ impl<'a> CodeGenerator<'a> {
term, term,
false, false,
is_expect.into(), is_expect.into(),
error_term, otherwise,
); );
term = term.apply( term = term.apply(
@ -5333,13 +5354,14 @@ impl<'a> CodeGenerator<'a> {
let value = arg_stack.pop().unwrap(); let value = arg_stack.pop().unwrap();
let mut term = arg_stack.pop().unwrap(); let mut term = arg_stack.pop().unwrap();
let otherwise = arg_stack.pop().unwrap();
term = Term::var( term = Term::var(
self.special_functions self.special_functions
.use_function_uplc(CONSTR_FIELDS_EXPOSER.to_string()), .use_function_uplc(CONSTR_FIELDS_EXPOSER.to_string()),
) )
.apply(value) .apply(value)
.delayed_choose_list(term, error_term); .delayed_choose_list(term, otherwise);
Some(term) Some(term)
} }
@ -5347,8 +5369,9 @@ impl<'a> CodeGenerator<'a> {
let value = arg_stack.pop().unwrap(); let value = arg_stack.pop().unwrap();
let mut term = arg_stack.pop().unwrap(); let mut term = arg_stack.pop().unwrap();
let otherwise = arg_stack.pop().unwrap();
term = value.delayed_choose_list(term, error_term); term = value.delayed_choose_list(term, otherwise);
Some(term) Some(term)
} }
@ -5545,6 +5568,11 @@ impl<'a> CodeGenerator<'a> {
let value = arg_stack.pop().unwrap(); let value = arg_stack.pop().unwrap();
let mut term = arg_stack.pop().unwrap(); let mut term = arg_stack.pop().unwrap();
let otherwise = if is_expect {
arg_stack.pop().unwrap()
} else {
Term::Error
};
let list_id = self.id_gen.next(); let list_id = self.id_gen.next();
let mut id_list = vec![]; let mut id_list = vec![];
@ -5567,7 +5595,7 @@ impl<'a> CodeGenerator<'a> {
term, term,
false, false,
is_expect.into(), is_expect.into(),
error_term, otherwise,
) )
.apply(value); .apply(value);
@ -5583,6 +5611,12 @@ impl<'a> CodeGenerator<'a> {
let value = arg_stack.pop().unwrap(); let value = arg_stack.pop().unwrap();
let mut term = arg_stack.pop().unwrap(); let mut term = arg_stack.pop().unwrap();
let otherwise = if is_expect {
arg_stack.pop().unwrap()
} else {
Term::Error
};
let list_id = self.id_gen.next(); let list_id = self.id_gen.next();
if let Some(name) = snd { if let Some(name) = snd {
@ -5590,6 +5624,7 @@ impl<'a> CodeGenerator<'a> {
convert_data_to_type( convert_data_to_type(
Term::snd_pair().apply(Term::var(format!("__pair_{list_id}"))), Term::snd_pair().apply(Term::var(format!("__pair_{list_id}"))),
&inner_types[1], &inner_types[1],
otherwise.clone(),
) )
} else { } else {
known_data_to_type( known_data_to_type(
@ -5604,6 +5639,7 @@ impl<'a> CodeGenerator<'a> {
convert_data_to_type( convert_data_to_type(
Term::fst_pair().apply(Term::var(format!("__pair_{list_id}"))), Term::fst_pair().apply(Term::var(format!("__pair_{list_id}"))),
&inner_types[0], &inner_types[0],
otherwise,
) )
} else { } else {
known_data_to_type( known_data_to_type(

View File

@ -102,7 +102,6 @@ pub enum Air {
}, },
CastFromData { CastFromData {
tipo: Rc<Type>, tipo: Rc<Type>,
is_expect: bool,
}, },
CastToData { CastToData {
tipo: Rc<Type>, tipo: Rc<Type>,

View File

@ -1,5 +1,5 @@
use super::{ use super::{
air::{Air, ExpectLevel}, air::ExpectLevel,
tree::{AirMsg, AirTree, TreePath}, tree::{AirMsg, AirTree, TreePath},
}; };
use crate::{ use crate::{
@ -71,7 +71,7 @@ pub struct AssignmentProperties {
pub kind: TypedAssignmentKind, pub kind: TypedAssignmentKind,
pub remove_unused: bool, pub remove_unused: bool,
pub full_check: bool, pub full_check: bool,
pub msg_func: Option<AirMsg>, pub otherwise: AirTree,
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -233,6 +233,14 @@ impl CodeGenSpecialFuncs {
} }
} }
pub fn use_function_string(&mut self, func_name: String) -> String {
if !self.used_funcs.contains(&func_name) {
self.used_funcs.push(func_name.to_string());
}
func_name
}
pub fn use_function_tree(&mut self, func_name: String) -> AirTree { pub fn use_function_tree(&mut self, func_name: String) -> AirTree {
if !self.used_funcs.contains(&func_name) { if !self.used_funcs.contains(&func_name) {
self.used_funcs.push(func_name.to_string()); self.used_funcs.push(func_name.to_string());
@ -1035,7 +1043,7 @@ 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 /// 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 /// 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. /// For BLS12_381_G1Element and BLS12_381_G2Element, hash to group exists so just adopt that.
pub fn unknown_data_to_type_debug( pub fn unknown_data_to_type_otherwise(
term: Term<Name>, term: Term<Name>,
field_type: &Type, field_type: &Type,
error_term: Term<Name>, error_term: Term<Name>,
@ -1362,7 +1370,7 @@ pub fn list_access_to_uplc(
term: Term<Name>, term: Term<Name>,
is_list_accessor: bool, is_list_accessor: bool,
expect_level: ExpectLevel, expect_level: ExpectLevel,
error_term: Term<Name>, otherwise: Term<Name>,
) -> Term<Name> { ) -> Term<Name> {
let names_len = names_types_ids.len(); let names_len = names_types_ids.len();
@ -1393,7 +1401,7 @@ pub fn list_access_to_uplc(
} }
return Term::var("empty_list") return Term::var("empty_list")
.delayed_choose_list(term, error_term) .delayed_choose_list(term, otherwise)
.lambda("empty_list"); .lambda("empty_list");
} }
@ -1412,16 +1420,16 @@ pub fn list_access_to_uplc(
Term::head_list().apply(Term::var(tail_name.to_string())) Term::head_list().apply(Term::var(tail_name.to_string()))
} else if matches!(expect_level, ExpectLevel::Full) { } else if matches!(expect_level, ExpectLevel::Full) {
// Expect level is full so we have an unknown piece of data to cast // Expect level is full so we have an unknown piece of data to cast
if error_term == Term::Error { if otherwise == Term::Error {
unknown_data_to_type( unknown_data_to_type(
Term::head_list().apply(Term::var(tail_name.to_string())), Term::head_list().apply(Term::var(tail_name.to_string())),
&tipo.to_owned(), &tipo.to_owned(),
) )
} else { } else {
unknown_data_to_type_debug( unknown_data_to_type_otherwise(
Term::head_list().apply(Term::var(tail_name.to_string())), Term::head_list().apply(Term::var(tail_name.to_string())),
&tipo.to_owned(), &tipo.to_owned(),
error_term.clone(), otherwise.clone(),
) )
} }
} else { } else {
@ -1456,22 +1464,22 @@ pub fn list_access_to_uplc(
ExpectLevel::None => acc.lambda(name).apply(head_item).lambda(tail_name), ExpectLevel::None => acc.lambda(name).apply(head_item).lambda(tail_name),
ExpectLevel::Full | ExpectLevel::Items => { ExpectLevel::Full | ExpectLevel::Items => {
if error_term == Term::Error && tail_present { if otherwise == Term::Error && tail_present {
// No need to check last item if tail was present // No need to check last item if tail was present
acc.lambda(name).apply(head_item).lambda(tail_name) acc.lambda(name).apply(head_item).lambda(tail_name)
} else if tail_present { } else if tail_present {
// Custom error instead of trying to do head_item on a possibly empty list. // Custom error instead of trying to do head_item on a possibly empty list.
Term::var(tail_name.to_string()) Term::var(tail_name.to_string())
.delayed_choose_list( .delayed_choose_list(
error_term.clone(), otherwise.clone(),
acc.lambda(name).apply(head_item), acc.lambda(name).apply(head_item),
) )
.lambda(tail_name) .lambda(tail_name)
} else if error_term == Term::Error { } else if otherwise == Term::Error {
// Check head is last item in this list // Check head is last item in this list
Term::tail_list() Term::tail_list()
.apply(Term::var(tail_name.to_string())) .apply(Term::var(tail_name.to_string()))
.delayed_choose_list(acc, error_term.clone()) .delayed_choose_list(acc, otherwise.clone())
.lambda(name) .lambda(name)
.apply(head_item) .apply(head_item)
.lambda(tail_name) .lambda(tail_name)
@ -1479,10 +1487,10 @@ pub fn list_access_to_uplc(
// Custom error if list is not empty after this head // Custom error if list is not empty after this head
Term::var(tail_name.to_string()) Term::var(tail_name.to_string())
.delayed_choose_list( .delayed_choose_list(
error_term.clone(), otherwise.clone(),
Term::tail_list() Term::tail_list()
.apply(Term::var(tail_name.to_string())) .apply(Term::var(tail_name.to_string()))
.delayed_choose_list(acc, error_term.clone()) .delayed_choose_list(acc, otherwise.clone())
.lambda(name) .lambda(name)
.apply(head_item), .apply(head_item),
) )
@ -1498,7 +1506,7 @@ pub fn list_access_to_uplc(
let head_item = head_item(name, tipo, &tail_name); let head_item = head_item(name, tipo, &tail_name);
if matches!(expect_level, ExpectLevel::None) || error_term == Term::Error { if matches!(expect_level, ExpectLevel::None) || otherwise == Term::Error {
acc.apply(Term::tail_list().apply(Term::var(tail_name.to_string()))) acc.apply(Term::tail_list().apply(Term::var(tail_name.to_string())))
.lambda(name) .lambda(name)
.apply(head_item) .apply(head_item)
@ -1507,7 +1515,7 @@ pub fn list_access_to_uplc(
// case for a custom error if the list is empty at this point // case for a custom error if the list is empty at this point
Term::var(tail_name.to_string()) Term::var(tail_name.to_string())
.delayed_choose_list( .delayed_choose_list(
error_term.clone(), otherwise.clone(),
acc.apply( acc.apply(
Term::tail_list().apply(Term::var(tail_name.to_string())), Term::tail_list().apply(Term::var(tail_name.to_string())),
) )
@ -1778,7 +1786,6 @@ pub fn cast_validator_args(term: Term<Name>, arguments: &[TypedArg]) -> Term<Nam
} }
pub fn wrap_validator_condition(air_tree: AirTree, trace: TraceLevel) -> AirTree { pub fn wrap_validator_condition(air_tree: AirTree, trace: TraceLevel) -> AirTree {
let success_branch = vec![(air_tree, AirTree::void())];
let otherwise = match trace { let otherwise = match trace {
TraceLevel::Silent | TraceLevel::Compact => AirTree::error(void(), true), TraceLevel::Silent | TraceLevel::Compact => AirTree::error(void(), true),
TraceLevel::Verbose => AirTree::trace( TraceLevel::Verbose => AirTree::trace(
@ -1788,7 +1795,7 @@ pub fn wrap_validator_condition(air_tree: AirTree, trace: TraceLevel) -> AirTree
), ),
}; };
AirTree::if_branches(success_branch, void(), otherwise) AirTree::if_branch(void(), air_tree, AirTree::void(), otherwise)
} }
pub fn extract_constant(term: &Term<Name>) -> Option<Rc<UplcConstant>> { pub fn extract_constant(term: &Term<Name>) -> Option<Rc<UplcConstant>> {
@ -1841,22 +1848,3 @@ pub fn get_line_columns_by_span(
.line_and_column_number(span.start) .line_and_column_number(span.start)
.expect("Out of bounds span") .expect("Out of bounds span")
} }
pub fn air_holds_msg(air: &Air) -> bool {
match air {
Air::AssertConstr { .. } | Air::AssertBool { .. } | Air::FieldsEmpty | Air::ListEmpty => {
true
}
Air::FieldsExpose { is_expect, .. }
| Air::TupleAccessor { is_expect, .. }
| Air::PairAccessor { is_expect, .. }
| Air::CastFromData { is_expect, .. } => *is_expect,
Air::ListAccessor { expect_level, .. } => {
matches!(expect_level, ExpectLevel::Full | ExpectLevel::Items)
}
_ => false,
}
}

View File

@ -133,14 +133,14 @@ pub enum AirTree {
AssertConstr { AssertConstr {
constr_index: usize, constr_index: usize,
constr: Box<AirTree>, constr: Box<AirTree>,
msg: Option<AirMsg>,
then: Box<AirTree>, then: Box<AirTree>,
otherwise: Box<AirTree>,
}, },
AssertBool { AssertBool {
is_true: bool, is_true: bool,
value: Box<AirTree>, value: Box<AirTree>,
msg: Option<AirMsg>,
then: Box<AirTree>, then: Box<AirTree>,
otherwise: Box<AirTree>,
}, },
// Clause Guards // Clause Guards
ClauseGuard { ClauseGuard {
@ -174,8 +174,8 @@ pub enum AirTree {
indices: Vec<(usize, String, Rc<Type>)>, indices: Vec<(usize, String, Rc<Type>)>,
record: Box<AirTree>, record: Box<AirTree>,
is_expect: bool, is_expect: bool,
msg: Option<AirMsg>,
then: Box<AirTree>, then: Box<AirTree>,
otherwise: Box<AirTree>,
}, },
// List Access // List Access
ListAccessor { ListAccessor {
@ -184,8 +184,8 @@ pub enum AirTree {
tail: bool, tail: bool,
list: Box<AirTree>, list: Box<AirTree>,
expect_level: ExpectLevel, expect_level: ExpectLevel,
msg: Option<AirMsg>,
then: Box<AirTree>, then: Box<AirTree>,
otherwise: Box<AirTree>,
}, },
ListExpose { ListExpose {
tipo: Rc<Type>, tipo: Rc<Type>,
@ -199,8 +199,8 @@ pub enum AirTree {
tipo: Rc<Type>, tipo: Rc<Type>,
tuple: Box<AirTree>, tuple: Box<AirTree>,
is_expect: bool, is_expect: bool,
msg: Option<AirMsg>,
then: Box<AirTree>, then: Box<AirTree>,
otherwise: Box<AirTree>,
}, },
// Pair Access // Pair Access
PairAccessor { PairAccessor {
@ -208,9 +208,9 @@ pub enum AirTree {
snd: Option<String>, snd: Option<String>,
tipo: Rc<Type>, tipo: Rc<Type>,
is_expect: bool, is_expect: bool,
msg: Option<AirMsg>,
pair: Box<AirTree>, pair: Box<AirTree>,
then: Box<AirTree>, then: Box<AirTree>,
otherwise: Box<AirTree>,
}, },
// Misc. // Misc.
FieldsEmpty { FieldsEmpty {
@ -220,8 +220,8 @@ pub enum AirTree {
}, },
ListEmpty { ListEmpty {
list: Box<AirTree>, list: Box<AirTree>,
msg: Option<AirMsg>,
then: Box<AirTree>, then: Box<AirTree>,
otherwise: Box<AirTree>,
}, },
NoOp { NoOp {
then: Box<AirTree>, then: Box<AirTree>,
@ -297,7 +297,7 @@ pub enum AirTree {
CastFromData { CastFromData {
tipo: Rc<Type>, tipo: Rc<Type>,
value: Box<AirTree>, value: Box<AirTree>,
msg: Option<AirMsg>, otherwise: Box<AirTree>,
}, },
CastToData { CastToData {
tipo: Rc<Type>, tipo: Rc<Type>,
@ -359,7 +359,7 @@ pub enum AirTree {
// If // If
If { If {
tipo: Rc<Type>, tipo: Rc<Type>,
pattern: Box<AirTree>, condition: Box<AirTree>,
then: Box<AirTree>, then: Box<AirTree>,
otherwise: Box<AirTree>, otherwise: Box<AirTree>,
}, },
@ -390,6 +390,10 @@ pub enum AirTree {
} }
impl AirTree { impl AirTree {
pub fn is_error(&self) -> bool {
matches!(self, AirTree::ErrorTerm { .. })
}
pub fn int(value: impl ToString) -> AirTree { pub fn int(value: impl ToString) -> AirTree {
AirTree::Int { AirTree::Int {
value: value.to_string(), value: value.to_string(),
@ -562,11 +566,11 @@ impl AirTree {
} }
} }
pub fn cast_from_data(value: AirTree, tipo: Rc<Type>, msg: Option<AirMsg>) -> AirTree { pub fn cast_from_data(value: AirTree, tipo: Rc<Type>, otherwise: AirTree) -> AirTree {
AirTree::CastFromData { AirTree::CastFromData {
tipo, tipo,
value: value.into(), value: value.into(),
msg, otherwise: otherwise.into(),
} }
} }
@ -580,28 +584,28 @@ impl AirTree {
pub fn assert_constr_index( pub fn assert_constr_index(
constr_index: usize, constr_index: usize,
constr: AirTree, constr: AirTree,
msg: Option<AirMsg>,
then: AirTree, then: AirTree,
otherwise: AirTree,
) -> AirTree { ) -> AirTree {
AirTree::AssertConstr { AirTree::AssertConstr {
constr_index, constr_index,
constr: constr.into(), constr: constr.into(),
msg,
then: then.into(), then: then.into(),
otherwise: otherwise.into(),
} }
} }
pub fn assert_bool( pub fn assert_bool(
is_true: bool, is_true: bool,
value: AirTree, value: AirTree,
msg: Option<AirMsg>,
then: AirTree, then: AirTree,
otherwise: AirTree,
) -> AirTree { ) -> AirTree {
AirTree::AssertBool { AirTree::AssertBool {
is_true, is_true,
value: value.into(), value: value.into(),
msg,
then: then.into(), then: then.into(),
otherwise: otherwise.into(),
} }
} }
@ -771,31 +775,18 @@ impl AirTree {
} }
} }
pub fn if_branches( pub fn if_branch(
mut branches: Vec<(AirTree, AirTree)>,
tipo: Rc<Type>, tipo: Rc<Type>,
condition: AirTree,
branch: AirTree,
otherwise: AirTree, otherwise: AirTree,
) -> AirTree { ) -> AirTree {
assert!(!branches.is_empty()); AirTree::If {
let last_if = branches.pop().unwrap(); tipo,
condition: condition.into(),
let mut final_if = AirTree::If { then: branch.into(),
tipo: tipo.clone(),
pattern: Box::new(last_if.0),
then: Box::new(last_if.1),
otherwise: otherwise.into(), otherwise: otherwise.into(),
};
while let Some(branch) = branches.pop() {
final_if = AirTree::If {
tipo: tipo.clone(),
pattern: Box::new(branch.0),
then: Box::new(branch.1),
otherwise: final_if.into(),
};
} }
final_if
} }
pub fn create_constr(tag: usize, tipo: Rc<Type>, args: Vec<AirTree>) -> AirTree { pub fn create_constr(tag: usize, tipo: Rc<Type>, args: Vec<AirTree>) -> AirTree {
@ -845,23 +836,23 @@ impl AirTree {
vec![list_of_fields], vec![list_of_fields],
), ),
tipo.clone(), tipo.clone(),
None, AirTree::error(void(), false),
) )
} }
pub fn fields_expose( pub fn fields_expose(
indices: Vec<(usize, String, Rc<Type>)>, indices: Vec<(usize, String, Rc<Type>)>,
record: AirTree, record: AirTree,
msg: Option<AirMsg>,
is_expect: bool, is_expect: bool,
then: AirTree, then: AirTree,
otherwise: AirTree,
) -> AirTree { ) -> AirTree {
AirTree::FieldsExpose { AirTree::FieldsExpose {
indices, indices,
record: record.into(), record: record.into(),
msg,
is_expect, is_expect,
then: then.into(), then: then.into(),
otherwise: otherwise.into(),
} }
} }
@ -870,9 +861,10 @@ impl AirTree {
tipo: Rc<Type>, tipo: Rc<Type>,
tail: bool, tail: bool,
list: AirTree, list: AirTree,
msg: Option<AirMsg>,
expect_level: ExpectLevel, expect_level: ExpectLevel,
then: AirTree, then: AirTree,
otherwise: AirTree,
) -> AirTree { ) -> AirTree {
AirTree::ListAccessor { AirTree::ListAccessor {
tipo, tipo,
@ -880,8 +872,8 @@ impl AirTree {
tail, tail,
list: list.into(), list: list.into(),
expect_level, expect_level,
msg,
then: then.into(), then: then.into(),
otherwise: otherwise.into(),
} }
} }
@ -903,17 +895,17 @@ impl AirTree {
names: Vec<String>, names: Vec<String>,
tipo: Rc<Type>, tipo: Rc<Type>,
tuple: AirTree, tuple: AirTree,
msg: Option<AirMsg>,
is_expect: bool, is_expect: bool,
then: AirTree, then: AirTree,
otherwise: AirTree,
) -> AirTree { ) -> AirTree {
AirTree::TupleAccessor { AirTree::TupleAccessor {
names, names,
tipo, tipo,
tuple: tuple.into(), tuple: tuple.into(),
msg,
is_expect, is_expect,
then: then.into(), then: then.into(),
otherwise: otherwise.into(),
} }
} }
@ -922,18 +914,18 @@ impl AirTree {
snd: Option<String>, snd: Option<String>,
tipo: Rc<Type>, tipo: Rc<Type>,
pair: AirTree, pair: AirTree,
msg: Option<AirMsg>,
is_expect: bool, is_expect: bool,
then: AirTree, then: AirTree,
otherwise: AirTree,
) -> AirTree { ) -> AirTree {
AirTree::PairAccessor { AirTree::PairAccessor {
fst, fst,
snd, snd,
tipo, tipo,
is_expect, is_expect,
msg,
pair: pair.into(), pair: pair.into(),
then: then.into(), then: then.into(),
otherwise: otherwise.into(),
} }
} }
@ -949,7 +941,7 @@ impl AirTree {
vec![tuple], vec![tuple],
), ),
tipo.clone(), tipo.clone(),
None, AirTree::error(void(), false),
) )
} }
@ -976,11 +968,12 @@ impl AirTree {
} }
} }
pub fn list_empty(list: AirTree, msg: Option<AirMsg>, then: AirTree) -> AirTree { pub fn list_empty(list: AirTree, then: AirTree, otherwise: AirTree) -> AirTree {
AirTree::ListEmpty { AirTree::ListEmpty {
list: list.into(), list: list.into(),
msg,
then: then.into(), then: then.into(),
otherwise: otherwise.into(),
} }
} }
@ -1114,8 +1107,8 @@ impl AirTree {
AirTree::AssertConstr { AirTree::AssertConstr {
constr, constr,
constr_index, constr_index,
msg,
then, then,
otherwise,
} => { } => {
air_vec.push(Air::AssertConstr { air_vec.push(Air::AssertConstr {
constr_index: *constr_index, constr_index: *constr_index,
@ -1123,27 +1116,21 @@ impl AirTree {
// msg is first so we can pop it off first in uplc_gen // msg is first so we can pop it off first in uplc_gen
// if traces are on // if traces are on
if let Some(msg) = msg {
msg.to_air_tree().create_air_vec(air_vec);
}
constr.create_air_vec(air_vec); constr.create_air_vec(air_vec);
then.create_air_vec(air_vec); then.create_air_vec(air_vec);
otherwise.create_air_vec(air_vec);
} }
AirTree::AssertBool { AirTree::AssertBool {
is_true, is_true,
value, value,
msg,
then, then,
otherwise,
} => { } => {
air_vec.push(Air::AssertBool { is_true: *is_true }); air_vec.push(Air::AssertBool { is_true: *is_true });
if let Some(msg) = msg {
msg.to_air_tree().create_air_vec(air_vec);
}
value.create_air_vec(air_vec); value.create_air_vec(air_vec);
then.create_air_vec(air_vec); then.create_air_vec(air_vec);
otherwise.create_air_vec(air_vec);
} }
AirTree::ClauseGuard { AirTree::ClauseGuard {
subject_name, subject_name,
@ -1205,30 +1192,29 @@ impl AirTree {
AirTree::FieldsExpose { AirTree::FieldsExpose {
indices, indices,
record, record,
msg,
is_expect, is_expect,
then, then,
otherwise,
} => { } => {
air_vec.push(Air::FieldsExpose { air_vec.push(Air::FieldsExpose {
indices: indices.clone(), indices: indices.clone(),
is_expect: *is_expect, is_expect: *is_expect,
}); });
if let Some(msg) = msg {
msg.to_air_tree().create_air_vec(air_vec);
}
record.create_air_vec(air_vec); record.create_air_vec(air_vec);
then.create_air_vec(air_vec); then.create_air_vec(air_vec);
if *is_expect {
otherwise.create_air_vec(air_vec);
}
} }
AirTree::ListAccessor { AirTree::ListAccessor {
tipo, tipo,
names, names,
tail, tail,
list, list,
msg,
expect_level, expect_level,
then, then,
otherwise,
} => { } => {
air_vec.push(Air::ListAccessor { air_vec.push(Air::ListAccessor {
tipo: tipo.clone(), tipo: tipo.clone(),
@ -1237,12 +1223,11 @@ impl AirTree {
expect_level: *expect_level, expect_level: *expect_level,
}); });
if let Some(msg) = msg {
msg.to_air_tree().create_air_vec(air_vec);
}
list.create_air_vec(air_vec); list.create_air_vec(air_vec);
then.create_air_vec(air_vec); then.create_air_vec(air_vec);
if matches!(expect_level, ExpectLevel::Full | ExpectLevel::Items) {
otherwise.create_air_vec(air_vec);
}
} }
AirTree::ListExpose { AirTree::ListExpose {
tipo, tipo,
@ -1261,9 +1246,9 @@ impl AirTree {
names, names,
tipo, tipo,
tuple, tuple,
msg,
is_expect, is_expect,
then, then,
otherwise,
} => { } => {
air_vec.push(Air::TupleAccessor { air_vec.push(Air::TupleAccessor {
names: names.clone(), names: names.clone(),
@ -1271,21 +1256,20 @@ impl AirTree {
is_expect: *is_expect, is_expect: *is_expect,
}); });
if let Some(msg) = msg {
msg.to_air_tree().create_air_vec(air_vec);
}
tuple.create_air_vec(air_vec); tuple.create_air_vec(air_vec);
then.create_air_vec(air_vec); then.create_air_vec(air_vec);
if *is_expect {
otherwise.create_air_vec(air_vec);
}
} }
AirTree::PairAccessor { AirTree::PairAccessor {
fst, fst,
snd, snd,
tipo, tipo,
is_expect, is_expect,
msg,
pair, pair,
then, then,
otherwise,
} => { } => {
air_vec.push(Air::PairAccessor { air_vec.push(Air::PairAccessor {
fst: fst.clone(), fst: fst.clone(),
@ -1294,12 +1278,11 @@ impl AirTree {
is_expect: *is_expect, is_expect: *is_expect,
}); });
if let Some(msg) = msg {
msg.to_air_tree().create_air_vec(air_vec);
}
pair.create_air_vec(air_vec); pair.create_air_vec(air_vec);
then.create_air_vec(air_vec); then.create_air_vec(air_vec);
if *is_expect {
otherwise.create_air_vec(air_vec);
}
} }
AirTree::FieldsEmpty { constr, msg, then } => { AirTree::FieldsEmpty { constr, msg, then } => {
air_vec.push(Air::FieldsEmpty); air_vec.push(Air::FieldsEmpty);
@ -1311,15 +1294,16 @@ impl AirTree {
constr.create_air_vec(air_vec); constr.create_air_vec(air_vec);
then.create_air_vec(air_vec); then.create_air_vec(air_vec);
} }
AirTree::ListEmpty { list, msg, then } => { AirTree::ListEmpty {
list,
then,
otherwise,
} => {
air_vec.push(Air::ListEmpty); air_vec.push(Air::ListEmpty);
if let Some(msg) = msg {
msg.to_air_tree().create_air_vec(air_vec);
}
list.create_air_vec(air_vec); list.create_air_vec(air_vec);
then.create_air_vec(air_vec); then.create_air_vec(air_vec);
otherwise.create_air_vec(air_vec);
} }
AirTree::NoOp { then } => { AirTree::NoOp { then } => {
air_vec.push(Air::NoOp); air_vec.push(Air::NoOp);
@ -1417,17 +1401,15 @@ impl AirTree {
air_vec.push(Air::UnOp { op: *op }); air_vec.push(Air::UnOp { op: *op });
arg.create_air_vec(air_vec); arg.create_air_vec(air_vec);
} }
AirTree::CastFromData { tipo, value, msg } => { AirTree::CastFromData {
air_vec.push(Air::CastFromData { tipo,
tipo: tipo.clone(), value,
is_expect: msg.is_some(), otherwise,
}); } => {
air_vec.push(Air::CastFromData { tipo: tipo.clone() });
if let Some(msg) = msg {
msg.to_air_tree().create_air_vec(air_vec);
}
value.create_air_vec(air_vec); value.create_air_vec(air_vec);
otherwise.create_air_vec(air_vec);
} }
AirTree::CastToData { tipo, value } => { AirTree::CastToData { tipo, value } => {
air_vec.push(Air::CastToData { tipo: tipo.clone() }); air_vec.push(Air::CastToData { tipo: tipo.clone() });
@ -1532,7 +1514,7 @@ impl AirTree {
} }
AirTree::If { AirTree::If {
tipo, tipo,
pattern, condition: pattern,
then, then,
otherwise, otherwise,
} => { } => {
@ -2174,7 +2156,7 @@ impl AirTree {
); );
} }
AirTree::If { AirTree::If {
pattern, condition: pattern,
then, then,
otherwise, otherwise,
.. ..
@ -2590,7 +2572,7 @@ impl AirTree {
} }
} }
AirTree::If { AirTree::If {
pattern, condition: pattern,
then, then,
otherwise, otherwise,
.. ..