pub mod air; pub mod builder; pub mod tree; use std::sync::Arc; use indexmap::{IndexMap, IndexSet}; use itertools::Itertools; use uplc::{ ast::{Constant as UplcConstant, Name, NamedDeBruijn, Program, Term, Type as UplcType}, builder::{CONSTR_FIELDS_EXPOSER, CONSTR_GET_FIELD, CONSTR_INDEX_EXPOSER, EXPECT_ON_LIST}, builtins::DefaultFunction, machine::cost_model::ExBudget, optimize::aiken_optimize_and_intern, parser::interner::Interner, }; use crate::{ ast::{ AssignmentKind, BinOp, Pattern, Span, TypedArg, TypedClause, TypedDataType, TypedFunction, TypedValidator, UnOp, }, builtins::{bool, data, int, void}, expr::TypedExpr, gen_uplc::builder::{ convert_opaque_type, erase_opaque_type_operations, find_and_replace_generics, get_arg_type_name, get_generic_id_and_type, get_variant_name, monomorphize, wrap_as_multi_validator, wrap_validator_condition, CodeGenFunction, SpecificClause, }, tipo::{ ModuleValueConstructor, PatternConstructor, Type, TypeInfo, ValueConstructor, ValueConstructorVariant, }, IdGenerator, }; use self::{ air::Air, builder::{ cast_validator_args, convert_type_to_data, lookup_data_type_by_tipo, modify_self_calls, rearrange_clauses, AssignmentProperties, ClauseProperties, DataTypeKey, FunctionAccessKey, UserFunction, }, tree::{AirExpression, AirTree, TreePath}, }; #[derive(Clone)] pub struct CodeGenerator<'a> { defined_functions: IndexMap, functions: IndexMap, data_types: IndexMap, module_types: IndexMap<&'a String, &'a TypeInfo>, needs_field_access: bool, code_gen_functions: IndexMap, zero_arg_functions: IndexMap<(FunctionAccessKey, String), Vec>, tracing: bool, id_gen: IdGenerator, } impl<'a> CodeGenerator<'a> { pub fn new( functions: IndexMap, data_types: IndexMap, module_types: IndexMap<&'a String, &'a TypeInfo>, tracing: bool, ) -> Self { CodeGenerator { defined_functions: IndexMap::new(), functions, data_types, module_types, needs_field_access: false, code_gen_functions: IndexMap::new(), zero_arg_functions: IndexMap::new(), tracing, id_gen: IdGenerator::new(), } } pub fn reset(&mut self) { self.code_gen_functions = IndexMap::new(); self.zero_arg_functions = IndexMap::new(); self.needs_field_access = false; self.defined_functions = IndexMap::new(); self.id_gen = IdGenerator::new(); } pub fn insert_function( &mut self, module_name: String, function_name: String, _variant_name: String, value: &'a TypedFunction, ) -> Option<&'a TypedFunction> { self.functions.insert( FunctionAccessKey { module_name, function_name, }, value, ) } pub fn generate( &mut self, TypedValidator { fun, other_fun, params, .. }: &TypedValidator, ) -> Program { let mut air_tree_fun = self.build(&fun.body); air_tree_fun = wrap_validator_condition(air_tree_fun); let mut validator_args_tree = self.check_validator_args(&fun.arguments, true, air_tree_fun); validator_args_tree = AirTree::no_op().hoist_over(validator_args_tree); let full_tree = self.hoist_functions_to_validator(validator_args_tree); // optimizations on air tree let full_vec = full_tree.to_vec(); // println!("FULL VEC {:#?}", full_vec); let mut term = self.uplc_code_gen(full_vec); if let Some(other) = other_fun { self.reset(); let mut air_tree_fun_other = self.build(&other.body); air_tree_fun_other = wrap_validator_condition(air_tree_fun_other); let mut validator_args_tree_other = self.check_validator_args(&fun.arguments, true, air_tree_fun_other); validator_args_tree_other = AirTree::no_op().hoist_over(validator_args_tree_other); let full_tree_other = self.hoist_functions_to_validator(validator_args_tree_other); // optimizations on air tree let full_vec_other = full_tree_other.to_vec(); // println!("FULL VEC {:#?}", full_vec_other); let other_term = self.uplc_code_gen(full_vec_other); let (spend, mint) = if other.arguments.len() > fun.arguments.len() { (other_term, term) } else { (term, other_term) }; term = wrap_as_multi_validator(spend, mint); self.needs_field_access = true; } term = cast_validator_args(term, params); self.finalize(term) } pub fn generate_test(&mut self, test_body: &TypedExpr) -> Program { let mut air_tree = self.build(test_body); air_tree = AirTree::no_op().hoist_over(air_tree); let full_tree = self.hoist_functions_to_validator(air_tree); // optimizations on air tree let full_vec = full_tree.to_vec(); println!("FULL VEC {:#?}", full_vec); let term = self.uplc_code_gen(full_vec); self.finalize(term) } fn finalize(&mut self, mut term: Term) -> Program { if self.needs_field_access { term = term .constr_get_field() .constr_fields_exposer() .constr_index_exposer(); } // TODO: Once SOP is implemented, new version is 1.1.0 let mut program = Program { version: (1, 0, 0), term, }; program = aiken_optimize_and_intern(program); println!("Program: {}", program.to_pretty()); // This is very important to call here. // If this isn't done, re-using the same instance // of the generator will result in free unique errors // among other unpredictable things. In fact, // switching to a shared code generator caused some // instability issues and we fixed it by placing this // method here. self.reset(); program } fn build(&mut self, body: &TypedExpr) -> AirTree { match body { TypedExpr::UInt { value, .. } => AirTree::int(value), TypedExpr::String { value, .. } => AirTree::string(value), TypedExpr::ByteArray { bytes, .. } => AirTree::byte_array(bytes.clone()), TypedExpr::Sequence { expressions, .. } | TypedExpr::Pipeline { expressions, .. } => { let mut expressions = expressions.clone(); let mut last_exp = self.build(&expressions.pop().unwrap_or_else(|| { unreachable!("Sequence or Pipeline should have at least one expression") })); while let Some(expression) = expressions.pop() { let exp_tree = self.build(&expression); last_exp = exp_tree.hoist_over(last_exp); } last_exp } TypedExpr::Var { constructor, name, .. } => AirTree::var(constructor.clone(), name, ""), TypedExpr::Fn { args, body, .. } => AirTree::anon_func( args.iter() .map(|arg| arg.arg_name.get_variable_name().unwrap_or("_").to_string()) .collect_vec(), self.build(body), ), TypedExpr::List { tipo, elements, tail, .. } => AirTree::list( elements.iter().map(|elem| self.build(elem)).collect_vec(), tipo.clone(), tail.as_ref().map(|tail| self.build(tail)), ), TypedExpr::Call { tipo, fun, args, .. } => match fun.as_ref() { TypedExpr::Var { constructor: ValueConstructor { variant: ValueConstructorVariant::Record { name: constr_name, .. }, .. }, .. } | TypedExpr::ModuleSelect { constructor: ModuleValueConstructor::Record { name: constr_name, .. }, .. } => { let Some(data_type) = lookup_data_type_by_tipo(&self.data_types, tipo) else {unreachable!("Creating a record with no record definition.")}; let (constr_index, _) = data_type .constructors .iter() .enumerate() .find(|(_, dt)| &dt.name == constr_name) .unwrap(); let constr_args = args.iter().map(|arg| self.build(&arg.value)).collect_vec(); AirTree::create_constr(constr_index, tipo.clone(), constr_args) } TypedExpr::Var { constructor: ValueConstructor { variant: ValueConstructorVariant::ModuleFn { builtin, .. }, .. }, .. } => { let Some(fun_arg_types) = fun.tipo().arg_types() else {unreachable!("Expected a function type with arguments")}; let func_args = args .iter() .zip(fun_arg_types) .map(|(arg, arg_tipo)| { let mut arg_val = self.build(&arg.value); if arg_tipo.is_data() && !arg.value.tipo().is_data() { arg_val = AirTree::cast_to_data(arg_val, arg.value.tipo()) } arg_val }) .collect_vec(); if let Some(func) = builtin { AirTree::builtin(*func, tipo.clone(), func_args) } else { AirTree::call(self.build(fun.as_ref()), tipo.clone(), func_args) } } TypedExpr::ModuleSelect { module_name, constructor: ModuleValueConstructor::Fn { name, .. }, .. } => { let type_info = self.module_types.get(module_name).unwrap(); let value = type_info.values.get(name).unwrap(); let ValueConstructorVariant::ModuleFn { builtin, .. } = &value.variant else {unreachable!("Missing module function definition")}; let Some(fun_arg_types) = fun.tipo().arg_types() else {unreachable!("Expected a function type with arguments")}; let func_args = args .iter() .zip(fun_arg_types) .map(|(arg, arg_tipo)| { let mut arg_val = self.build(&arg.value); if arg_tipo.is_data() && !arg.value.tipo().is_data() { arg_val = AirTree::cast_to_data(arg_val, arg.value.tipo()) } arg_val }) .collect_vec(); if let Some(func) = builtin { AirTree::builtin(*func, tipo.clone(), func_args) } else { AirTree::call(self.build(fun.as_ref()), tipo.clone(), func_args) } } _ => { let Some(fun_arg_types) = fun.tipo().arg_types() else {unreachable!("Expected a function type with arguments")}; let func_args = args .iter() .zip(fun_arg_types) .map(|(arg, arg_tipo)| { let mut arg_val = self.build(&arg.value); if arg_tipo.is_data() && !arg.value.tipo().is_data() { arg_val = AirTree::cast_to_data(arg_val, arg.value.tipo()) } arg_val }) .collect_vec(); AirTree::call(self.build(fun.as_ref()), tipo.clone(), func_args) } }, TypedExpr::BinOp { name, left, right, tipo, .. } => AirTree::binop( *name, tipo.clone(), self.build(left), self.build(right), left.tipo(), ), TypedExpr::Assignment { tipo, value, pattern, kind, .. } => { let replaced_type = convert_opaque_type(tipo, &self.data_types); let air_value = self.build(value); self.assignment( pattern, air_value, &replaced_type, AssignmentProperties { value_type: value.tipo(), kind: *kind, remove_unused: kind.is_let(), full_check: !tipo.is_data() && value.tipo().is_data() && kind.is_expect(), }, ) } TypedExpr::Trace { tipo, then, text, .. } => AirTree::trace(self.build(text), tipo.clone(), self.build(then)), TypedExpr::When { tipo, subject, clauses, .. } => { let mut clauses = clauses.clone(); if clauses.is_empty() { unreachable!("We should have one clause at least") } else if clauses.len() == 1 { let last_clause = clauses.pop().unwrap(); let clause_then = self.build(&last_clause.then); let subject_val = self.build(subject); let assignment = self.assignment( &last_clause.pattern, subject_val, tipo, AssignmentProperties { value_type: subject.tipo(), kind: AssignmentKind::Let, remove_unused: false, full_check: false, }, ); assignment.hoist_over(clause_then) } else { clauses = if subject.tipo().is_list() { rearrange_clauses(clauses) } else { clauses }; let last_clause = clauses.pop().unwrap(); let constr_var = format!( "__when_var_span_{}_{}", subject.location().start, subject.location().end ); let subject_name = format!( "__subject_var_span_{}_{}", subject.location().start, subject.location().end ); let clauses = self.handle_each_clause( &clauses, last_clause, &subject.tipo(), &mut ClauseProperties::init( &subject.tipo(), constr_var.clone(), subject_name.clone(), ), ); let constr_assign = AirTree::let_assignment(&constr_var, self.build(subject)); let when_assign = AirTree::when( subject_name, tipo.clone(), subject.tipo(), AirTree::local_var(constr_var, subject.tipo()), clauses, ); constr_assign.hoist_over(when_assign) } } TypedExpr::If { branches, final_else, tipo, .. } => AirTree::if_branches( branches .iter() .map(|branch| (self.build(&branch.condition), self.build(&branch.body))) .collect_vec(), tipo.clone(), self.build(final_else), ), TypedExpr::RecordAccess { tipo, index, record, .. } => AirTree::record_access(*index, tipo.clone(), self.build(record)), TypedExpr::ModuleSelect { tipo, module_name, constructor, .. } => match constructor { ModuleValueConstructor::Record { name, arity, tipo, field_map, .. } => { let data_type = lookup_data_type_by_tipo(&self.data_types, tipo); let val_constructor = ValueConstructor::public( tipo.clone(), ValueConstructorVariant::Record { name: name.clone(), arity: *arity, field_map: field_map.clone(), location: Span::empty(), module: module_name.clone(), constructors_count: data_type .unwrap_or_else(|| { unreachable!("Created a module type without a definition?") }) .constructors .len() as u16, }, ); AirTree::var(val_constructor, name, "") } ModuleValueConstructor::Fn { name, module, .. } => { let func = self.functions.get(&FunctionAccessKey { module_name: module_name.clone(), function_name: name.clone(), }); let type_info = self.module_types.get(module_name).unwrap(); let value = type_info.values.get(name).unwrap(); if let Some(_func) = func { AirTree::var( ValueConstructor::public(tipo.clone(), value.variant.clone()), format!("{module}_{name}"), "", ) } else { let ValueConstructorVariant::ModuleFn { builtin: Some(builtin), .. } = &value.variant else { unreachable!("Didn't find the function definition.") }; AirTree::builtin(*builtin, tipo.clone(), vec![]) } } ModuleValueConstructor::Constant { literal, .. } => builder::constants_ir(literal), }, TypedExpr::Tuple { tipo, elems, .. } => AirTree::tuple( elems.iter().map(|elem| self.build(elem)).collect_vec(), tipo.clone(), ), TypedExpr::TupleIndex { tipo, index, tuple, .. } => AirTree::tuple_index(*index, tipo.clone(), self.build(tuple)), TypedExpr::ErrorTerm { tipo, .. } => AirTree::error(tipo.clone()), TypedExpr::RecordUpdate { tipo, spread, args, .. } => { let mut index_types = vec![]; let mut update_args = vec![]; let mut highest_index = 0; for arg in args .iter() .sorted_by(|arg1, arg2| arg1.index.cmp(&arg2.index)) { let arg_val = self.build(&arg.value); if arg.index > highest_index { highest_index = arg.index; } index_types.push((arg.index, arg.value.tipo())); update_args.push(arg_val); } AirTree::record_update( index_types, highest_index, tipo.clone(), self.build(spread), update_args, ) } TypedExpr::UnOp { value, op, .. } => AirTree::unop(*op, self.build(value)), } } pub fn assignment( &mut self, pattern: &Pattern>, mut value: AirTree, tipo: &Arc, props: AssignmentProperties, ) -> AirTree { if props.value_type.is_data() && props.kind.is_expect() && !tipo.is_data() { value = AirTree::cast_from_data(value, tipo.clone()); } else if !props.value_type.is_data() && tipo.is_data() { value = AirTree::cast_to_data(value, props.value_type.clone()); } match pattern { Pattern::Int { value: expected_int, location, .. } => { if props.kind.is_expect() { let name = format!( "__expected_by_{}_span_{}_{}", expected_int, location.start, location.end ); let assignment = AirTree::let_assignment(&name, value); let expect = AirTree::binop( BinOp::Eq, bool(), AirTree::int(expected_int), AirTree::local_var(name, int()), int(), ); AirTree::assert_bool(true, assignment.hoist_over(expect)) } else { unreachable!("Code Gen should never reach here") } } Pattern::Var { name, .. } => { if props.full_check { let mut index_map = IndexMap::new(); let non_opaque_tipo = convert_opaque_type(tipo, &self.data_types); let assignment = AirTree::let_assignment(name, value); let val = AirTree::local_var(name, tipo.clone()); if tipo.is_primitive() { AirTree::let_assignment(name, assignment.hoist_over(val)) } else { let expect = self.expect_type_assign( &non_opaque_tipo, val.clone(), &mut index_map, pattern.location(), ); let assign_expect = AirTree::let_assignment("_", expect); AirTree::let_assignment( name, assignment.hoist_over(assign_expect.hoist_over(val)), ) } } else { AirTree::let_assignment(name, value) } } Pattern::Assign { name, pattern, .. } => { let inner_pattern = self.assignment(pattern, AirTree::local_var(name, tipo.clone()), tipo, props); let assign = AirTree::let_assignment(name, value); AirTree::UnhoistedSequence(vec![assign, inner_pattern]) } Pattern::Discard { name, .. } => { if props.full_check { let name = &format!("__discard_expect_{}", name); let mut index_map = IndexMap::new(); let tipo = convert_opaque_type(tipo, &self.data_types); let assignment = AirTree::let_assignment(name, value); let val = AirTree::local_var(name, tipo.clone()); if tipo.is_primitive() { AirTree::let_assignment(name, assignment.hoist_over(val)) } else { let expect = self.expect_type_assign( &tipo, val.clone(), &mut index_map, pattern.location(), ); let assign_expect = AirTree::let_assignment("_", expect); AirTree::let_assignment( name, assignment.hoist_over(assign_expect.hoist_over(val)), ) } } else if !props.remove_unused { AirTree::let_assignment(name, value) } else { AirTree::no_op() } } Pattern::List { elements, tail, .. } => { assert!(tipo.is_list()); assert!(props.kind.is_expect()); let list_elem_types = tipo.get_inner_types(); let list_elem_type = list_elem_types .get(0) .unwrap_or_else(|| unreachable!("No list element type?")); let mut elems = elements .iter() .enumerate() .map(|(index, elem)| { let elem_name = match elem { Pattern::Var { name, .. } => name.to_string(), Pattern::Assign { name, .. } => name.to_string(), Pattern::Discard { name, .. } => { if props.full_check { format!("__discard_{}", name) } else { "_".to_string() } } _ => format!( "elem_{}_span_{}_{}", index, elem.location().start, elem.location().end ), }; let val = AirTree::local_var(&elem_name, list_elem_type.clone()); ( elem_name, self.assignment( elem, val, list_elem_type, AssignmentProperties { value_type: list_elem_type.clone(), kind: props.kind, remove_unused: true, full_check: props.full_check, }, ), ) }) .collect_vec(); // If Some then push tail onto elems tail.iter().for_each(|tail| { let tail_name = match tail.as_ref() { Pattern::Var { name, .. } => name.to_string(), Pattern::Assign { name, .. } => name.to_string(), Pattern::Discard { name, .. } => { if props.kind.is_expect() && props.value_type.is_data() && !tipo.is_data() { format!("__discard_{}", name) } else { "_".to_string() } } _ => format!( "tail_span_{}_{}", tail.location().start, tail.location().end ), }; let val = AirTree::local_var(&tail_name, tipo.clone()); elems.push(( tail_name, self.assignment( tail, val, tipo, AssignmentProperties { value_type: tipo.clone(), kind: props.kind, remove_unused: true, full_check: props.full_check, }, ), )); }); let names = elems.iter().map(|(name, _)| name.to_string()).collect_vec(); let list_access = if elements.is_empty() { AirTree::list_empty(value) } else { AirTree::list_access(names, tipo.clone(), tail.is_some(), tail.is_none(), value) }; let mut sequence = vec![list_access]; sequence.append(&mut elems.into_iter().map(|(_, elem)| elem).collect_vec()); AirTree::UnhoistedSequence(sequence) } Pattern::Constructor { arguments, constructor, name, .. } => { let mut sequence = vec![]; if tipo.is_bool() { assert!(props.kind.is_expect()); AirTree::assert_bool(name == "True", value) } else if tipo.is_void() { AirTree::let_assignment("_", value) } else { if props.kind.is_expect() { let data_type = lookup_data_type_by_tipo(&self.data_types, tipo) .unwrap_or_else(|| panic!("Failed to find definition for {}", name)); if data_type.constructors.len() > 1 || props.full_check { let (index, _) = data_type .constructors .iter() .enumerate() .find(|(_, constr)| constr.name == *name) .unwrap_or_else(|| { panic!("Found constructor type {} with 0 constructors", name) }); let constructor_name = format!( "__constructor_{}_span_{}_{}", name, pattern.location().start, pattern.location().end ); // I'm consuming `value` here let constructor_val = AirTree::let_assignment(&constructor_name, value); sequence.push(constructor_val); let assert_constr = AirTree::assert_constr_index( index, AirTree::local_var(&constructor_name, tipo.clone()), ); sequence.push(assert_constr); //I'm reusing the `value` pointer value = AirTree::local_var(constructor_name, tipo.clone()); } } let field_map = match constructor { PatternConstructor::Record { field_map, .. } => field_map.clone(), }; let mut type_map: IndexMap> = IndexMap::new(); for (index, arg) in tipo.arg_types().unwrap().iter().enumerate() { let field_type = arg.clone(); type_map.insert(index, field_type); } let fields = arguments .iter() .enumerate() .map(|(index, arg)| { let label = arg.label.clone().unwrap_or_default(); let field_index = if let Some(field_map) = &field_map { *field_map.fields.get(&label).map(|x| &x.0).unwrap_or(&index) } else { index }; let field_name = match &arg.value { Pattern::Var { name, .. } => name.to_string(), Pattern::Assign { name, .. } => name.to_string(), Pattern::Discard { name, .. } => { if props.full_check { format!("__discard_{}", name) } else { "_".to_string() } } _ => format!( "field_{}_span_{}_{}", field_index, arg.value.location().start, arg.value.location().end ), }; let arg_type = type_map.get(&field_index).unwrap_or_else(|| { unreachable!( "Missing type for field {} of constr {}", field_index, name ) }); let val = AirTree::local_var(field_name.to_string(), arg_type.clone()); ( field_index, field_name, arg_type.clone(), self.assignment( &arg.value, val, arg_type, AssignmentProperties { value_type: props.value_type.clone(), kind: props.kind, remove_unused: true, full_check: props.full_check, }, ), ) }) .collect_vec(); let indices = fields .iter() .map(|(index, name, tipo, _)| (*index, name.to_string(), tipo.clone())) .collect_vec(); // This `value` is either value param that was passed in or // local var sequence.push(AirTree::fields_expose(indices, props.full_check, value)); sequence.append( &mut fields .into_iter() .map(|(_, _, _, field)| field) .collect_vec(), ); AirTree::UnhoistedSequence(sequence) } } Pattern::Tuple { elems, location, .. } => { let mut type_map: IndexMap> = IndexMap::new(); let mut sequence = vec![]; for (index, arg) in tipo.get_inner_types().iter().enumerate() { let field_type = arg.clone(); type_map.insert(index, field_type); } let elems = elems .iter() .enumerate() .map(|(index, arg)| { let tuple_name = match &arg { Pattern::Var { name, .. } => name.to_string(), Pattern::Assign { name, .. } => name.to_string(), Pattern::Discard { name, .. } => { if props.full_check { format!("__discard_{}", name) } else { "_".to_string() } } _ => format!( "tuple_{}_span_{}_{}", index, arg.location().start, arg.location().end ), }; let arg_type = type_map.get(&index).unwrap_or_else(|| { unreachable!( "Missing type for tuple index {} of tuple_span_{}_{}", index, location.start, location.end ) }); let val = AirTree::local_var(tuple_name.to_string(), arg_type.clone()); ( tuple_name, self.assignment( arg, val, arg_type, AssignmentProperties { value_type: props.value_type.clone(), kind: props.kind, remove_unused: true, full_check: props.full_check, }, ), ) }) .collect_vec(); let indices = elems.iter().map(|(name, _)| name.to_string()).collect_vec(); // This `value` is either value param that was passed in or // local var sequence.push(AirTree::tuple_access( indices, tipo.clone(), props.full_check, value, )); sequence.append(&mut elems.into_iter().map(|(_, field)| field).collect_vec()); AirTree::UnhoistedSequence(sequence) } } } pub fn expect_type_assign( &mut self, tipo: &Arc, value: AirTree, defined_data_types: &mut IndexMap, location: Span, ) -> AirTree { assert!(tipo.get_generic().is_none()); if tipo.is_primitive() { // Since we would return void anyway and ignore then we can just return value here and ignore value } else if tipo.is_map() { assert!(!tipo.get_inner_types().is_empty()); let inner_list_type = &tipo.get_inner_types()[0]; let inner_pair_types = inner_list_type.get_inner_types(); assert!(inner_pair_types.len() == 2); let map_name = format!("__map_span_{}_{}", location.start, location.end); let pair_name = format!("__pair_span_{}_{}", location.start, location.end); let fst_name = format!("__pair_fst_span_{}_{}", location.start, location.end); let snd_name = format!("__pair_snd_span_{}_{}", location.start, location.end); let assign = AirTree::let_assignment(&map_name, value); let tuple_access = AirTree::tuple_access( vec![fst_name.clone(), snd_name.clone()], inner_list_type.clone(), false, AirTree::local_var(&pair_name, inner_list_type.clone()), ); let expect_fst = self.expect_type_assign( &inner_pair_types[0], AirTree::local_var(fst_name, inner_pair_types[0].clone()), defined_data_types, location, ); let expect_snd = self.expect_type_assign( &inner_pair_types[1], AirTree::local_var(snd_name, inner_pair_types[1].clone()), defined_data_types, location, ); let anon_func_body = AirTree::UnhoistedSequence(vec![ tuple_access, AirTree::let_assignment("_", expect_fst), ]) .hoist_over(expect_snd); let unwrap_function = AirTree::anon_func(vec![pair_name], anon_func_body); let function = self.code_gen_functions.get(EXPECT_ON_LIST); if function.is_none() { let expect_list_func = AirTree::expect_on_list(); self.code_gen_functions.insert( EXPECT_ON_LIST.to_string(), CodeGenFunction::Function { body: expect_list_func, params: vec!["__list_to_check".to_string(), "__check_with".to_string()], }, ); } if let Some(counter) = defined_data_types.get_mut(EXPECT_ON_LIST) { *counter += 1 } else { defined_data_types.insert(EXPECT_ON_LIST.to_string(), 1); } let func_call = AirTree::call( AirTree::var( ValueConstructor::public( void(), ValueConstructorVariant::ModuleFn { name: EXPECT_ON_LIST.to_string(), field_map: None, module: "".to_string(), arity: 1, location, builtin: None, }, ), EXPECT_ON_LIST, "", ), void(), vec![AirTree::local_var(map_name, tipo.clone()), unwrap_function], ); assign.hoist_over(func_call) } else if tipo.is_list() { assert!(!tipo.get_inner_types().is_empty()); let inner_list_type = &tipo.get_inner_types()[0]; let list_name = format!("__list_span_{}_{}", location.start, location.end); let item_name = format!("__item_span_{}_{}", location.start, location.end); let assign = AirTree::let_assignment(&list_name, value); let expect_item = self.expect_type_assign( inner_list_type, AirTree::cast_from_data( AirTree::local_var(&item_name, data()), inner_list_type.clone(), ), defined_data_types, location, ); let anon_func_body = expect_item; let unwrap_function = AirTree::anon_func(vec![item_name], anon_func_body); let function = self.code_gen_functions.get(EXPECT_ON_LIST); if function.is_none() { let expect_list_func = AirTree::expect_on_list(); self.code_gen_functions.insert( EXPECT_ON_LIST.to_string(), CodeGenFunction::Function { body: expect_list_func, params: vec!["__list_to_check".to_string(), "__check_with".to_string()], }, ); } if let Some(counter) = defined_data_types.get_mut(EXPECT_ON_LIST) { *counter += 1 } else { defined_data_types.insert(EXPECT_ON_LIST.to_string(), 1); } let func_call = AirTree::call( AirTree::var( ValueConstructor::public( void(), ValueConstructorVariant::ModuleFn { name: EXPECT_ON_LIST.to_string(), field_map: None, module: "".to_string(), arity: 1, location, builtin: None, }, ), EXPECT_ON_LIST, "", ), void(), vec![AirTree::local_var(list_name, tipo.clone()), unwrap_function], ); assign.hoist_over(func_call) } else if tipo.is_2_tuple() { let tuple_inner_types = tipo.get_inner_types(); let pair_name = format!("__pair_span_{}_{}", location.start, location.end); let fst_name = format!("__pair_fst_span_{}_{}", location.start, location.end); let snd_name = format!("__pair_snd_span_{}_{}", location.start, location.end); let tuple_assign = AirTree::let_assignment(&pair_name, value); let tuple_access = AirTree::tuple_access( vec![fst_name.clone(), snd_name.clone()], tipo.clone(), false, AirTree::local_var(pair_name, tipo.clone()), ); let expect_fst = self.expect_type_assign( &tuple_inner_types[0], AirTree::local_var(fst_name, tuple_inner_types[0].clone()), defined_data_types, location, ); let expect_snd = self.expect_type_assign( &tuple_inner_types[1], AirTree::local_var(snd_name, tuple_inner_types[1].clone()), defined_data_types, location, ); AirTree::UnhoistedSequence(vec![ tuple_assign, tuple_access, AirTree::let_assignment("_", expect_fst), ]) .hoist_over(expect_snd) } else if tipo.is_tuple() { let tuple_inner_types = tipo.get_inner_types(); let tuple_name = format!("__tuple_span_{}_{}", location.start, location.end); let tuple_assign = AirTree::let_assignment(&tuple_name, value); let tuple_expect_items = tuple_inner_types .iter() .enumerate() .map(|(index, arg)| { let tuple_index_name = format!( "__tuple_index_{}_span_{}_{}", index, location.start, location.end ); let expect_tuple_item = self.expect_type_assign( tipo, AirTree::local_var(&tuple_index_name, arg.clone()), defined_data_types, location, ); ( tuple_index_name, AirTree::let_assignment("_", expect_tuple_item), ) }) .collect_vec(); let tuple_index_names = tuple_expect_items .iter() .map(|(name, _)| name.clone()) .collect_vec(); let tuple_access = AirTree::tuple_access( tuple_index_names, tipo.clone(), false, AirTree::local_var(tuple_name, tipo.clone()), ); let mut tuple_expects = tuple_expect_items .into_iter() .map(|(_, item)| item) .collect_vec(); let mut sequence = vec![tuple_assign, tuple_access]; sequence.append(&mut tuple_expects); AirTree::UnhoistedSequence(sequence).hoist_over(AirTree::void()) } else { let data_type = lookup_data_type_by_tipo(&self.data_types, tipo).unwrap_or_else(|| { unreachable!("We need a data type definition fot type {:#?}", tipo) }); let data_type_variant = tipo .get_inner_types() .iter() .map(|arg| get_arg_type_name(arg)) .join("_"); let mono_types: IndexMap> = if !data_type.typed_parameters.is_empty() { data_type .typed_parameters .iter() .zip(tipo.arg_types().unwrap()) .flat_map(|item| get_generic_id_and_type(item.0, &item.1)) .collect() } else { IndexMap::new() }; let data_type_name = format!("__expect_{}_{}", data_type.name, data_type_variant); let function = self.code_gen_functions.get(&data_type_name); let error_term = if self.tracing { AirTree::trace( AirTree::string("Constr index did not match any type variant"), tipo.clone(), AirTree::error(tipo.clone()), ) } else { AirTree::error(tipo.clone()) }; if function.is_none() && defined_data_types.get(&data_type_name).is_none() { defined_data_types.insert(data_type_name.clone(), 1); let current_defined = defined_data_types.clone(); let mut diff_defined_types = vec![]; let constr_clauses = data_type.constructors.iter().enumerate().rfold( error_term, |acc, (index, constr)| { let constr_args = constr .arguments .iter() .enumerate() .map(|(index, arg)| { let arg_name = arg.label.clone().unwrap_or(format!("__field_{index}")); let arg_tipo = find_and_replace_generics(&arg.tipo, &mono_types); let assign = AirTree::let_assignment( "_", self.expect_type_assign( &arg_tipo, AirTree::local_var(&arg_name, arg_tipo.clone()), defined_data_types, location, ), ); (index, arg_name, arg_tipo, assign) }) .collect_vec(); let indices = constr_args .iter() .map(|(index, arg_name, arg_tipo, _)| { (*index, arg_name.clone(), (*arg_tipo).clone()) }) .collect_vec(); let mut assigns = constr_args .into_iter() .map(|(_, _, _, assign)| assign) .collect_vec(); if assigns.is_empty() { let empty = AirTree::fields_empty(AirTree::local_var( format!("__constr_var_span_{}_{}", location.start, location.end), tipo.clone(), )); assigns.insert(0, empty); } else { let expose = AirTree::fields_expose( indices, true, AirTree::local_var( format!( "__constr_var_span_{}_{}", location.start, location.end ), tipo.clone(), ), ); assigns.insert(0, expose); }; let then = AirTree::UnhoistedSequence(assigns).hoist_over(AirTree::void()); AirTree::clause( format!("__subject_span_{}_{}", location.start, location.end), AirTree::int(index), tipo.clone(), then, acc, false, ) }, ); let overhead_assign = AirTree::let_assignment( format!("__constr_var_span_{}_{}", location.start, location.end), AirTree::local_var("__param_0", tipo.clone()), ); let when_expr = AirTree::when( format!("__subject_span_{}_{}", location.start, location.end), void(), tipo.clone(), AirTree::local_var( format!("__constr_var_span_{}_{}", location.start, location.end), tipo.clone(), ), constr_clauses, ); let func_body = overhead_assign.hoist_over(when_expr); for (inner_data_type, inner_count) in defined_data_types.iter() { if let Some(prev_count) = current_defined.get(inner_data_type) { if inner_count - prev_count > 0 { diff_defined_types.push(inner_data_type.to_string()); } } else { diff_defined_types.push(inner_data_type.to_string()); } } let code_gen_func = CodeGenFunction::Function { body: func_body, params: vec!["__param_0".to_string()], }; self.code_gen_functions .insert(data_type_name.clone(), code_gen_func); } else if let Some(counter) = defined_data_types.get_mut(&data_type_name) { *counter += 1; } else { defined_data_types.insert(data_type_name.to_string(), 1); } let func_var = AirTree::var( ValueConstructor::public( tipo.clone(), ValueConstructorVariant::ModuleFn { name: data_type_name.to_string(), field_map: None, module: "".to_string(), arity: 1, location: Span::empty(), builtin: None, }, ), data_type_name, "", ); AirTree::call(func_var, void(), vec![value]) } } pub fn handle_each_clause( &mut self, clauses: &[TypedClause], final_clause: TypedClause, subject_tipo: &Arc, props: &mut ClauseProperties, ) -> AirTree { assert!( !subject_tipo.is_void(), "WHY ARE YOU PATTERN MATCHING VOID???" ); props.complex_clause = false; if let Some((clause, rest_clauses)) = clauses.split_first() { let mut clause_then = self.build(&clause.then); if let Some(guard) = &clause.guard { props.complex_clause = true; let clause_guard_name = format!( "__clause_guard_span_{}_{}", clause.location.start, clause.location.end ); let clause_guard_assign = AirTree::let_assignment( &clause_guard_name, builder::handle_clause_guard(guard), ); clause_then = clause_guard_assign.hoist_over( AirTree::clause_guard(clause_guard_name, AirTree::bool(true), bool()) .hoist_over(clause_then), ); } match &mut props.specific_clause { SpecificClause::ConstrClause => { let data_type = lookup_data_type_by_tipo(&self.data_types, subject_tipo); let (clause_cond, clause_assign) = self.clause_pattern(&clause.pattern, subject_tipo, props); let clause_assign_hoisted = clause_assign.hoist_over(clause_then); let complex_clause = props.complex_clause; let mut next_clause_props = ClauseProperties::init( subject_tipo, props.clause_var_name.clone(), props.original_subject_name.clone(), ); if let Some(data_type) = data_type { if data_type.constructors.len() > 1 { AirTree::clause( &props.original_subject_name, clause_cond, subject_tipo.clone(), clause_assign_hoisted, self.handle_each_clause( rest_clauses, final_clause, subject_tipo, &mut next_clause_props, ), complex_clause, ) } else { AirTree::wrap_clause( clause_assign_hoisted, self.handle_each_clause( rest_clauses, final_clause, subject_tipo, &mut next_clause_props, ), ) } } else { AirTree::clause( &props.original_subject_name, clause_cond, subject_tipo.clone(), clause_assign_hoisted, self.handle_each_clause( rest_clauses, final_clause, subject_tipo, &mut next_clause_props, ), complex_clause, ) } } SpecificClause::ListClause { current_index, defined_tails, } => { let Pattern::List { elements, tail, .. } = &clause.pattern else { let mut next_clause_props = ClauseProperties { clause_var_name: props.clause_var_name.clone(), complex_clause: false, needs_constr_var: false, original_subject_name: props.original_subject_name.clone(), final_clause: false, specific_clause: SpecificClause::ListClause { current_index: *current_index, defined_tails: defined_tails.clone(), }, }; let (_, clause_assign) = self.clause_pattern(&clause.pattern, subject_tipo, props); let clause_assign_hoisted = clause_assign.hoist_over(clause_then); return AirTree::wrap_clause( clause_assign_hoisted, self.handle_each_clause( rest_clauses, final_clause, subject_tipo, &mut next_clause_props, ), ); }; let tail_name = if *current_index == 0 { props.original_subject_name.clone() } else { format!( "tail_index_{}_span_{}_{}", *current_index, clause.pattern.location().start, clause.pattern.location().end ) }; let next_tail_name = { if rest_clauses.is_empty() { None } else { let next_clause = &rest_clauses[0]; let next_elements_len = match &next_clause.pattern { Pattern::List { elements, .. } => elements.len(), _ => 0, }; if (*current_index as usize) < next_elements_len { Some(format!( "tail_index_{}_span_{}_{}", *current_index + 1, next_clause.pattern.location().start, next_clause.pattern.location().end )) } else { None } } }; let mut use_wrap_clause = false; if elements.len() - usize::from(tail.is_some() && !elements.is_empty()) >= *current_index as usize { *current_index += 1; defined_tails.push(tail_name.clone()); } else if next_tail_name.is_none() { use_wrap_clause = true; } let mut next_clause_props = ClauseProperties { clause_var_name: props.clause_var_name.clone(), complex_clause: false, needs_constr_var: false, original_subject_name: props.original_subject_name.clone(), final_clause: false, specific_clause: SpecificClause::ListClause { current_index: *current_index, defined_tails: defined_tails.clone(), }, }; let (_, clause_assign) = self.clause_pattern(&clause.pattern, subject_tipo, props); let clause_assign_hoisted = clause_assign.hoist_over(clause_then); let complex_clause = props.complex_clause; if use_wrap_clause { AirTree::wrap_clause( clause_assign_hoisted, self.handle_each_clause( rest_clauses, final_clause, subject_tipo, &mut next_clause_props, ), ) } else { AirTree::list_clause( tail_name, subject_tipo.clone(), clause_assign_hoisted, self.handle_each_clause( rest_clauses, final_clause, subject_tipo, &mut next_clause_props, ), next_tail_name, complex_clause, ) } } SpecificClause::TupleClause { defined_tuple_indices, } => { let current_defined_indices = defined_tuple_indices.clone(); let (_, pattern_assigns) = self.clause_pattern(&clause.pattern, subject_tipo, props); let ClauseProperties{ specific_clause: SpecificClause::TupleClause { defined_tuple_indices }, ..} = props else { unreachable!() }; let new_defined_indices: IndexSet<(usize, String)> = defined_tuple_indices .difference(¤t_defined_indices) .cloned() .collect(); let mut next_clause_props = ClauseProperties { clause_var_name: props.clause_var_name.clone(), complex_clause: false, needs_constr_var: false, original_subject_name: props.original_subject_name.clone(), final_clause: false, specific_clause: SpecificClause::TupleClause { defined_tuple_indices: defined_tuple_indices.clone(), }, }; if new_defined_indices.is_empty() { AirTree::wrap_clause( pattern_assigns.hoist_over(clause_then), self.handle_each_clause( rest_clauses, final_clause, subject_tipo, &mut next_clause_props, ), ) } else { AirTree::tuple_clause( &props.original_subject_name, subject_tipo.clone(), new_defined_indices, current_defined_indices, pattern_assigns.hoist_over(clause_then), self.handle_each_clause( rest_clauses, final_clause, subject_tipo, &mut next_clause_props, ), props.complex_clause, ) } } } } else { // handle final_clause props.final_clause = true; assert!(final_clause.guard.is_none()); let clause_then = self.build(&final_clause.then); let (condition, assignments) = self.clause_pattern(&final_clause.pattern, subject_tipo, props); AirTree::finally(condition, assignments.hoist_over(clause_then)) } } pub fn clause_pattern( &mut self, pattern: &Pattern>, subject_tipo: &Arc, props: &mut ClauseProperties, // We return condition and then assignments sequence ) -> (AirTree, AirTree) { match pattern { Pattern::Int { value, .. } => { assert!(!props.final_clause); (AirTree::int(value), AirTree::no_op()) } Pattern::Var { name, .. } => ( AirTree::void(), AirTree::let_assignment( name, AirTree::local_var(&props.clause_var_name, subject_tipo.clone()), ), ), Pattern::Assign { name, pattern, .. } => { let (inner_condition, inner_assignment) = self.clause_pattern(pattern, subject_tipo, props); let sequence = vec![ AirTree::let_assignment( name, AirTree::local_var(&props.clause_var_name, subject_tipo.clone()), ), inner_assignment, ]; (inner_condition, AirTree::UnhoistedSequence(sequence)) } Pattern::Discard { .. } => (AirTree::void(), AirTree::no_op()), Pattern::List { elements, tail, .. } => { let ClauseProperties { specific_clause: SpecificClause::ListClause { defined_tails, .. }, complex_clause, .. } = props else { unreachable!() }; let list_elem_types = subject_tipo.get_inner_types(); let list_elem_type = list_elem_types .get(0) .unwrap_or_else(|| unreachable!("No list element type?")); let defined_tails = defined_tails.clone(); let elems = elements .iter() .enumerate() .map(|(index, elem)| { let elem_name = match elem { Pattern::Var { name, .. } => name.to_string(), Pattern::Assign { name, .. } => name.to_string(), Pattern::Discard { .. } => "_".to_string(), _ => format!( "elem_{}_span_{}_{}", index, elem.location().start, elem.location().end ), }; let mut elem_props = ClauseProperties::init_inner( list_elem_type, elem_name.clone(), elem_name.clone(), props.final_clause, ); let statement = self.nested_clause_condition(elem, list_elem_type, &mut elem_props); *complex_clause = *complex_clause || elem_props.complex_clause; (elem_name, statement) }) .collect_vec(); let mut defined_heads = elems.iter().map(|(head, _)| head.to_string()).collect_vec(); let mut air_elems = elems .into_iter() .map(|(_, statement)| statement) .collect_vec(); let mut list_tail = None; tail.iter().for_each(|elem| { let tail = defined_tails.last().cloned().unwrap_or_default(); let elem_name = match elem.as_ref() { Pattern::Var { name, .. } => name.to_string(), Pattern::Assign { name, .. } => name.to_string(), Pattern::Discard { .. } => "_".to_string(), _ => format!( "tail_span_{}_{}", elem.location().start, elem.location().end ), }; let mut elem_props = ClauseProperties::init_inner( subject_tipo, elem_name.clone(), elem_name.clone(), props.final_clause, ); let statement = self.nested_clause_condition(elem, subject_tipo, &mut elem_props); *complex_clause = *complex_clause || elem_props.complex_clause; air_elems.push(statement); list_tail = Some((tail, elem_name.to_string())); defined_heads.push(elem_name) }); let list_assign = if props.final_clause { AirTree::list_access( defined_heads, subject_tipo.clone(), tail.is_some(), false, AirTree::local_var(&props.original_subject_name, subject_tipo.clone()), ) } else { AirTree::list_expose( defined_heads .into_iter() .zip(defined_tails.into_iter()) .collect_vec(), list_tail, subject_tipo.clone(), ) }; let mut sequence = vec![list_assign]; sequence.append(&mut air_elems); (AirTree::void(), AirTree::UnhoistedSequence(sequence)) } Pattern::Constructor { name, arguments, constructor, tipo: function_tipo, .. } => { if subject_tipo.is_bool() { (AirTree::bool(name == "True"), AirTree::no_op()) } else { assert!( matches!(function_tipo.as_ref().clone(), Type::Fn { .. }) || matches!(function_tipo.as_ref().clone(), Type::App { .. }) ); let data_type = lookup_data_type_by_tipo(&self.data_types, subject_tipo) .unwrap_or_else(|| { unreachable!( "Code Gen should have the definition for this constructor {}", name ) }); assert!(!data_type.constructors.is_empty()); let (constr_index, _) = data_type .constructors .iter() .enumerate() .find(|(_, dt)| &dt.name == name) .unwrap(); let field_map = match constructor { PatternConstructor::Record { field_map, .. } => field_map.clone(), }; let mut type_map: IndexMap> = IndexMap::new(); for (index, arg) in function_tipo.arg_types().unwrap().iter().enumerate() { let field_type = arg.clone(); type_map.insert(index, field_type); } let fields = arguments .iter() .enumerate() .map(|(index, arg)| { let label = arg.label.clone().unwrap_or_default(); let field_index = if let Some(field_map) = &field_map { *field_map.fields.get(&label).map(|x| &x.0).unwrap_or(&index) } else { index }; let field_name = match &arg.value { Pattern::Var { name, .. } => name.to_string(), Pattern::Assign { name, .. } => name.to_string(), Pattern::Discard { .. } => "_".to_string(), _ => format!( "field_{}_span_{}_{}", field_index, arg.value.location().start, arg.value.location().end ), }; let arg_type = type_map.get(&field_index).unwrap_or_else(|| { unreachable!( "Missing type for field {} of constr {}", field_index, name ) }); let mut field_props = ClauseProperties::init_inner( arg_type, field_name.clone(), field_name.clone(), props.final_clause, ); let statement = self.nested_clause_condition( &arg.value, arg_type, &mut field_props, ); props.complex_clause = props.complex_clause || field_props.complex_clause; (field_index, field_name, arg_type, statement) }) .collect_vec(); let indices = fields .iter() .map(|(constr_index, name, tipo, _)| { (*constr_index, name.to_string(), (*tipo).clone()) }) .collect_vec(); let mut air_fields = fields.into_iter().map(|(_, _, _, val)| val).collect_vec(); let field_assign = AirTree::fields_expose( indices, false, AirTree::local_var(props.clause_var_name.clone(), subject_tipo.clone()), ); let mut sequence = vec![field_assign]; sequence.append(&mut air_fields); ( AirTree::int(constr_index), AirTree::UnhoistedSequence(sequence), ) } } Pattern::Tuple { elems, .. } => { let items_type = subject_tipo.get_inner_types(); let name_index_assigns = elems .iter() .enumerate() .map(|(index, element)| { let elem_name = match element { Pattern::Var { name, .. } => name.to_string(), Pattern::Assign { name, .. } => name.to_string(), Pattern::Discard { .. } => "_".to_string(), _ => format!( "tuple_index_{}_span_{}_{}", index, element.location().start, element.location().end ), }; let mut tuple_props = ClauseProperties::init_inner( &items_type[index], elem_name.clone(), elem_name.clone(), props.final_clause, ); let elem = self.nested_clause_condition( element, &items_type[index], &mut tuple_props, ); props.complex_clause = props.complex_clause || tuple_props.complex_clause; (elem_name, index, elem) }) .collect_vec(); let mut defined_indices = match props.clone() { ClauseProperties { specific_clause: SpecificClause::TupleClause { defined_tuple_indices, }, .. } => defined_tuple_indices, _ => unreachable!(), }; let mut previous_defined_names = vec![]; name_index_assigns.iter().for_each(|(name, index, _)| { if let Some((index, prev_name)) = defined_indices .iter() .find(|(defined_index, _nm)| defined_index == index) { previous_defined_names.push((*index, prev_name.clone(), name.clone())); } else if name != "_" { assert!(defined_indices.insert((*index, name.clone()))); } }); let tuple_name_assigns = previous_defined_names .into_iter() .map(|(index, prev_name, name)| { AirTree::let_assignment( name, AirTree::local_var(prev_name, items_type[index].clone()), ) }) .collect_vec(); let mut tuple_item_assigns = name_index_assigns .into_iter() .map(|(_, _, item)| item) .collect_vec(); match props { ClauseProperties { specific_clause: SpecificClause::TupleClause { defined_tuple_indices, }, .. } => { *defined_tuple_indices = defined_indices; } _ => unreachable!(), } let mut sequence = tuple_name_assigns; sequence.append(&mut tuple_item_assigns); if props.final_clause { sequence.insert( 0, todo!(), // AirTree::tuple_access(names, tipo, check_last_item, tuple), ) } (AirTree::void(), AirTree::UnhoistedSequence(sequence)) } } } fn nested_clause_condition( &mut self, pattern: &Pattern>, subject_tipo: &Arc, props: &mut ClauseProperties, ) -> AirTree { if props.final_clause { print!("FINAL CLAUSE PATTERN IS {:#?}", pattern); props.complex_clause = false; let (_, assign) = self.clause_pattern(pattern, subject_tipo, props); println!("FINAL CLAUSE ASSIGN IS {:#?}", assign); assign } else { assert!( !subject_tipo.is_void(), "WHY ARE YOU PATTERN MATCHING ON A NESTED VOID???" ); match pattern { Pattern::Int { value, .. } => { props.complex_clause = true; AirTree::clause_guard(&props.original_subject_name, AirTree::int(value), int()) } Pattern::Var { name, .. } => AirTree::let_assignment( name, AirTree::local_var(&props.clause_var_name, subject_tipo.clone()), ), Pattern::Assign { name, pattern, .. } => AirTree::UnhoistedSequence(vec![ AirTree::let_assignment( name, AirTree::local_var(&props.clause_var_name, subject_tipo.clone()), ), self.nested_clause_condition(pattern, subject_tipo, props), ]), Pattern::Discard { .. } => AirTree::no_op(), Pattern::List { elements, tail, .. } => { props.complex_clause = true; let tail_name_base = "__tail".to_string(); if elements.is_empty() { assert!( tail.is_none(), "Why do you have a [..] in a clause? Use a var." ); AirTree::list_clause_guard( &props.original_subject_name, subject_tipo.clone(), false, None, ) } else { let mut clause_assigns = vec![]; let ClauseProperties { specific_clause: SpecificClause::ListClause { current_index, defined_tails, }, .. } = props else { unreachable!() }; for (index, _) in elements.iter().enumerate() { let prev_tail_name = if index == 0 { props.original_subject_name.clone() } else { format!("{}_{}", tail_name_base, index - 1) }; let tail_name = format!("{tail_name_base}_{index}"); if elements.len() - 1 == index { if tail.is_some() { clause_assigns.push(AirTree::list_clause_guard( prev_tail_name, subject_tipo.clone(), true, None, )); } else { clause_assigns.push(AirTree::list_clause_guard( prev_tail_name, subject_tipo.clone(), true, Some(tail_name.to_string()), )); clause_assigns.push(AirTree::list_clause_guard( tail_name.to_string(), subject_tipo.clone(), false, None, )); *current_index += 1; defined_tails.push(tail_name); } } else { clause_assigns.push(AirTree::list_clause_guard( prev_tail_name, subject_tipo.clone(), true, Some(tail_name.to_string()), )); *current_index += 1; defined_tails.push(tail_name); }; } let (_, assigns) = self.clause_pattern(pattern, subject_tipo, props); clause_assigns.push(assigns); AirTree::UnhoistedSequence(clause_assigns) } } Pattern::Constructor { name: constr_name, is_record, .. } => { props.complex_clause = true; if subject_tipo.is_bool() { AirTree::clause_guard( &props.original_subject_name, AirTree::bool(constr_name == "True"), bool(), ) } else { let (cond, assign) = self.clause_pattern(pattern, subject_tipo, props); if *is_record { assign } else { AirTree::UnhoistedSequence(vec![ AirTree::clause_guard( &props.original_subject_name, cond, subject_tipo.clone(), ), assign, ]) } } } Pattern::Tuple { .. } => { props.complex_clause = true; let (_, assign) = self.clause_pattern(pattern, subject_tipo, props); let defined_indices = match &props.specific_clause { SpecificClause::TupleClause { defined_tuple_indices, } => defined_tuple_indices.clone(), _ => unreachable!(), }; let tuple_access = AirTree::tuple_clause_guard( &props.original_subject_name, subject_tipo.clone(), defined_indices, ); AirTree::UnhoistedSequence(vec![tuple_access, assign]) } } } } pub fn check_validator_args( &mut self, arguments: &[TypedArg], has_context: bool, body: AirTree, ) -> AirTree { let checked_args = arguments .iter() .enumerate() .map(|(index, arg)| { let arg_name = arg.arg_name.get_variable_name().unwrap_or("_").to_string(); if !(has_context && index == arguments.len() - 1) && &arg_name != "_" { let param = AirTree::local_var(&arg_name, data()); let actual_type = convert_opaque_type(&arg.tipo, &self.data_types); let assign = self.assignment( &Pattern::Var { location: Span::empty(), name: arg_name.to_string(), }, param, &actual_type, AssignmentProperties { value_type: data(), kind: AssignmentKind::Expect, remove_unused: false, full_check: true, }, ); (arg_name, assign) } else { (arg_name, AirTree::no_op()) } }) .collect_vec(); let arg_names = checked_args .iter() .map(|(name, _)| name.to_string()) .collect_vec(); let arg_assigns = AirTree::UnhoistedSequence(checked_args.into_iter().map(|(_, arg)| arg).collect_vec()); AirTree::anon_func(arg_names, arg_assigns.hoist_over(body)) } fn hoist_functions_to_validator(&mut self, mut air_tree: AirTree) -> AirTree { let mut functions_to_hoist = IndexMap::new(); let mut used_functions = vec![]; let mut defined_functions = vec![]; let mut hoisted_functions = vec![]; let mut validator_hoistable; // TODO change subsequent tree traversals to be more like a stream. air_tree.traverse_tree_with(&mut |air_tree: &mut AirTree, _| { erase_opaque_type_operations(air_tree, &self.data_types); }); self.find_function_vars_and_depth( &mut air_tree, &mut functions_to_hoist, &mut used_functions, &mut TreePath::new(), 0, 0, ); validator_hoistable = used_functions.clone(); while let Some((key, variant_name)) = used_functions.pop() { defined_functions.push((key.clone(), variant_name.clone())); let function_variants = functions_to_hoist .get(&key) .unwrap_or_else(|| panic!("Missing Function Definition")); let (tree_path, function) = function_variants .get(&variant_name) .unwrap_or_else(|| panic!("Missing Function Variant Definition")); if let UserFunction::Function(body, deps) = function { let mut hoist_body = body.clone(); let mut hoist_deps = deps.clone(); let mut tree_path = tree_path.clone(); self.define_dependent_functions( &mut hoist_body, &mut functions_to_hoist, &mut used_functions, &defined_functions, &mut hoist_deps, &mut tree_path, ); let function_variants = functions_to_hoist .get_mut(&key) .unwrap_or_else(|| panic!("Missing Function Definition")); let (_, function) = function_variants .get_mut(&variant_name) .expect("Missing Function Variant Definition"); *function = UserFunction::Function(hoist_body, hoist_deps); } else { todo!("Deal with Link later") } } // First we need to sort functions by dependencies // here's also where we deal with mutual recursion let mut sorted_function_vec = vec![]; while let Some((generic_func, variant)) = validator_hoistable.pop() { let function_variants = functions_to_hoist .get(&generic_func) .unwrap_or_else(|| panic!("Missing Function Definition")); let function_has_params = self .functions .get(&generic_func) .map(|func| !func.arguments.is_empty()) .unwrap_or_else(|| true); let (_, function) = function_variants .get(&variant) .unwrap_or_else(|| panic!("Missing Function Variant Definition")); // TODO: change this part to handle mutual recursion if let UserFunction::Function(_, deps) = function { if function_has_params { for (dep_generic_func, dep_variant) in deps.iter() { if !(dep_generic_func == &generic_func && dep_variant == &variant) { validator_hoistable .push((dep_generic_func.clone(), dep_variant.clone())); let remove_index = sorted_function_vec .iter() .position(|(generic_func, variant)| { generic_func == dep_generic_func && variant == dep_variant }); if let Some(index) = remove_index { sorted_function_vec.remove(index); } } } } } else { todo!("Deal with Link later") } sorted_function_vec.push((generic_func, variant)); } sorted_function_vec.dedup(); // Now we need to hoist the functions to the top of the validator for (key, variant) in sorted_function_vec { if hoisted_functions .iter() .any(|(func_key, func_variant)| func_key == &key && func_variant == &variant) { continue; } let function_variants = functions_to_hoist .get(&key) .unwrap_or_else(|| panic!("Missing Function Definition")); let (tree_path, function) = function_variants .get(&variant) .unwrap_or_else(|| panic!("Missing Function Variant Definition")); self.hoist_function( &mut air_tree, tree_path, function, (&key, &variant), &functions_to_hoist, &mut hoisted_functions, ); } air_tree } fn hoist_function( &mut self, air_tree: &mut AirTree, tree_path: &TreePath, function: &UserFunction, key_var: (&FunctionAccessKey, &String), functions_to_hoist: &IndexMap< FunctionAccessKey, IndexMap, >, hoisted_functions: &mut Vec<(FunctionAccessKey, String)>, ) { if let UserFunction::Function(body, func_deps) = function { let mut body = body.clone(); let node_to_edit = air_tree.find_air_tree_node(tree_path); let (key, variant) = key_var; // check for recursiveness let is_recursive = func_deps .iter() .any(|(dep_key, dep_variant)| dep_key == key && dep_variant == variant); // first grab dependencies let func_params = self .functions .get(key) .map(|func| { func.arguments .iter() .map(|func_arg| { func_arg .arg_name .get_variable_name() .unwrap_or("_") .to_string() }) .collect_vec() }) .unwrap_or_else(|| { let Some(CodeGenFunction::Function { params, .. }) = self.code_gen_functions.get(&key.function_name) else { unreachable!() }; params.clone() }); let params_empty = func_params.is_empty(); let deps = (tree_path, func_deps.clone()); if !params_empty { if is_recursive { body.traverse_tree_with(&mut |air_tree: &mut AirTree, _| { modify_self_calls(air_tree, key, variant); }); } body = AirTree::define_func( &key.function_name, &key.module_name, variant, func_params, is_recursive, body, ); let function_deps = self.hoist_dependent_functions( deps, params_empty, key, variant, hoisted_functions, functions_to_hoist, ); // now hoist full function onto validator tree *node_to_edit = function_deps.hoist_over(body.hoist_over(node_to_edit.clone())); hoisted_functions.push((key.clone(), variant.clone())); } else { body = self .hoist_dependent_functions( deps, params_empty, key, variant, hoisted_functions, functions_to_hoist, ) .hoist_over(body); self.zero_arg_functions .insert((key.clone(), variant.clone()), body.to_vec()); } } else { todo!() } } fn hoist_dependent_functions( &mut self, deps: (&TreePath, Vec<(FunctionAccessKey, String)>), params_empty: bool, key: &FunctionAccessKey, variant: &String, hoisted_functions: &mut Vec<(FunctionAccessKey, String)>, functions_to_hoist: &IndexMap< FunctionAccessKey, IndexMap, >, ) -> AirTree { let (func_path, func_deps) = deps; let mut deps_vec = func_deps; let mut dep_insertions = vec![]; // This part handles hoisting dependencies while let Some((dep_key, dep_variant)) = deps_vec.pop() { if (!params_empty // if the dependency is the same as the function we're hoisting // or we hoisted it, then skip it && hoisted_functions.iter().any(|(generic, variant)| { generic == &dep_key && variant == &dep_variant })) || (&dep_key == key && &dep_variant == variant) { continue; } let dependency = functions_to_hoist .get(&dep_key) .unwrap_or_else(|| panic!("Missing Function Definition")); let (dep_path, dep_function) = dependency .get(&dep_variant) .unwrap_or_else(|| panic!("Missing Function Variant Definition")); // In the case of zero args, we need to hoist the dependency function to the top of the zero arg function if &dep_path.common_ancestor(func_path) == func_path || params_empty { let dependent_params = self .functions .get(&dep_key) .map(|dep_func| { dep_func .arguments .iter() .map(|func_arg| { func_arg .arg_name .get_variable_name() .unwrap_or("_") .to_string() }) .collect_vec() }) .unwrap_or_else(|| { let Some(CodeGenFunction::Function { params, .. }) = self.code_gen_functions.get(&dep_key.function_name) else { unreachable!() }; params.clone() }); let UserFunction::Function(mut dep_air_tree, dependency_deps) = dep_function.clone() else { unreachable!() }; let is_dependent_recursive = dependency_deps .iter() .any(|(key, variant)| &dep_key == key && &dep_variant == variant); let dependency_deps_to_add = dependency_deps .iter() .filter(|(dep_k, dep_v)| !(dep_k == &dep_key && dep_v == &dep_variant)) .filter(|(dep_k, dep_v)| { !params_empty && !hoisted_functions .iter() .any(|(generic, variant)| generic == dep_k && variant == dep_v) }) .cloned() .collect_vec(); if is_dependent_recursive { dep_air_tree.traverse_tree_with(&mut |air_tree: &mut AirTree, _| { modify_self_calls(air_tree, &dep_key, &dep_variant); }); } dep_insertions.push(AirTree::define_func( &dep_key.function_name, &dep_key.module_name, &dep_variant, dependent_params, is_dependent_recursive, dep_air_tree, )); deps_vec.extend(dependency_deps_to_add); if !params_empty { hoisted_functions.push((dep_key.clone(), dep_variant.clone())); } } } dep_insertions.reverse(); AirTree::UnhoistedSequence(dep_insertions) } fn define_dependent_functions( &mut self, air_tree: &mut AirTree, function_usage: &mut IndexMap< FunctionAccessKey, IndexMap, >, used_functions: &mut Vec<(FunctionAccessKey, String)>, defined_functions: &[(FunctionAccessKey, String)], current_function_deps: &mut Vec<(FunctionAccessKey, String)>, validator_tree_path: &mut TreePath, ) { let Some((depth, index)) = validator_tree_path.pop() else { return }; validator_tree_path.push(depth, index); self.find_function_vars_and_depth( air_tree, function_usage, current_function_deps, validator_tree_path, depth + 1, 0, ); for (generic_function_key, variant_name) in current_function_deps.iter() { if !used_functions .iter() .any(|(key, name)| key == generic_function_key && name == variant_name) && !defined_functions .iter() .any(|(key, name)| key == generic_function_key && name == variant_name) { used_functions.push((generic_function_key.clone(), variant_name.clone())); } } } fn find_function_vars_and_depth( &mut self, air_tree: &mut AirTree, function_usage: &mut IndexMap< FunctionAccessKey, IndexMap, >, dependency_functions: &mut Vec<(FunctionAccessKey, String)>, path: &mut TreePath, current_depth: usize, depth_index: usize, ) { air_tree.traverse_tree_with_path( path, current_depth, depth_index, &mut |air_tree, tree_path| { if let AirTree::Expression(AirExpression::Var { constructor, variant_name, .. }) = air_tree { let ValueConstructorVariant::ModuleFn { name: func_name, module, builtin: None, .. } = &constructor.variant else { return }; let function_var_tipo = &constructor.tipo; let generic_function_key = FunctionAccessKey { module_name: module.clone(), function_name: func_name.clone(), }; let function_def = self.functions.get(&generic_function_key); let Some(function_def) = function_def else { let code_gen_func = self .code_gen_functions .get(&generic_function_key.function_name) .unwrap_or_else(|| panic!("Missing Code Gen Function Definition")); if !dependency_functions .iter() .any(|(key, name)| key == &generic_function_key && name.is_empty()) { dependency_functions .push((generic_function_key.clone(), "".to_string())); } if let Some(func_variants) = function_usage.get_mut(&generic_function_key) { let (path, _) = func_variants.get_mut("").unwrap(); *path = path.common_ancestor(tree_path); } else { let CodeGenFunction::Function{ body, .. } = code_gen_func else { unreachable!() }; let mut function_variant_path = IndexMap::new(); function_variant_path.insert( "".to_string(), ( tree_path.clone(), UserFunction::Function(body.clone(), vec![]), ), ); function_usage.insert(generic_function_key, function_variant_path); } return; }; let mut function_var_types = function_var_tipo .arg_types() .unwrap_or_else(|| panic!("Expected a function tipo with arg types")); function_var_types.push( function_var_tipo .return_type() .unwrap_or_else(|| panic!("Should have return type")), ); let mut function_def_types = function_def .arguments .iter() .map(|arg| &arg.tipo) .collect_vec(); function_def_types.push(&function_def.return_type); let mono_types: IndexMap> = if !function_def_types.is_empty() { function_def_types .into_iter() .zip(function_var_types.into_iter()) .flat_map(|(func_tipo, var_tipo)| { get_generic_id_and_type(func_tipo, &var_tipo) }) .collect() } else { IndexMap::new() }; let variant = mono_types .iter() .sorted_by(|(id, _), (id2, _)| id.cmp(id2)) .map(|(_, tipo)| get_variant_name(tipo)) .join(""); *variant_name = variant.clone(); if !dependency_functions .iter() .any(|(key, name)| key == &generic_function_key && name == &variant) { dependency_functions.push((generic_function_key.clone(), variant.clone())); } if let Some(func_variants) = function_usage.get_mut(&generic_function_key) { if let Some((path, _)) = func_variants.get_mut(&variant) { *path = path.common_ancestor(tree_path); } else { let mut function_air_tree_body = self.build(&function_def.body); function_air_tree_body.traverse_tree_with(&mut |air_tree, _| { monomorphize(air_tree, &mono_types); erase_opaque_type_operations(air_tree, &self.data_types); }); func_variants.insert( variant, ( tree_path.clone(), UserFunction::Function(function_air_tree_body, vec![]), ), ); } } else { let mut function_air_tree_body = self.build(&function_def.body); function_air_tree_body.traverse_tree_with(&mut |air_tree, _| { monomorphize(air_tree, &mono_types); erase_opaque_type_operations(air_tree, &self.data_types); }); let mut function_variant_path = IndexMap::new(); function_variant_path.insert( variant, ( tree_path.clone(), UserFunction::Function(function_air_tree_body, vec![]), ), ); function_usage.insert(generic_function_key, function_variant_path); } } }, ); } fn uplc_code_gen(&mut self, mut ir_stack: Vec) -> Term { let mut arg_stack: Vec> = vec![]; while let Some(ir_element) = ir_stack.pop() { self.gen_uplc(ir_element, &mut arg_stack); } arg_stack.pop().unwrap() } fn gen_uplc(&mut self, ir: Air, arg_stack: &mut Vec>) { // Going to mark the changes made to code gen after air tree implementation match ir { Air::Int { value } => { arg_stack.push(Term::integer(value.parse().unwrap())); } Air::String { value } => { arg_stack.push(Term::string(value)); } Air::ByteArray { bytes } => { arg_stack.push(Term::byte_string(bytes)); } Air::Bool { value } => { arg_stack.push(Term::bool(value)); } Air::Var { name, constructor, variant_name, } => { match &constructor.variant { ValueConstructorVariant::LocalVariable { .. } => arg_stack.push(Term::Var( Name { text: name, unique: 0.into(), } .into(), )), ValueConstructorVariant::ModuleConstant { .. } => { unreachable!() } ValueConstructorVariant::ModuleFn { builtin: Some(builtin), .. } => { let term = match builtin { DefaultFunction::IfThenElse | DefaultFunction::ChooseUnit | DefaultFunction::Trace | DefaultFunction::ChooseList | DefaultFunction::ChooseData | DefaultFunction::UnConstrData => { builder::special_case_builtin(builtin, 0, vec![]) } DefaultFunction::FstPair | DefaultFunction::SndPair | DefaultFunction::HeadList => { builder::undata_builtin(builtin, 0, &constructor.tipo, vec![]) } DefaultFunction::MkCons | DefaultFunction::MkPairData => { unimplemented!("MkCons and MkPairData should be handled by an anon function or using [] or ( a, b, .., z).\n") } _ => { let mut term: Term = (*builtin).into(); term = builder::apply_builtin_forces(term, builtin.force_count()); term } }; arg_stack.push(term); } ValueConstructorVariant::ModuleFn { name: func_name, module, .. } => { let name = if (*func_name == name || name == format!("{module}_{func_name}")) && !module.is_empty() { format!("{module}_{func_name}{variant_name}") } else { format!("{func_name}{variant_name}") }; arg_stack.push(Term::Var( Name { text: name, unique: 0.into(), } .into(), )); } ValueConstructorVariant::Record { name: constr_name, .. } => { if constructor.tipo.is_bool() { arg_stack.push(Term::bool(constr_name == "True")); } else if constructor.tipo.is_void() { arg_stack.push(Term::Constant(UplcConstant::Unit.into())); } else { let data_type = builder::lookup_data_type_by_tipo( &self.data_types, &constructor.tipo, ) .unwrap(); let (constr_index, constr_type) = data_type .constructors .iter() .enumerate() .find(|(_, x)| x.name == *constr_name) .unwrap(); let mut term = Term::empty_list(); if constr_type.arguments.is_empty() { term = Term::constr_data() .apply(Term::integer(constr_index.try_into().unwrap())) .apply(term); let mut program: Program = Program { version: (1, 0, 0), term, }; let mut interner = Interner::new(); interner.program(&mut program); let eval_program: Program = program.try_into().unwrap(); let evaluated_term: Term = eval_program.eval(ExBudget::default()).result().unwrap(); term = evaluated_term.try_into().unwrap(); } else { for (index, arg) in constr_type.arguments.iter().enumerate().rev() { term = Term::mk_cons() .apply(convert_type_to_data( Term::var( arg.label .clone() .unwrap_or_else(|| format!("arg_{index}")), ), &arg.tipo, )) .apply(term); } term = Term::constr_data() .apply(Term::integer(constr_index.into())) .apply(term); for (index, arg) in constr_type.arguments.iter().enumerate().rev() { term = term.lambda( arg.label.clone().unwrap_or_else(|| format!("arg_{index}")), ) } } arg_stack.push(term); } } }; } Air::Void => arg_stack.push(Term::Constant(UplcConstant::Unit.into())), Air::List { count, tipo, tail } => { let mut args = vec![]; for _ in 0..count { let arg = arg_stack.pop().unwrap(); args.push(arg); } let mut constants = vec![]; for arg in &args { if let Term::Constant(c) = arg { constants.push(c.clone()) } } if constants.len() == args.len() && !tail { let list = if tipo.is_map() { let mut convert_keys = vec![]; let mut convert_values = vec![]; for constant in constants { match constant.as_ref() { UplcConstant::ProtoPair(_, _, fst, snd) => { convert_keys.push(fst.clone()); convert_values.push(snd.clone()); } _ => unreachable!(), } } let convert_keys = builder::convert_constants_to_data(convert_keys); let convert_values = builder::convert_constants_to_data(convert_values); Term::Constant( UplcConstant::ProtoList( UplcType::Pair(UplcType::Data.into(), UplcType::Data.into()), convert_keys .into_iter() .zip(convert_values.into_iter()) .map(|(key, value)| { UplcConstant::ProtoPair( UplcType::Data, UplcType::Data, key.into(), value.into(), ) }) .collect_vec(), ) .into(), ) } else { Term::Constant( UplcConstant::ProtoList( UplcType::Data, builder::convert_constants_to_data(constants), ) .into(), ) }; arg_stack.push(list); } else { let mut term = if tail { args.pop().unwrap() } else if tipo.is_map() { Term::empty_map() } else { Term::empty_list() }; // move this down here since the constant list path doesn't need to do this let list_element_type = tipo.get_inner_types()[0].clone(); for arg in args.into_iter().rev() { let list_item = if tipo.is_map() { arg } else { builder::convert_type_to_data(arg, &list_element_type) }; term = Term::mk_cons().apply(list_item).apply(term); } arg_stack.push(term); } } Air::ListAccessor { names, tail, // TODO: rename tipo - // tipo here refers to the list type while the actual return // type is nothing since this is an assignment over some expression tipo, check_last_item, } => { let value = arg_stack.pop().unwrap(); let mut term = arg_stack.pop().unwrap(); let list_id = self.id_gen.next(); let mut id_list = vec![]; id_list.push(list_id); for _ in 0..names.len() { id_list.push(self.id_gen.next()); } let inner_types = tipo .get_inner_types() .into_iter() .cycle() .take(names.len()) .collect_vec(); if !names.is_empty() { term = builder::list_access_to_uplc( &names, &id_list, tail, 0, term, inner_types, check_last_item, true, self.tracing, ) .apply(value); arg_stack.push(term); } else if check_last_item { let trace_term = if self.tracing { Term::Error.trace(Term::string("Expected no items for List")) } else { Term::Error }; term = value.delayed_choose_list(term, trace_term); arg_stack.push(term); } else { arg_stack.push(term); } } Air::ListExpose { tail_head_names, tail, // TODO: another case where tipo is not the actual return type, // but the list type tipo, } => { let mut term = arg_stack.pop().unwrap(); if let Some((tail_var, tail_name)) = &tail { term = term .lambda(tail_name) .apply(Term::tail_list().apply(Term::var(tail_var))); } for (tail_var, head_name) in tail_head_names.iter().rev() { let head_list = if tipo.is_map() { Term::head_list().apply(Term::var(tail_var)) } else { builder::convert_data_to_type( Term::head_list().apply(Term::var(tail_var)), &tipo.get_inner_types()[0], ) }; term = term.lambda(head_name).apply(head_list); } arg_stack.push(term); } Air::Fn { params } => { let mut term = arg_stack.pop().unwrap(); for param in params.iter().rev() { term = term.lambda(param); } if params.is_empty() { arg_stack.push(term.delay()) } else { arg_stack.push(term); } } Air::Call { count, .. } => { if count >= 1 { let mut term = arg_stack.pop().unwrap(); for _ in 0..count { let arg = arg_stack.pop().unwrap(); term = term.apply(arg); } arg_stack.push(term); } else { let term = arg_stack.pop().unwrap(); let zero_arg_functions = self.zero_arg_functions.clone(); // How we handle empty anon functions has changed // We now delay zero arg anon functions and force them on a call operation if let Term::Var(name) = &term { let text = &name.text; if let Some((_, air_vec)) = zero_arg_functions.iter().find( |( ( FunctionAccessKey { module_name, function_name, }, variant, ), _, )| { let name_module = format!("{module_name}_{function_name}{variant}"); let name = format!("{function_name}{variant}"); text == &name || text == &name_module }, ) { let mut term = self.uplc_code_gen(air_vec.clone()); term = term .constr_get_field() .constr_fields_exposer() .constr_index_exposer(); let mut program: Program = Program { version: (1, 0, 0), term, }; let mut interner = Interner::new(); interner.program(&mut program); let eval_program: Program = program.try_into().unwrap(); let evaluated_term: Term = eval_program.eval(ExBudget::max()).result().unwrap(); arg_stack.push(evaluated_term.try_into().unwrap()); } else { arg_stack.push(term.force()) } } else { unreachable!("Shouldn't call anything other than var") } } } Air::Builtin { func, tipo, count } => { let mut arg_vec = vec![]; for _ in 0..count { arg_vec.push(arg_stack.pop().unwrap()); } let tipo = match tipo.as_ref() { Type::Fn { ret, .. } => ret, _ => &tipo, }; let term = match &func { DefaultFunction::IfThenElse | DefaultFunction::ChooseUnit | DefaultFunction::Trace | DefaultFunction::ChooseList | DefaultFunction::ChooseData | DefaultFunction::UnConstrData => { builder::special_case_builtin(&func, count, arg_vec) } DefaultFunction::FstPair | DefaultFunction::SndPair | DefaultFunction::HeadList => { builder::undata_builtin(&func, count, tipo, arg_vec) } DefaultFunction::MkCons | DefaultFunction::MkPairData => { unimplemented!("MkCons and MkPairData should be handled by an anon function or using [] or ( a, b, .., z).\n") } _ => { let mut term: Term = func.into(); term = builder::apply_builtin_forces(term, func.force_count()); for arg in arg_vec { term = term.apply(arg.clone()); } term } }; arg_stack.push(term); } Air::BinOp { name, // changed this to argument tipo argument_tipo: tipo, .. } => { let left = arg_stack.pop().unwrap(); let right = arg_stack.pop().unwrap(); let builtin = if tipo.is_int() { Term::equals_integer() } else if tipo.is_string() { Term::equals_string() } else if tipo.is_bytearray() { Term::equals_bytestring() } else { Term::equals_data() }; let term = match name { BinOp::And => left.delayed_if_else(right, Term::bool(false)), BinOp::Or => left.delayed_if_else(Term::bool(true), right), BinOp::Eq => { if tipo.is_bool() { let term = left.delayed_if_else( right.clone(), right.if_else(Term::bool(false), Term::bool(true)), ); arg_stack.push(term); return; } else if tipo.is_map() { let term = builtin .apply(Term::map_data().apply(left)) .apply(Term::map_data().apply(right)); arg_stack.push(term); return; } else if tipo.is_tuple() && matches!(tipo.get_uplc_type(), UplcType::Pair(_, _)) { let term = builtin .apply(Term::map_data().apply( Term::mk_cons().apply(left).apply(Term::empty_map()), )) .apply(Term::map_data().apply( Term::mk_cons().apply(right).apply(Term::empty_map()), )); arg_stack.push(term); return; } else if tipo.is_list() || tipo.is_tuple() { let term = builtin .apply(Term::list_data().apply(left)) .apply(Term::list_data().apply(right)); arg_stack.push(term); return; } else if tipo.is_void() { let term = left.choose_unit(right.choose_unit(Term::bool(true))); arg_stack.push(term); return; } builtin.apply(left).apply(right) } BinOp::NotEq => { if tipo.is_bool() { let term = left.delayed_if_else( right.clone().if_else(Term::bool(false), Term::bool(true)), right, ); arg_stack.push(term); return; } else if tipo.is_map() { let term = builtin .apply(Term::map_data().apply(left)) .apply(Term::map_data().apply(right)) .if_else(Term::bool(false), Term::bool(true)); arg_stack.push(term); return; } else if tipo.is_tuple() && matches!(tipo.get_uplc_type(), UplcType::Pair(_, _)) { let term = builtin .apply(Term::map_data().apply( Term::mk_cons().apply(left).apply(Term::empty_map()), )) .apply(Term::map_data().apply( Term::mk_cons().apply(right).apply(Term::empty_map()), )) .if_else(Term::bool(false), Term::bool(true)); arg_stack.push(term); return; } else if tipo.is_list() || tipo.is_tuple() { let term = builtin .apply(Term::list_data().apply(left)) .apply(Term::list_data().apply(right)) .if_else(Term::bool(false), Term::bool(true)); arg_stack.push(term); return; } else if tipo.is_void() { arg_stack.push(Term::bool(false)); return; } builtin .apply(left) .apply(right) .if_else(Term::bool(false), Term::bool(true)) } BinOp::LtInt => Term::Builtin(DefaultFunction::LessThanInteger) .apply(left) .apply(right), BinOp::LtEqInt => Term::Builtin(DefaultFunction::LessThanEqualsInteger) .apply(left) .apply(right), BinOp::GtEqInt => Term::Builtin(DefaultFunction::LessThanEqualsInteger) .apply(right) .apply(left), BinOp::GtInt => Term::Builtin(DefaultFunction::LessThanInteger) .apply(right) .apply(left), BinOp::AddInt => Term::add_integer().apply(left).apply(right), BinOp::SubInt => Term::Builtin(DefaultFunction::SubtractInteger) .apply(left) .apply(right), BinOp::MultInt => Term::Builtin(DefaultFunction::MultiplyInteger) .apply(left) .apply(right), BinOp::DivInt => Term::Builtin(DefaultFunction::DivideInteger) .apply(left) .apply(right), BinOp::ModInt => Term::Builtin(DefaultFunction::ModInteger) .apply(left) .apply(right), }; arg_stack.push(term); } Air::DefineFunc { func_name, params, recursive, module_name, variant_name, } => { let func_name = if module_name.is_empty() { format!("{func_name}{variant_name}") } else { format!("{module_name}_{func_name}{variant_name}") }; let mut func_body = arg_stack.pop().unwrap(); let mut term = arg_stack.pop().unwrap(); for param in params.iter().rev() { func_body = func_body.lambda(param.clone()); } if !recursive { term = term.lambda(func_name).apply(func_body); arg_stack.push(term); } else { func_body = func_body.lambda(func_name.clone()); term = term .lambda(func_name.clone()) .apply(Term::var(func_name.clone()).apply(Term::var(func_name.clone()))) .lambda(func_name) .apply(func_body); arg_stack.push(term); } } Air::Let { name } => { let arg = arg_stack.pop().unwrap(); let mut term = arg_stack.pop().unwrap(); term = term.lambda(name).apply(arg); arg_stack.push(term); } Air::CastFromData { tipo } => { let mut term = arg_stack.pop().unwrap(); term = builder::convert_data_to_type(term, &tipo); arg_stack.push(term); } Air::CastToData { tipo } => { let mut term = arg_stack.pop().unwrap(); term = builder::convert_type_to_data(term, &tipo); arg_stack.push(term); } Air::AssertConstr { constr_index } => { self.needs_field_access = true; let constr = arg_stack.pop().unwrap(); let mut term = arg_stack.pop().unwrap(); let trace_term = if self.tracing { Term::Error.trace(Term::string("Expected on incorrect constructor variant.")) } else { Term::Error }; term = Term::equals_integer() .apply(Term::integer(constr_index.into())) .apply(Term::var(CONSTR_INDEX_EXPOSER).apply(constr)) .delayed_if_else(term, trace_term); arg_stack.push(term); } Air::AssertBool { is_true } => { let value = arg_stack.pop().unwrap(); let mut term = arg_stack.pop().unwrap(); let trace_term = if self.tracing { Term::Error.trace(Term::string("Expected on incorrect boolean variant")) } else { Term::Error }; if is_true { term = value.delayed_if_else(term, trace_term) } else { term = value.delayed_if_else(trace_term, term) } arg_stack.push(term); } Air::When { subject_name, // using subject type here subject_tipo: tipo, .. } => { let subject = arg_stack.pop().unwrap(); let subject = if tipo.is_int() || tipo.is_bytearray() || tipo.is_string() || tipo.is_list() || tipo.is_tuple() || tipo.is_bool() { subject } else { self.needs_field_access = true; Term::var(CONSTR_INDEX_EXPOSER).apply(subject) }; let mut term = arg_stack.pop().unwrap(); term = term.lambda(subject_name).apply(subject); arg_stack.push(term); } Air::Clause { subject_tipo: tipo, subject_name, complex_clause, } => { // clause to compare let clause = arg_stack.pop().unwrap(); // the body to be run if the clause matches let mut body = arg_stack.pop().unwrap(); // the next branch in the when expression let mut term = arg_stack.pop().unwrap(); if tipo.is_bool() { let other_clauses = if complex_clause { Term::var("__other_clauses_delayed") } else { term.clone().delay() }; if matches!(clause, Term::Constant(boolean) if matches!(boolean.as_ref(), UplcConstant::Bool(true))) { body = Term::var(subject_name) .if_else(body.delay(), other_clauses) .force(); } else { body = Term::var(subject_name) .if_else(other_clauses, body.delay()) .force(); } if complex_clause { term = body.lambda("__other_clauses_delayed").apply(term.delay()); } else { term = body; } } else { let condition = if tipo.is_int() { Term::equals_integer() .apply(clause) .apply(Term::var(subject_name)) } else if tipo.is_bytearray() { Term::equals_bytestring() .apply(clause) .apply(Term::var(subject_name)) } else if tipo.is_string() { Term::equals_string() .apply(clause) .apply(Term::var(subject_name)) } else if tipo.is_list() || tipo.is_tuple() { unreachable!("{:#?}", tipo) } else { Term::equals_integer() .apply(clause) .apply(Term::var(subject_name)) }; if complex_clause { term = condition .if_else(body.delay(), Term::var("__other_clauses_delayed")) .force() .lambda("__other_clauses_delayed") .apply(term.delay()); } else { term = condition.delayed_if_else(body, term); } } arg_stack.push(term); } Air::ListClause { tail_name, next_tail_name, complex_clause, .. } => { // no longer need to pop off discard let body = arg_stack.pop().unwrap(); let mut term = arg_stack.pop().unwrap(); let arg = if let Some(next_tail_name) = next_tail_name { term.lambda(next_tail_name) .apply(Term::tail_list().apply(Term::var(tail_name.clone()))) } else { term }; if complex_clause { term = Term::var(tail_name) .choose_list(body.delay(), Term::var("__other_clauses_delayed")) .force() .lambda("__other_clauses_delayed") .apply(arg.delay()); } else { term = Term::var(tail_name).delayed_choose_list(body, arg); } arg_stack.push(term); } Air::WrapClause => { // no longer need to pop off discard let arg = arg_stack.pop().unwrap(); let mut term = arg_stack.pop().unwrap(); term = term.lambda("__other_clauses_delayed").apply(arg.delay()); arg_stack.push(term); } Air::TupleClause { subject_tipo: tipo, indices, subject_name, complex_clause, .. } => { let mut term = arg_stack.pop().unwrap(); let tuple_types = tipo.get_inner_types(); if complex_clause { let next_clause = arg_stack.pop().unwrap(); term = term .lambda("__other_clauses_delayed") .apply(next_clause.delay()); } if tuple_types.len() == 2 { for (index, name) in indices.iter() { let builtin = if *index == 0 { Term::fst_pair() } else { Term::snd_pair() }; term = term.lambda(name).apply(builder::convert_data_to_type( builtin.apply(Term::var(subject_name.clone())), &tuple_types[*index].clone(), )); } } else { for (index, name) in indices.iter() { term = term .lambda(name.clone()) .apply(builder::convert_data_to_type( Term::head_list().apply( Term::var(subject_name.clone()).repeat_tail_list(*index), ), &tuple_types[*index].clone(), )); } } arg_stack.push(term); } Air::ClauseGuard { subject_name, subject_tipo: tipo, } => { let checker = arg_stack.pop().unwrap(); let then = arg_stack.pop().unwrap(); if tipo.is_bool() { let mut term = Term::var("__other_clauses_delayed"); if matches!(checker, Term::Constant(boolean) if matches!(boolean.as_ref(), UplcConstant::Bool(true))) { term = Term::var(subject_name).if_else(then.delay(), term).force(); } else { term = Term::var(subject_name).if_else(term, then.delay()).force(); } arg_stack.push(term); } else { let condition = if tipo.is_int() { Term::equals_integer() .apply(checker) .apply(Term::var(subject_name)) } else if tipo.is_bytearray() { Term::equals_bytestring() .apply(checker) .apply(Term::var(subject_name)) } else if tipo.is_string() { Term::equals_string() .apply(checker) .apply(Term::var(subject_name)) } else if tipo.is_list() || tipo.is_tuple() { unreachable!() } else { self.needs_field_access = true; Term::equals_integer() .apply(checker) .apply(Term::var(CONSTR_INDEX_EXPOSER).apply(Term::var(subject_name))) }; let term = condition .if_else(then.delay(), Term::var("__other_clauses_delayed")) .force(); arg_stack.push(term); } } Air::ListClauseGuard { tail_name, next_tail_name, inverse, .. } => { // no longer need to pop off discard // the body to be run if the clause matches // the next branch in the when expression let mut term = arg_stack.pop().unwrap(); term = if let Some(next_tail_name) = next_tail_name { term.lambda(next_tail_name) .apply(Term::tail_list().apply(Term::var(tail_name.clone()))) } else { term }; if !inverse { term = Term::var(tail_name) .choose_list(term.delay(), Term::var("__other_clauses_delayed")) .force(); } else { term = Term::var(tail_name) .choose_list(Term::var("__other_clauses_delayed"), term.delay()) .force(); } arg_stack.push(term); } Air::TupleGuard { subject_tipo: tipo, indices, subject_name, } => { let mut term = arg_stack.pop().unwrap(); let tuple_types = tipo.get_inner_types(); if tuple_types.len() == 2 { for (index, name) in indices.iter() { let builtin = if *index == 0 { Term::fst_pair() } else { Term::snd_pair() }; term = term.lambda(name).apply(builder::convert_data_to_type( builtin.apply(Term::var(subject_name.clone())), &tuple_types[*index].clone(), )); } } else { for (index, name) in indices.iter() { term = term .lambda(name.clone()) .apply(builder::convert_data_to_type( Term::head_list().apply( Term::var(subject_name.clone()).repeat_tail_list(*index), ), &tuple_types[*index].clone(), )); } } arg_stack.push(term); } Air::Finally => { let _clause = arg_stack.pop().unwrap(); } Air::If { .. } => { let condition = arg_stack.pop().unwrap(); let then = arg_stack.pop().unwrap(); let mut term = arg_stack.pop().unwrap(); term = condition.delayed_if_else(then, term); arg_stack.push(term); } Air::Constr { tag: constr_index, tipo, count, } => { let mut arg_vec = vec![]; for _ in 0..count { arg_vec.push(arg_stack.pop().unwrap()); } let mut term = Term::empty_list(); for (index, arg) in arg_vec.iter().enumerate().rev() { term = Term::mk_cons() .apply(builder::convert_type_to_data( arg.clone(), &tipo.arg_types().unwrap()[index], )) .apply(term); } term = Term::constr_data() .apply(Term::integer(constr_index.into())) .apply(term); if arg_vec.iter().all(|item| matches!(item, Term::Constant(_))) { let mut program: Program = Program { version: (1, 0, 0), term, }; let mut interner = Interner::new(); interner.program(&mut program); let eval_program: Program = program.try_into().unwrap(); let evaluated_term: Term = eval_program.eval(ExBudget::default()).result().unwrap(); term = evaluated_term.try_into().unwrap(); } arg_stack.push(term); } Air::RecordAccess { record_index, tipo } => { self.needs_field_access = true; let constr = arg_stack.pop().unwrap(); let mut term = Term::var(CONSTR_GET_FIELD) .apply(Term::var(CONSTR_FIELDS_EXPOSER).apply(constr)) .apply(Term::integer(record_index.into())); term = builder::convert_data_to_type(term, &tipo); arg_stack.push(term); } Air::FieldsExpose { indices, check_last_item, } => { self.needs_field_access = true; let mut id_list = vec![]; let value = arg_stack.pop().unwrap(); let mut term = arg_stack.pop().unwrap(); let list_id = self.id_gen.next(); id_list.push(list_id); for _ in 0..indices.len() { id_list.push(self.id_gen.next()); } let current_index = 0; let names = indices.iter().cloned().map(|item| item.1).collect_vec(); let inner_types = indices.iter().cloned().map(|item| item.2).collect_vec(); if !indices.is_empty() { term = builder::list_access_to_uplc( &names, &id_list, false, current_index, term, inner_types, check_last_item, false, self.tracing, ); term = term.apply(Term::var(CONSTR_FIELDS_EXPOSER).apply(value)); arg_stack.push(term); } else if check_last_item { let trace_term = if self.tracing { Term::Error.trace(Term::string("Expected no fields for Constr")) } else { Term::Error }; term = Term::var(CONSTR_FIELDS_EXPOSER) .apply(value) .delayed_choose_list(term, trace_term); arg_stack.push(term); } else { arg_stack.push(term); }; } Air::FieldsEmpty => { self.needs_field_access = true; let value = arg_stack.pop().unwrap(); let mut term = arg_stack.pop().unwrap(); let trace_term = if self.tracing { Term::Error.trace(Term::string("Expected no fields for Constr")) } else { Term::Error }; term = Term::var(CONSTR_FIELDS_EXPOSER) .apply(value) .delayed_choose_list(term, trace_term); arg_stack.push(term); } Air::ListEmpty => { let value = arg_stack.pop().unwrap(); let mut term = arg_stack.pop().unwrap(); let trace_term = if self.tracing { Term::Error.trace(Term::string("Expected no items for List")) } else { Term::Error }; term = value.delayed_choose_list(term, trace_term); arg_stack.push(term); } Air::Tuple { tipo, count } => { let mut args = vec![]; for _ in 0..count { let arg = arg_stack.pop().unwrap(); args.push(arg); } let mut constants = vec![]; for arg in &args { if let Term::Constant(c) = arg { constants.push(c.clone()) } } let tuple_sub_types = tipo.get_inner_types(); if constants.len() == args.len() { let data_constants = builder::convert_constants_to_data(constants); if count == 2 { let term = Term::Constant( UplcConstant::ProtoPair( UplcType::Data, UplcType::Data, data_constants[0].clone().into(), data_constants[1].clone().into(), ) .into(), ); arg_stack.push(term); } else { let term = Term::Constant( UplcConstant::ProtoList(UplcType::Data, data_constants).into(), ); arg_stack.push(term); } } else if count == 2 { let term = Term::mk_pair_data() .apply(builder::convert_type_to_data( args[0].clone(), &tuple_sub_types[0], )) .apply(builder::convert_type_to_data( args[1].clone(), &tuple_sub_types[1], )); arg_stack.push(term); } else { let mut term = Term::empty_list(); for (arg, tipo) in args.into_iter().zip(tuple_sub_types.into_iter()).rev() { term = Term::mk_cons() .apply(builder::convert_type_to_data(arg, &tipo)) .apply(term); } arg_stack.push(term); } } Air::RecordUpdate { highest_index, indices, tipo, } => { self.needs_field_access = true; let tail_name_prefix = "__tail_index"; let data_type = lookup_data_type_by_tipo(&self.data_types, &tipo) .unwrap_or_else(|| panic!("HOW DID YOU DO THIS ON BOOL OR VOID")); let constructor_field_count = data_type.constructors[0].arguments.len(); let record = arg_stack.pop().unwrap(); let mut args = IndexMap::new(); let mut unchanged_field_indices = vec![]; // plus 2 so we get one index higher than the record update index // then we add that and any other unchanged fields to an array to later create the // lambda bindings unchanged_field_indices.push(0); let mut prev_index = 0; for (index, tipo) in indices .into_iter() .sorted_by(|(index1, _), (index2, _)| index1.cmp(index2)) { let arg = arg_stack.pop().unwrap(); args.insert(index, (tipo.clone(), arg)); for field_index in (prev_index + 1)..index { unchanged_field_indices.push(field_index); } prev_index = index; } unchanged_field_indices.push(prev_index + 1); let mut term = Term::var(format!("{tail_name_prefix}_{}", highest_index + 1)); for current_index in (0..(highest_index + 1)).rev() { let tail_name = format!("{tail_name_prefix}_{current_index}"); if let Some((tipo, arg)) = args.get(¤t_index) { term = Term::mk_cons() .apply(builder::convert_type_to_data(arg.clone(), tipo)) .apply(term); } else { term = Term::mk_cons() .apply(Term::head_list().apply(Term::var(tail_name))) .apply(term); } } term = Term::constr_data() .apply(Term::integer(0.into())) .apply(term); if unchanged_field_indices.len() > 1 { let (prev_index, rest_list) = unchanged_field_indices .split_last() .unwrap_or_else(|| panic!("WHAT HAPPENED")); let mut prev_index = *prev_index; for index in rest_list.iter().rev() { let index = *index; let suffix_tail = format!("{tail_name_prefix}_{prev_index}"); let tail = format!("{tail_name_prefix}_{index}"); let mut tail_list = Term::var(tail); if index < prev_index { tail_list = tail_list.repeat_tail_list(prev_index - index); if prev_index == constructor_field_count { term = term.lambda(suffix_tail).apply(Term::empty_list()); } else { term = term.lambda(suffix_tail).apply(tail_list); } } prev_index = index; } } term = term .lambda(format!("{tail_name_prefix}_0")) .apply(Term::var(CONSTR_FIELDS_EXPOSER).apply(record)); arg_stack.push(term); } Air::UnOp { op } => { let value = arg_stack.pop().unwrap(); let term = match op { UnOp::Not => value.if_else(Term::bool(false), Term::bool(true)), UnOp::Negate => { if let Term::Constant(c) = &value { if let UplcConstant::Integer(i) = c.as_ref() { Term::integer(-i) } else { Term::sub_integer() .apply(Term::integer(0.into())) .apply(value) } } else { Term::sub_integer() .apply(Term::integer(0.into())) .apply(value) } } }; arg_stack.push(term); } Air::TupleIndex { tipo, tuple_index } => { let mut term = arg_stack.pop().unwrap(); if matches!(tipo.get_uplc_type(), UplcType::Pair(_, _)) { if tuple_index == 0 { term = builder::convert_data_to_type( Term::fst_pair().apply(term), &tipo.get_inner_types()[0], ); } else { term = builder::convert_data_to_type( Term::snd_pair().apply(term), &tipo.get_inner_types()[1], ); } } else { self.needs_field_access = true; term = builder::convert_data_to_type( Term::var(CONSTR_GET_FIELD) .apply(term) .apply(Term::integer(tuple_index.into())), &tipo.get_inner_types()[tuple_index], ); } arg_stack.push(term); } Air::TupleAccessor { tipo, names, check_last_item, } => { let inner_types = tipo.get_inner_types(); let value = arg_stack.pop().unwrap(); let mut term = arg_stack.pop().unwrap(); let list_id = self.id_gen.next(); if names.len() == 2 { term = term .lambda(names[1].clone()) .apply(builder::convert_data_to_type( Term::snd_pair().apply(Term::var(format!("__tuple_{list_id}"))), &inner_types[1], )) .lambda(names[0].clone()) .apply(builder::convert_data_to_type( Term::fst_pair().apply(Term::var(format!("__tuple_{list_id}"))), &inner_types[0], )) .lambda(format!("__tuple_{list_id}")) .apply(value); arg_stack.push(term); } else if !names.is_empty() { let mut id_list = vec![]; id_list.push(list_id); for _ in 0..names.len() { id_list.push(self.id_gen.next()); } term = builder::list_access_to_uplc( &names, &id_list, false, 0, term, tipo.get_inner_types(), check_last_item, false, self.tracing, ) .apply(value); arg_stack.push(term); } else if check_last_item { let trace_term = if self.tracing { Term::Error.trace(Term::string("Expected no items for Tuple")) } else { Term::Error }; term = value.delayed_choose_list(term, trace_term); arg_stack.push(term); } else { arg_stack.push(term); } } Air::Trace { .. } => { let text = arg_stack.pop().unwrap(); let term = arg_stack.pop().unwrap(); let term = term.trace(text); arg_stack.push(term); } Air::ErrorTerm { .. } => arg_stack.push(Term::Error), Air::NoOp => {} } } }