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::{
air::Air,
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,
ClauseProperties, CodeGenSpecialFuncs, CycleFunctionNames, HoistableFunction, Variant,
},
@ -42,6 +42,7 @@ use indexmap::{IndexMap, IndexSet};
use itertools::Itertools;
use petgraph::{algo, Graph};
use std::{collections::HashMap, rc::Rc};
use uplc::{
ast::{Constant as UplcConstant, Name, NamedDeBruijn, Program, Term, Type as UplcType},
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 msg_func = match self.tracing {
TraceLevel::Silent => None,
TraceLevel::Verbose | TraceLevel::Compact => {
if kind.is_expect() {
let otherwise = match (self.tracing, kind) {
(
TraceLevel::Silent,
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 {
TraceLevel::Silent => unreachable!("excluded from pattern guards"),
TraceLevel::Compact => get_line_columns_by_span(
module_build_name,
location,
&self.module_src,
)
.to_string(),
TraceLevel::Compact => {
get_line_columns_by_span(module_build_name, location, &self.module_src)
.to_string()
}
TraceLevel::Verbose => {
get_src_code_by_span(module_build_name, location, &self.module_src)
}
@ -283,10 +289,10 @@ impl<'a> CodeGenerator<'a> {
string(),
);
Some(self.special_functions.use_function_msg(msg_func_name))
} else {
None
}
let msg_string =
AirTree::string(self.special_functions.use_function_string(msg_func_name));
AirTree::trace(msg_string, void(), AirTree::error(void(), false))
}
};
@ -304,7 +310,7 @@ impl<'a> CodeGenerator<'a> {
kind: *kind,
remove_unused: kind.is_let(),
full_check: !tipo.is_data() && value.tipo().is_data() && kind.is_expect(),
msg_func,
otherwise,
},
)
} else {
@ -574,7 +580,7 @@ impl<'a> CodeGenerator<'a> {
kind: AssignmentKind::let_(),
remove_unused: false,
full_check: false,
msg_func: None,
otherwise: AirTree::error(void(), false),
},
)
} 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 {
branches,
final_else,
tipo,
..
} => AirTree::if_branches(
branches
.iter()
.map(|branch| {
(
self.build(&branch.condition, module_build_name, &[]),
self.build(&branch.body, module_build_name, &[]),
)
})
.collect_vec(),
tipo.clone(),
} => branches.iter().rfold(
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 {
@ -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
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() {
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);
AirTree::assert_bool(true, expr, props.msg_func.clone(), then)
AirTree::assert_bool(true, expr, then, props.otherwise.clone())
}
Pattern::Var { name, .. } => {
@ -925,7 +946,7 @@ impl<'a> CodeGenerator<'a> {
val,
&mut index_map,
pattern.location(),
props.msg_func,
props.otherwise,
);
let assign_expect = AirTree::let_assignment("_", expect, then);
@ -965,7 +986,7 @@ impl<'a> CodeGenerator<'a> {
val,
&mut index_map,
pattern.location(),
props.msg_func,
props.otherwise,
);
let assignment = AirTree::let_assignment("_", expect, then);
@ -975,7 +996,7 @@ impl<'a> CodeGenerator<'a> {
} else if !props.remove_unused {
AirTree::let_assignment(name, value, then)
} else {
AirTree::no_op(then)
then
}
}
@ -1025,7 +1046,7 @@ impl<'a> CodeGenerator<'a> {
kind: props.kind,
remove_unused: true,
full_check: props.full_check,
msg_func: props.msg_func.clone(),
otherwise: props.otherwise.clone(),
},
)
} else {
@ -1073,7 +1094,7 @@ impl<'a> CodeGenerator<'a> {
kind: props.kind,
remove_unused: true,
full_check: props.full_check,
msg_func: props.msg_func.clone(),
otherwise: props.otherwise.clone(),
},
)
} else {
@ -1088,20 +1109,20 @@ impl<'a> CodeGenerator<'a> {
elems.reverse();
if elements.is_empty() {
AirTree::list_empty(value, props.msg_func, then)
AirTree::list_empty(value, then, props.otherwise.clone())
} else {
AirTree::list_access(
elems,
tipo.clone(),
tail.is_some(),
value,
props.msg_func,
if props.full_check {
ExpectLevel::Full
} else {
ExpectLevel::Items
},
then,
props.otherwise.clone(),
)
}
}
@ -1164,7 +1185,7 @@ impl<'a> CodeGenerator<'a> {
kind: props.kind,
remove_unused: true,
full_check: props.full_check,
msg_func: props.msg_func.clone(),
otherwise: props.otherwise.clone(),
},
)
} else {
@ -1190,11 +1211,6 @@ impl<'a> CodeGenerator<'a> {
let local_value = AirTree::local_var(&constructor_name, tipo.clone());
let then = {
let (is_expect, msg) = if props.full_check {
(true, props.msg_func.clone())
} else {
(false, None)
};
assert!(fields.len() == 2);
AirTree::pair_access(
@ -1208,9 +1224,9 @@ impl<'a> CodeGenerator<'a> {
.unwrap(),
tipo.clone(),
local_value,
msg,
is_expect,
props.full_check,
then,
props.otherwise.clone(),
)
};
@ -1223,7 +1239,7 @@ impl<'a> CodeGenerator<'a> {
} if tipo.is_bool() => {
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() => {
@ -1308,7 +1324,7 @@ impl<'a> CodeGenerator<'a> {
kind: props.kind,
remove_unused: true,
full_check: props.full_check,
msg_func: props.msg_func.clone(),
otherwise: props.otherwise.clone(),
},
)
} else {
@ -1336,12 +1352,13 @@ impl<'a> CodeGenerator<'a> {
let then = if check_replaceable_opaque_type(tipo, &self.data_types) {
AirTree::let_assignment(&fields[0].1, local_value, then)
} else {
let (is_expect, msg) = if props.full_check {
(true, props.msg_func.clone())
} else {
(false, None)
};
AirTree::fields_expose(fields, local_value, msg, is_expect, then)
AirTree::fields_expose(
fields,
local_value,
props.full_check,
then,
props.otherwise.clone(),
)
};
let data_type = lookup_data_type_by_tipo(&self.data_types, tipo)
@ -1362,10 +1379,11 @@ impl<'a> CodeGenerator<'a> {
AirTree::assert_constr_index(
index,
AirTree::local_var(&constructor_name, tipo.clone()),
props.msg_func,
then,
props.otherwise.clone(),
)
} else {
assert!(data_type.constructors.len() == 1);
then
};
@ -1425,7 +1443,7 @@ impl<'a> CodeGenerator<'a> {
kind: props.kind,
remove_unused: true,
full_check: props.full_check,
msg_func: props.msg_func.clone(),
otherwise: props.otherwise.clone(),
},
)
} else {
@ -1439,15 +1457,16 @@ impl<'a> CodeGenerator<'a> {
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
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,
defined_data_types: &mut IndexMap<String, u64>,
location: Span,
msg_func: Option<AirMsg>,
otherwise: AirTree,
) -> AirTree {
assert!(tipo.get_generic().is_none());
// Shouldn't be needed but still here just in case
@ -1468,6 +1487,7 @@ impl<'a> CodeGenerator<'a> {
match uplc_type {
// primitives
// Untyped Data
Some(
UplcType::Integer
| UplcType::String
@ -1476,10 +1496,9 @@ impl<'a> CodeGenerator<'a> {
| UplcType::Unit
| UplcType::Bls12_381G1Element
| UplcType::Bls12_381G2Element
| UplcType::Bls12_381MlResult,
| UplcType::Bls12_381MlResult
| UplcType::Data,
) => value,
// Untyped Data
Some(UplcType::Data) => value,
// Map type
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()),
defined_data_types,
location,
msg_func.clone(),
otherwise.clone(),
);
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()),
defined_data_types,
location,
msg_func.clone(),
otherwise.clone(),
);
let anon_func_body = AirTree::pair_access(
@ -1516,9 +1535,9 @@ impl<'a> CodeGenerator<'a> {
Some(snd_name),
inner_list_type.clone(),
AirTree::local_var(&pair_name, inner_list_type.clone()),
msg_func.clone(),
true,
AirTree::let_assignment("_", expect_fst, expect_snd),
otherwise.clone(),
);
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()),
defined_data_types,
location,
msg_func.clone(),
otherwise.clone(),
);
tuple_expect_items.push(tuple_index_name);
@ -1602,9 +1621,9 @@ impl<'a> CodeGenerator<'a> {
tuple_expect_items,
tipo.clone(),
AirTree::local_var(&tuple_name, tipo.clone()),
msg_func,
true,
then,
otherwise,
);
AirTree::let_assignment(&tuple_name, value, tuple_access)
@ -1626,11 +1645,11 @@ impl<'a> CodeGenerator<'a> {
AirTree::cast_from_data(
AirTree::local_var(&item_name, data()),
inner_list_type.clone(),
msg_func.clone(),
otherwise.clone(),
),
defined_data_types,
location,
msg_func,
otherwise,
);
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()),
defined_data_types,
location,
msg_func.clone(),
otherwise.clone(),
);
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()),
defined_data_types,
location,
msg_func.clone(),
otherwise.clone(),
);
let pair_access = AirTree::pair_access(
@ -1717,9 +1736,9 @@ impl<'a> CodeGenerator<'a> {
Some(snd_name.clone()),
tipo.clone(),
AirTree::local_var(&pair_name, tipo.clone()),
msg_func.clone(),
true,
AirTree::let_assignment("_", expect_fst, expect_snd),
otherwise,
);
AirTree::let_assignment(&pair_name, value, pair_access)
@ -1800,7 +1819,7 @@ impl<'a> CodeGenerator<'a> {
AirTree::local_var(arg_name, arg_tipo),
defined_data_types,
location,
msg_term.clone(),
otherwise.clone(),
),
then,
)
@ -1830,9 +1849,9 @@ impl<'a> CodeGenerator<'a> {
),
tipo.clone(),
),
msg_term.clone(),
true,
constr_then,
otherwise.clone(),
)
};
@ -1895,12 +1914,7 @@ impl<'a> CodeGenerator<'a> {
let args = match self.tracing {
TraceLevel::Silent => vec![value],
TraceLevel::Compact | TraceLevel::Verbose => vec![
value,
msg_func
.expect("should be unreachable: no msg func with tracing enabled.")
.to_air_tree(),
],
TraceLevel::Compact | TraceLevel::Verbose => vec![value, otherwise],
};
let module_fn = ValueConstructorVariant::ModuleFn {
@ -2459,9 +2473,9 @@ impl<'a> CodeGenerator<'a> {
// One special usage of list access here
// 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
None,
ExpectLevel::None,
elems_then,
AirTree::error(void(), false),
)
} else {
assert!(defined_tails.len() >= elems.len());
@ -2540,9 +2554,9 @@ impl<'a> CodeGenerator<'a> {
name_index_assigns[1].0.clone(),
subject_tipo.clone(),
AirTree::local_var(props.clause_var_name.clone(), subject_tipo.clone()),
None,
false,
next_then,
AirTree::error(void(), false),
)
};
@ -2667,9 +2681,9 @@ impl<'a> CodeGenerator<'a> {
AirTree::fields_expose(
fields,
AirTree::local_var(props.clause_var_name.clone(), subject_tipo.clone()),
None,
false,
next_then,
AirTree::error(void(), false),
)
};
@ -2796,9 +2810,9 @@ impl<'a> CodeGenerator<'a> {
names,
subject_tipo.clone(),
AirTree::local_var(&props.clause_var_name, subject_tipo.clone()),
None,
false,
tuple_name_assigns,
AirTree::error(void(), false),
),
)
} else {
@ -3034,8 +3048,8 @@ impl<'a> CodeGenerator<'a> {
let actual_type = convert_opaque_type(&arg.tipo, &self.data_types, true);
let msg_func = match self.tracing {
TraceLevel::Silent => None,
let otherwise = match self.tracing {
TraceLevel::Silent => AirTree::error(void(), false),
TraceLevel::Compact | TraceLevel::Verbose => {
let msg = match self.tracing {
TraceLevel::Silent => {
@ -3059,7 +3073,11 @@ impl<'a> CodeGenerator<'a> {
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(),
remove_unused: false,
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>> {
// Going to mark the changes made to code gen after air tree implementation
let error_term = match self.tracing {
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 {
let convert_data_to_type = |term, tipo, otherwise| {
if otherwise == Term::Error {
builder::unknown_data_to_type(term, tipo)
} 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 otherwise = if matches!(expect_level, ExpectLevel::Full | ExpectLevel::Items) {
arg_stack.pop().unwrap()
} else {
Term::Error
};
let list_id = self.id_gen.next();
let mut id_list = vec![];
@ -4362,7 +4373,7 @@ impl<'a> CodeGenerator<'a> {
term,
true,
expect_level,
error_term,
otherwise,
)
.apply(value);
@ -4774,8 +4785,9 @@ impl<'a> CodeGenerator<'a> {
}
Air::CastFromData { tipo, .. } => {
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() {
let mut program: Program<Name> = Program {
@ -4834,6 +4846,7 @@ impl<'a> CodeGenerator<'a> {
let constr = arg_stack.pop().unwrap();
let mut term = arg_stack.pop().unwrap();
let otherwise = arg_stack.pop().unwrap();
term = Term::equals_integer()
.apply(Term::integer(constr_index.into()))
@ -4844,7 +4857,7 @@ impl<'a> CodeGenerator<'a> {
)
.apply(constr),
)
.delayed_if_then_else(term, error_term);
.delayed_if_then_else(term, otherwise);
Some(term)
}
@ -4852,11 +4865,12 @@ impl<'a> CodeGenerator<'a> {
let value = arg_stack.pop().unwrap();
let mut term = arg_stack.pop().unwrap();
let otherwise = arg_stack.pop().unwrap();
if is_true {
term = value.delayed_if_then_else(term, error_term)
term = value.delayed_if_then_else(term, otherwise)
} else {
term = value.delayed_if_then_else(error_term, term)
term = value.delayed_if_then_else(otherwise, term)
}
Some(term)
}
@ -5286,6 +5300,13 @@ impl<'a> CodeGenerator<'a> {
let value = 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();
id_list.push(list_id);
@ -5313,7 +5334,7 @@ impl<'a> CodeGenerator<'a> {
term,
false,
is_expect.into(),
error_term,
otherwise,
);
term = term.apply(
@ -5333,13 +5354,14 @@ impl<'a> CodeGenerator<'a> {
let value = arg_stack.pop().unwrap();
let mut term = arg_stack.pop().unwrap();
let otherwise = arg_stack.pop().unwrap();
term = Term::var(
self.special_functions
.use_function_uplc(CONSTR_FIELDS_EXPOSER.to_string()),
)
.apply(value)
.delayed_choose_list(term, error_term);
.delayed_choose_list(term, otherwise);
Some(term)
}
@ -5347,8 +5369,9 @@ impl<'a> CodeGenerator<'a> {
let value = 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)
}
@ -5545,6 +5568,11 @@ impl<'a> CodeGenerator<'a> {
let value = 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 mut id_list = vec![];
@ -5567,7 +5595,7 @@ impl<'a> CodeGenerator<'a> {
term,
false,
is_expect.into(),
error_term,
otherwise,
)
.apply(value);
@ -5583,6 +5611,12 @@ impl<'a> CodeGenerator<'a> {
let value = 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();
if let Some(name) = snd {
@ -5590,6 +5624,7 @@ impl<'a> CodeGenerator<'a> {
convert_data_to_type(
Term::snd_pair().apply(Term::var(format!("__pair_{list_id}"))),
&inner_types[1],
otherwise.clone(),
)
} else {
known_data_to_type(
@ -5604,6 +5639,7 @@ impl<'a> CodeGenerator<'a> {
convert_data_to_type(
Term::fst_pair().apply(Term::var(format!("__pair_{list_id}"))),
&inner_types[0],
otherwise,
)
} else {
known_data_to_type(

View File

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

View File

@ -1,5 +1,5 @@
use super::{
air::{Air, ExpectLevel},
air::ExpectLevel,
tree::{AirMsg, AirTree, TreePath},
};
use crate::{
@ -71,7 +71,7 @@ pub struct AssignmentProperties {
pub kind: TypedAssignmentKind,
pub remove_unused: bool,
pub full_check: bool,
pub msg_func: Option<AirMsg>,
pub otherwise: AirTree,
}
#[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 {
if !self.used_funcs.contains(&func_name) {
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
/// 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_debug(
pub fn unknown_data_to_type_otherwise(
term: Term<Name>,
field_type: &Type,
error_term: Term<Name>,
@ -1362,7 +1370,7 @@ pub fn list_access_to_uplc(
term: Term<Name>,
is_list_accessor: bool,
expect_level: ExpectLevel,
error_term: Term<Name>,
otherwise: Term<Name>,
) -> Term<Name> {
let names_len = names_types_ids.len();
@ -1393,7 +1401,7 @@ pub fn list_access_to_uplc(
}
return Term::var("empty_list")
.delayed_choose_list(term, error_term)
.delayed_choose_list(term, otherwise)
.lambda("empty_list");
}
@ -1412,16 +1420,16 @@ pub fn list_access_to_uplc(
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 error_term == Term::Error {
if otherwise == Term::Error {
unknown_data_to_type(
Term::head_list().apply(Term::var(tail_name.to_string())),
&tipo.to_owned(),
)
} else {
unknown_data_to_type_debug(
unknown_data_to_type_otherwise(
Term::head_list().apply(Term::var(tail_name.to_string())),
&tipo.to_owned(),
error_term.clone(),
otherwise.clone(),
)
}
} else {
@ -1456,22 +1464,22 @@ pub fn list_access_to_uplc(
ExpectLevel::None => acc.lambda(name).apply(head_item).lambda(tail_name),
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
acc.lambda(name).apply(head_item).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())
.delayed_choose_list(
error_term.clone(),
otherwise.clone(),
acc.lambda(name).apply(head_item),
)
.lambda(tail_name)
} else if error_term == Term::Error {
} else if otherwise == Term::Error {
// Check head is last item in this list
Term::tail_list()
.apply(Term::var(tail_name.to_string()))
.delayed_choose_list(acc, error_term.clone())
.delayed_choose_list(acc, otherwise.clone())
.lambda(name)
.apply(head_item)
.lambda(tail_name)
@ -1479,10 +1487,10 @@ pub fn list_access_to_uplc(
// Custom error if list is not empty after this head
Term::var(tail_name.to_string())
.delayed_choose_list(
error_term.clone(),
otherwise.clone(),
Term::tail_list()
.apply(Term::var(tail_name.to_string()))
.delayed_choose_list(acc, error_term.clone())
.delayed_choose_list(acc, otherwise.clone())
.lambda(name)
.apply(head_item),
)
@ -1498,7 +1506,7 @@ pub fn list_access_to_uplc(
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())))
.lambda(name)
.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
Term::var(tail_name.to_string())
.delayed_choose_list(
error_term.clone(),
otherwise.clone(),
acc.apply(
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 {
let success_branch = vec![(air_tree, AirTree::void())];
let otherwise = match trace {
TraceLevel::Silent | TraceLevel::Compact => AirTree::error(void(), true),
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>> {
@ -1841,22 +1848,3 @@ pub fn get_line_columns_by_span(
.line_and_column_number(span.start)
.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 {
constr_index: usize,
constr: Box<AirTree>,
msg: Option<AirMsg>,
then: Box<AirTree>,
otherwise: Box<AirTree>,
},
AssertBool {
is_true: bool,
value: Box<AirTree>,
msg: Option<AirMsg>,
then: Box<AirTree>,
otherwise: Box<AirTree>,
},
// Clause Guards
ClauseGuard {
@ -174,8 +174,8 @@ pub enum AirTree {
indices: Vec<(usize, String, Rc<Type>)>,
record: Box<AirTree>,
is_expect: bool,
msg: Option<AirMsg>,
then: Box<AirTree>,
otherwise: Box<AirTree>,
},
// List Access
ListAccessor {
@ -184,8 +184,8 @@ pub enum AirTree {
tail: bool,
list: Box<AirTree>,
expect_level: ExpectLevel,
msg: Option<AirMsg>,
then: Box<AirTree>,
otherwise: Box<AirTree>,
},
ListExpose {
tipo: Rc<Type>,
@ -199,8 +199,8 @@ pub enum AirTree {
tipo: Rc<Type>,
tuple: Box<AirTree>,
is_expect: bool,
msg: Option<AirMsg>,
then: Box<AirTree>,
otherwise: Box<AirTree>,
},
// Pair Access
PairAccessor {
@ -208,9 +208,9 @@ pub enum AirTree {
snd: Option<String>,
tipo: Rc<Type>,
is_expect: bool,
msg: Option<AirMsg>,
pair: Box<AirTree>,
then: Box<AirTree>,
otherwise: Box<AirTree>,
},
// Misc.
FieldsEmpty {
@ -220,8 +220,8 @@ pub enum AirTree {
},
ListEmpty {
list: Box<AirTree>,
msg: Option<AirMsg>,
then: Box<AirTree>,
otherwise: Box<AirTree>,
},
NoOp {
then: Box<AirTree>,
@ -297,7 +297,7 @@ pub enum AirTree {
CastFromData {
tipo: Rc<Type>,
value: Box<AirTree>,
msg: Option<AirMsg>,
otherwise: Box<AirTree>,
},
CastToData {
tipo: Rc<Type>,
@ -359,7 +359,7 @@ pub enum AirTree {
// If
If {
tipo: Rc<Type>,
pattern: Box<AirTree>,
condition: Box<AirTree>,
then: Box<AirTree>,
otherwise: Box<AirTree>,
},
@ -390,6 +390,10 @@ pub enum AirTree {
}
impl AirTree {
pub fn is_error(&self) -> bool {
matches!(self, AirTree::ErrorTerm { .. })
}
pub fn int(value: impl ToString) -> AirTree {
AirTree::Int {
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 {
tipo,
value: value.into(),
msg,
otherwise: otherwise.into(),
}
}
@ -580,28 +584,28 @@ impl AirTree {
pub fn assert_constr_index(
constr_index: usize,
constr: AirTree,
msg: Option<AirMsg>,
then: AirTree,
otherwise: AirTree,
) -> AirTree {
AirTree::AssertConstr {
constr_index,
constr: constr.into(),
msg,
then: then.into(),
otherwise: otherwise.into(),
}
}
pub fn assert_bool(
is_true: bool,
value: AirTree,
msg: Option<AirMsg>,
then: AirTree,
otherwise: AirTree,
) -> AirTree {
AirTree::AssertBool {
is_true,
value: value.into(),
msg,
then: then.into(),
otherwise: otherwise.into(),
}
}
@ -771,31 +775,18 @@ impl AirTree {
}
}
pub fn if_branches(
mut branches: Vec<(AirTree, AirTree)>,
pub fn if_branch(
tipo: Rc<Type>,
condition: AirTree,
branch: AirTree,
otherwise: AirTree,
) -> AirTree {
assert!(!branches.is_empty());
let last_if = branches.pop().unwrap();
let mut final_if = AirTree::If {
tipo: tipo.clone(),
pattern: Box::new(last_if.0),
then: Box::new(last_if.1),
AirTree::If {
tipo,
condition: condition.into(),
then: branch.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 {
@ -845,23 +836,23 @@ impl AirTree {
vec![list_of_fields],
),
tipo.clone(),
None,
AirTree::error(void(), false),
)
}
pub fn fields_expose(
indices: Vec<(usize, String, Rc<Type>)>,
record: AirTree,
msg: Option<AirMsg>,
is_expect: bool,
then: AirTree,
otherwise: AirTree,
) -> AirTree {
AirTree::FieldsExpose {
indices,
record: record.into(),
msg,
is_expect,
then: then.into(),
otherwise: otherwise.into(),
}
}
@ -870,9 +861,10 @@ impl AirTree {
tipo: Rc<Type>,
tail: bool,
list: AirTree,
msg: Option<AirMsg>,
expect_level: ExpectLevel,
then: AirTree,
otherwise: AirTree,
) -> AirTree {
AirTree::ListAccessor {
tipo,
@ -880,8 +872,8 @@ impl AirTree {
tail,
list: list.into(),
expect_level,
msg,
then: then.into(),
otherwise: otherwise.into(),
}
}
@ -903,17 +895,17 @@ impl AirTree {
names: Vec<String>,
tipo: Rc<Type>,
tuple: AirTree,
msg: Option<AirMsg>,
is_expect: bool,
then: AirTree,
otherwise: AirTree,
) -> AirTree {
AirTree::TupleAccessor {
names,
tipo,
tuple: tuple.into(),
msg,
is_expect,
then: then.into(),
otherwise: otherwise.into(),
}
}
@ -922,18 +914,18 @@ impl AirTree {
snd: Option<String>,
tipo: Rc<Type>,
pair: AirTree,
msg: Option<AirMsg>,
is_expect: bool,
then: AirTree,
otherwise: AirTree,
) -> AirTree {
AirTree::PairAccessor {
fst,
snd,
tipo,
is_expect,
msg,
pair: pair.into(),
then: then.into(),
otherwise: otherwise.into(),
}
}
@ -949,7 +941,7 @@ impl AirTree {
vec![tuple],
),
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 {
list: list.into(),
msg,
then: then.into(),
otherwise: otherwise.into(),
}
}
@ -1114,8 +1107,8 @@ impl AirTree {
AirTree::AssertConstr {
constr,
constr_index,
msg,
then,
otherwise,
} => {
air_vec.push(Air::AssertConstr {
constr_index: *constr_index,
@ -1123,27 +1116,21 @@ impl AirTree {
// msg is first so we can pop it off first in uplc_gen
// if traces are on
if let Some(msg) = msg {
msg.to_air_tree().create_air_vec(air_vec);
}
constr.create_air_vec(air_vec);
then.create_air_vec(air_vec);
otherwise.create_air_vec(air_vec);
}
AirTree::AssertBool {
is_true,
value,
msg,
then,
otherwise,
} => {
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);
then.create_air_vec(air_vec);
otherwise.create_air_vec(air_vec);
}
AirTree::ClauseGuard {
subject_name,
@ -1205,30 +1192,29 @@ impl AirTree {
AirTree::FieldsExpose {
indices,
record,
msg,
is_expect,
then,
otherwise,
} => {
air_vec.push(Air::FieldsExpose {
indices: indices.clone(),
is_expect: *is_expect,
});
if let Some(msg) = msg {
msg.to_air_tree().create_air_vec(air_vec);
}
record.create_air_vec(air_vec);
then.create_air_vec(air_vec);
if *is_expect {
otherwise.create_air_vec(air_vec);
}
}
AirTree::ListAccessor {
tipo,
names,
tail,
list,
msg,
expect_level,
then,
otherwise,
} => {
air_vec.push(Air::ListAccessor {
tipo: tipo.clone(),
@ -1237,12 +1223,11 @@ impl AirTree {
expect_level: *expect_level,
});
if let Some(msg) = msg {
msg.to_air_tree().create_air_vec(air_vec);
}
list.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 {
tipo,
@ -1261,9 +1246,9 @@ impl AirTree {
names,
tipo,
tuple,
msg,
is_expect,
then,
otherwise,
} => {
air_vec.push(Air::TupleAccessor {
names: names.clone(),
@ -1271,21 +1256,20 @@ impl AirTree {
is_expect: *is_expect,
});
if let Some(msg) = msg {
msg.to_air_tree().create_air_vec(air_vec);
}
tuple.create_air_vec(air_vec);
then.create_air_vec(air_vec);
if *is_expect {
otherwise.create_air_vec(air_vec);
}
}
AirTree::PairAccessor {
fst,
snd,
tipo,
is_expect,
msg,
pair,
then,
otherwise,
} => {
air_vec.push(Air::PairAccessor {
fst: fst.clone(),
@ -1294,12 +1278,11 @@ impl AirTree {
is_expect: *is_expect,
});
if let Some(msg) = msg {
msg.to_air_tree().create_air_vec(air_vec);
}
pair.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 } => {
air_vec.push(Air::FieldsEmpty);
@ -1311,15 +1294,16 @@ impl AirTree {
constr.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);
if let Some(msg) = msg {
msg.to_air_tree().create_air_vec(air_vec);
}
list.create_air_vec(air_vec);
then.create_air_vec(air_vec);
otherwise.create_air_vec(air_vec);
}
AirTree::NoOp { then } => {
air_vec.push(Air::NoOp);
@ -1417,17 +1401,15 @@ impl AirTree {
air_vec.push(Air::UnOp { op: *op });
arg.create_air_vec(air_vec);
}
AirTree::CastFromData { tipo, value, msg } => {
air_vec.push(Air::CastFromData {
tipo: tipo.clone(),
is_expect: msg.is_some(),
});
if let Some(msg) = msg {
msg.to_air_tree().create_air_vec(air_vec);
}
AirTree::CastFromData {
tipo,
value,
otherwise,
} => {
air_vec.push(Air::CastFromData { tipo: tipo.clone() });
value.create_air_vec(air_vec);
otherwise.create_air_vec(air_vec);
}
AirTree::CastToData { tipo, value } => {
air_vec.push(Air::CastToData { tipo: tipo.clone() });
@ -1532,7 +1514,7 @@ impl AirTree {
}
AirTree::If {
tipo,
pattern,
condition: pattern,
then,
otherwise,
} => {
@ -2174,7 +2156,7 @@ impl AirTree {
);
}
AirTree::If {
pattern,
condition: pattern,
then,
otherwise,
..
@ -2590,7 +2572,7 @@ impl AirTree {
}
}
AirTree::If {
pattern,
condition: pattern,
then,
otherwise,
..