use std::sync::Arc; use indexmap::{IndexMap, IndexSet}; use itertools::Itertools; use uplc::{ ast::{ builder::{ self, apply_wrap, assert_on_list, choose_list, constr_index_exposer, delayed_choose_list, delayed_if_else, final_wrapper, if_else, repeat_tail_list, ASSERT_ON_LIST, CONSTR_FIELDS_EXPOSER, CONSTR_GET_FIELD, }, Constant as UplcConstant, Name, NamedDeBruijn, Program, Term, Type as UplcType, }, builtins::DefaultFunction, machine::cost_model::ExBudget, optimize::aiken_optimize_and_intern, parser::interner::Interner, }; use crate::{ air::Air, ast::{ ArgName, AssignmentKind, BinOp, Clause, Pattern, Span, TypedArg, TypedDataType, TypedFunction, UnOp, }, builder::{ check_replaceable_opaque_type, check_when_pattern_needs, constants_ir, convert_constants_to_data, convert_data_to_type, convert_type_to_data, get_common_ancestor, get_generics_and_type, handle_clause_guard, handle_func_dependencies_ir, handle_recursion_ir, list_access_to_uplc, lookup_data_type_by_tipo, monomorphize, rearrange_clauses, replace_opaque_type, wrap_validator_args, AssignmentProperties, ClauseProperties, DataTypeKey, FuncComponents, FunctionAccessKey, }, builtins::bool, expr::TypedExpr, tipo::{ ModuleValueConstructor, PatternConstructor, Type, TypeInfo, ValueConstructor, ValueConstructorVariant, }, IdGenerator, }; #[derive(Clone)] pub struct CodeGenerator<'a> { defined_functions: IndexMap, functions: IndexMap, data_types: IndexMap, module_types: IndexMap<&'a String, &'a TypeInfo>, id_gen: IdGenerator, needs_field_access: bool, used_data_assert_on_list: bool, zero_arg_functions: IndexMap>, } impl<'a> CodeGenerator<'a> { pub fn new( functions: IndexMap, data_types: IndexMap, module_types: IndexMap<&'a String, &'a TypeInfo>, ) -> Self { CodeGenerator { defined_functions: IndexMap::new(), functions, data_types, module_types, id_gen: IdGenerator::new(), needs_field_access: false, used_data_assert_on_list: false, zero_arg_functions: IndexMap::new(), } } pub fn reset(&mut self) { self.needs_field_access = false; self.used_data_assert_on_list = false; self.zero_arg_functions = IndexMap::new(); self.id_gen = IdGenerator::new(); self.defined_functions = IndexMap::new(); } pub fn generate( &mut self, body: &TypedExpr, arguments: &[TypedArg], wrap_as_validator: bool, ) -> Program { let mut ir_stack = vec![]; let scope = vec![self.id_gen.next()]; self.build_ir(body, &mut ir_stack, scope); self.define_ir(&mut ir_stack); self.convert_opaque_type_to_inner_ir(&mut ir_stack); let mut term = self.uplc_code_gen(&mut ir_stack); if self.needs_field_access { term = builder::constr_get_field(term); term = builder::constr_fields_exposer(term); } // Wrap the validator body if ifThenElse term unit error term = if wrap_as_validator { final_wrapper(term) } else { term }; term = wrap_validator_args(term, arguments); term = if wrap_as_validator || self.used_data_assert_on_list { assert_on_list(term) } else { term }; let mut program = Program { version: (1, 0, 0), term, }; program = aiken_optimize_and_intern(program); // 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 } pub(crate) fn build_ir(&mut self, body: &TypedExpr, ir_stack: &mut Vec, scope: Vec) { match body { TypedExpr::Int { value, .. } => ir_stack.push(Air::Int { scope, value: value.to_string(), }), TypedExpr::String { value, .. } => ir_stack.push(Air::String { scope, value: value.to_string(), }), TypedExpr::ByteArray { bytes, .. } => ir_stack.push(Air::ByteArray { scope, bytes: bytes.to_vec(), }), TypedExpr::Pipeline { expressions, .. } | TypedExpr::Sequence { expressions, .. } => { for (index, expr) in expressions.iter().enumerate() { if index == 0 { self.build_ir(expr, ir_stack, scope.clone()); } else { let mut branch_scope = scope.clone(); branch_scope.push(self.id_gen.next()); self.build_ir(expr, ir_stack, branch_scope); } } } TypedExpr::Var { constructor, name, .. } => match &constructor.variant { ValueConstructorVariant::ModuleConstant { literal, .. } => { constants_ir(literal, ir_stack, scope); } ValueConstructorVariant::ModuleFn { builtin: Some(builtin), .. } => { ir_stack.push(Air::Builtin { scope, func: *builtin, tipo: constructor.tipo.clone(), }); } _ => { ir_stack.push(Air::Var { scope, constructor: constructor.clone(), name: name.clone(), variant_name: String::new(), }); } }, TypedExpr::Fn { args, body, .. } => { let mut func_body = vec![]; let mut func_scope = scope.clone(); func_scope.push(self.id_gen.next()); self.build_ir(body, &mut func_body, func_scope); let mut arg_names = vec![]; for arg in args { let name = arg.arg_name.get_variable_name().unwrap_or("_").to_string(); arg_names.push(name); } ir_stack.push(Air::Fn { scope, params: arg_names, }); ir_stack.append(&mut func_body); } TypedExpr::List { elements, tail, tipo, .. } => { ir_stack.push(Air::List { scope: scope.clone(), count: elements.len(), tipo: tipo.clone(), tail: tail.is_some(), }); for element in elements { let mut scope = scope.clone(); scope.push(self.id_gen.next()); self.build_ir(element, ir_stack, scope.clone()) } if let Some(tail) = tail { let mut scope = scope; scope.push(self.id_gen.next()); self.build_ir(tail, ir_stack, scope); } } TypedExpr::Call { fun, args, tipo, .. } => { if let Some(data_type) = lookup_data_type_by_tipo(self.data_types.clone(), tipo) { if let TypedExpr::Var { constructor, .. } = &**fun { if let ValueConstructorVariant::Record { name: constr_name, .. } = &constructor.variant { let (constr_index, _) = data_type .constructors .iter() .enumerate() .find(|(_, dt)| &dt.name == constr_name) .unwrap(); ir_stack.push(Air::Record { scope: scope.clone(), constr_index, tipo: constructor.tipo.clone(), count: args.len(), }); if let Some(fun_arg_types) = fun.tipo().arg_types() { for (arg, func_type) in args.iter().zip(fun_arg_types) { let mut scope = scope.clone(); scope.push(self.id_gen.next()); if func_type.is_data() && !arg.value.tipo().is_data() { ir_stack.push(Air::WrapData { scope: scope.clone(), tipo: arg.value.tipo(), }) } self.build_ir(&arg.value, ir_stack, scope); } return; } } } } ir_stack.push(Air::Call { scope: scope.clone(), count: args.len(), tipo: tipo.clone(), }); let mut scope_fun = scope.clone(); scope_fun.push(self.id_gen.next()); self.build_ir(fun, ir_stack, scope_fun); if let Some(fun_arg_types) = fun.tipo().arg_types() { for (arg, func_type) in args.iter().zip(fun_arg_types) { let mut scope = scope.clone(); scope.push(self.id_gen.next()); if func_type.is_data() && !arg.value.tipo().is_data() { ir_stack.push(Air::WrapData { scope: scope.clone(), tipo: arg.value.tipo(), }) } self.build_ir(&arg.value, ir_stack, scope); } } } TypedExpr::BinOp { name, left, right, .. } => { ir_stack.push(Air::BinOp { scope: scope.clone(), name: *name, count: 2, tipo: left.tipo(), }); let mut scope_left = scope.clone(); scope_left.push(self.id_gen.next()); let mut scope_right = scope; scope_right.push(self.id_gen.next()); self.build_ir(left, ir_stack, scope_left); self.build_ir(right, ir_stack, scope_right); } TypedExpr::Assignment { value, pattern, kind, tipo, .. } => { let mut value_vec: Vec = vec![]; let mut pattern_vec: Vec = vec![]; let mut value_scope = scope.clone(); value_scope.push(self.id_gen.next()); let mut replaced_type = tipo.clone(); replace_opaque_type(&mut replaced_type, self.data_types.clone()); self.build_ir(value, &mut value_vec, value_scope); self.assignment_ir( pattern, &mut pattern_vec, &mut value_vec, &replaced_type, AssignmentProperties { value_type: value.tipo(), kind: *kind, }, scope, ); ir_stack.append(&mut pattern_vec); } TypedExpr::When { subjects, clauses, .. } => { let subject_name = format!("__subject_name_{}", self.id_gen.next()); let constr_var = format!("__constr_name_{}", self.id_gen.next()); // assuming one subject at the moment let subject = subjects[0].clone(); let subject_tipo = subject.tipo(); if clauses.len() <= 1 { let mut value_vec: Vec = vec![]; let mut pattern_vec: Vec = vec![]; let mut subject_vec: Vec = vec![]; self.build_ir(&clauses[0].then, &mut value_vec, scope.clone()); self.build_ir(&subject, &mut subject_vec, scope.clone()); self.assignment_ir( &clauses[0].pattern[0], &mut pattern_vec, &mut subject_vec, &subject_tipo, AssignmentProperties { value_type: clauses[0].then.tipo(), kind: AssignmentKind::Let, }, scope, ); ir_stack.append(&mut pattern_vec); ir_stack.append(&mut value_vec); } else { // HERE TODO let clauses = if subject_tipo.is_list() { rearrange_clauses(clauses.clone()) } else { clauses.clone() }; if let Some((last_clause, clauses)) = clauses.split_last() { let mut pattern_vec = vec![]; let mut clause_properties = ClauseProperties::init( &subject_tipo, constr_var.clone(), subject_name.clone(), ); self.handle_each_clause( &mut pattern_vec, &mut clause_properties, clauses, &subject_tipo, scope.clone(), ); let last_pattern = &last_clause.pattern[0]; let mut final_scope = scope.clone(); final_scope.push(self.id_gen.next()); if !matches!(last_pattern, Pattern::Tuple { .. }) { pattern_vec.push(Air::Finally { scope: final_scope.clone(), }); } let mut final_clause_vec = vec![]; self.build_ir( &last_clause.then, &mut final_clause_vec, final_scope.clone(), ); *clause_properties.is_final_clause() = true; self.when_ir( last_pattern, &mut pattern_vec, &mut final_clause_vec, &subject_tipo, &mut clause_properties, final_scope, ); if *clause_properties.needs_constr_var() { ir_stack.push(Air::Let { scope: scope.clone(), name: constr_var.clone(), }); let mut subject_scope = scope.clone(); subject_scope.push(self.id_gen.next()); self.build_ir(&subject, ir_stack, subject_scope.clone()); let mut scope = scope; scope.push(self.id_gen.next()); ir_stack.push(Air::When { scope: scope.clone(), subject_name, tipo: subject_tipo.clone(), }); ir_stack.push(Air::Var { scope, constructor: ValueConstructor::public( subject_tipo, ValueConstructorVariant::LocalVariable { location: Span::empty(), }, ), name: constr_var, variant_name: String::new(), }) } else { ir_stack.push(Air::When { scope: scope.clone(), subject_name, tipo: subject_tipo, }); let mut scope = scope; scope.push(self.id_gen.next()); self.build_ir(&subject, ir_stack, scope); } ir_stack.append(&mut pattern_vec); } } } TypedExpr::If { branches, final_else, tipo, .. } => { let mut if_ir = vec![]; for (index, branch) in branches.iter().enumerate() { let mut branch_scope = scope.clone(); branch_scope.push(self.id_gen.next()); if index == 0 { if_ir.push(Air::If { scope: scope.clone(), tipo: tipo.clone(), }); } else { if_ir.push(Air::If { scope: branch_scope.clone(), tipo: tipo.clone(), }); } self.build_ir(&branch.condition, &mut if_ir, branch_scope.clone()); self.build_ir(&branch.body, &mut if_ir, branch_scope); } let mut branch_scope = scope; branch_scope.push(self.id_gen.next()); self.build_ir(final_else, &mut if_ir, branch_scope); ir_stack.append(&mut if_ir); } TypedExpr::RecordAccess { record, index, tipo, .. } => { self.needs_field_access = true; ir_stack.push(Air::RecordAccess { scope: scope.clone(), record_index: *index, tipo: tipo.clone(), }); self.build_ir(record, ir_stack, scope); } TypedExpr::ModuleSelect { constructor, module_name, tipo, .. } => match constructor { ModuleValueConstructor::Record { .. } => { todo!("Records from modules not yet implemented.") } ModuleValueConstructor::Fn { name, module, .. } => { let func = self.functions.get(&FunctionAccessKey { module_name: module_name.clone(), function_name: name.clone(), variant_name: String::new(), }); if let Some(func) = func { ir_stack.push(Air::Var { scope, constructor: ValueConstructor::public( tipo.clone(), ValueConstructorVariant::ModuleFn { name: name.clone(), field_map: None, module: module.clone(), arity: func.arguments.len(), location: Span::empty(), builtin: None, }, ), name: format!("{module}_{name}"), variant_name: String::new(), }); } else { let type_info = self.module_types.get(module_name).unwrap(); let value = type_info.values.get(name).unwrap(); match &value.variant { ValueConstructorVariant::ModuleFn { builtin, .. } => { let builtin = builtin.unwrap(); ir_stack.push(Air::Builtin { func: builtin, scope, tipo: tipo.clone(), }); } _ => unreachable!(), } } } ModuleValueConstructor::Constant { literal, .. } => { constants_ir(literal, ir_stack, scope); } }, TypedExpr::Todo { label, tipo, .. } => { ir_stack.push(Air::Todo { scope, label: label.clone(), tipo: tipo.clone(), }); } TypedExpr::RecordUpdate { spread, args, tipo, .. } => { let mut update_ir = vec![]; let mut spread_scope = scope.clone(); let mut index_types = vec![]; let mut highest_index = 0; spread_scope.push(self.id_gen.next()); self.build_ir(spread, &mut update_ir, spread_scope); for arg in args { let mut arg_scope = scope.clone(); arg_scope.push(self.id_gen.next()); self.build_ir(&arg.value, &mut update_ir, arg_scope); if arg.index > highest_index { highest_index = arg.index; } index_types.push((arg.index, arg.value.tipo())); } ir_stack.push(Air::RecordUpdate { scope, highest_index, indices: index_types, tipo: tipo.clone(), }); ir_stack.append(&mut update_ir); } TypedExpr::UnOp { value, op, .. } => { ir_stack.push(Air::UnOp { scope: scope.clone(), op: *op, }); self.build_ir(value, ir_stack, scope); } TypedExpr::Tuple { elems, tipo, .. } => { ir_stack.push(Air::Tuple { scope: scope.clone(), tipo: tipo.clone(), count: elems.len(), }); let mut elems_air = vec![]; for elem in elems { let mut scope = scope.clone(); scope.push(self.id_gen.next()); self.build_ir(elem, &mut elems_air, scope); } ir_stack.append(&mut elems_air); } TypedExpr::Trace { tipo, then, text, .. } => { let mut scope = scope; ir_stack.push(Air::Trace { tipo: tipo.clone(), scope: scope.clone(), }); scope.push(self.id_gen.next()); self.build_ir(text, ir_stack, scope.clone()); scope.push(self.id_gen.next()); self.build_ir(then, ir_stack, scope); } TypedExpr::TupleIndex { index, tuple, .. } => { ir_stack.push(Air::TupleIndex { scope: scope.clone(), tipo: tuple.tipo(), tuple_index: *index, }); self.build_ir(tuple, ir_stack, scope); } TypedExpr::ErrorTerm { tipo, label, .. } => { ir_stack.push(Air::ErrorTerm { scope, tipo: tipo.clone(), label: label.clone(), }); } } } fn handle_each_clause( &mut self, ir_stack: &mut Vec, clause_properties: &mut ClauseProperties, clauses: &[Clause, String>], subject_type: &Arc, scope: Vec, ) { for (index, clause) in clauses.iter().enumerate() { // scope per clause is different let mut scope = scope.clone(); scope.push(self.id_gen.next()); // holds when clause pattern Air let mut clause_subject_vec = vec![]; let mut clause_then_vec = vec![]; // reset complex clause setting per clause back to default *clause_properties.is_complex_clause() = false; self.build_ir(&clause.then, &mut clause_then_vec, scope.clone()); if let Some(clause_guard) = &clause.guard { let mut clause_guard_vec = vec![]; *clause_properties.is_complex_clause() = true; let clause_guard_name = format!("__clause_guard_{}", self.id_gen.next()); let mut clause_guard_scope = scope.clone(); clause_guard_scope.push(self.id_gen.next()); clause_guard_vec.push(Air::Let { scope: clause_guard_scope.clone(), name: clause_guard_name.clone(), }); handle_clause_guard( clause_guard, &mut clause_guard_vec, clause_guard_scope.clone(), ); clause_guard_vec.push(Air::ClauseGuard { scope: clause_guard_scope.clone(), subject_name: clause_guard_name, tipo: bool(), }); clause_guard_vec.push(Air::Bool { scope: clause_guard_scope.clone(), value: true, }); clause_guard_vec.append(&mut clause_then_vec); clause_then_vec = clause_guard_vec; } match clause_properties { ClauseProperties::ConstrClause { original_subject_name, .. } => { let subject_name = original_subject_name.clone(); self.when_ir( &clause.pattern[0], &mut clause_subject_vec, &mut clause_then_vec, subject_type, clause_properties, scope.clone(), ); ir_stack.push(Air::Clause { scope, tipo: subject_type.clone(), complex_clause: *clause_properties.is_complex_clause(), subject_name, }); } ClauseProperties::ListClause { original_subject_name, current_index, .. } => { let current_clause_index = if let Pattern::List { elements, .. } = &clause.pattern[0] { elements.len() } else if let Pattern::Assign { pattern, .. } = &clause.pattern[0] { if let Pattern::List { elements, .. } = pattern.as_ref() { elements.len() } else { unreachable!("{:#?}", pattern) } } else { unreachable!("{:#?}", &clause.pattern[0]) }; let prev_index = *current_index; let subject_name = if current_clause_index == 0 { original_subject_name.clone() } else { format!("__tail_{}", current_clause_index - 1) }; self.when_ir( &clause.pattern[0], &mut clause_subject_vec, &mut clause_then_vec, subject_type, clause_properties, scope.clone(), ); let next_tail = if index == clauses.len() - 1 { None } else { let next_list_size = if let Pattern::List { elements, .. } = &clauses[index + 1].pattern[0] { elements.len() } else if let Pattern::Assign { pattern, .. } = &clauses[index + 1].pattern[0] { if let Pattern::List { elements, .. } = pattern.as_ref() { elements.len() } else { unreachable!("{:#?}", pattern) } } else { unreachable!() }; if next_list_size == current_clause_index { None } else { Some(format!("__tail_{current_clause_index}")) } }; if current_clause_index as i64 == prev_index { ir_stack.push(Air::WrapClause { scope }); } else { ir_stack.push(Air::ListClause { scope, tipo: subject_type.clone(), tail_name: subject_name, next_tail_name: next_tail, complex_clause: *clause_properties.is_complex_clause(), }); } match clause_properties { ClauseProperties::ListClause { current_index, .. } => { *current_index = current_clause_index as i64; } _ => unreachable!(), } } ClauseProperties::TupleClause { original_subject_name, defined_tuple_indices, .. } => { let prev_defined_tuple_indices = defined_tuple_indices.clone(); let subject_name = original_subject_name.clone(); self.when_ir( &clause.pattern[0], &mut clause_subject_vec, &mut clause_then_vec, subject_type, clause_properties, scope.clone(), ); let current_defined_tuple_indices = match clause_properties { ClauseProperties::TupleClause { defined_tuple_indices, .. } => defined_tuple_indices.clone(), _ => unreachable!(), }; let indices_to_define = current_defined_tuple_indices .difference(&prev_defined_tuple_indices) .cloned() .collect(); ir_stack.push(Air::TupleClause { scope, tipo: subject_type.clone(), indices: indices_to_define, predefined_indices: prev_defined_tuple_indices, subject_name, count: subject_type.get_inner_types().len(), complex_clause: *clause_properties.is_complex_clause(), }); } } ir_stack.append(&mut clause_subject_vec); } } fn when_ir( &mut self, pattern: &Pattern>, pattern_vec: &mut Vec, values: &mut Vec, tipo: &Type, clause_properties: &mut ClauseProperties, scope: Vec, ) { match pattern { Pattern::Int { value, .. } => { pattern_vec.push(Air::Int { scope, value: value.clone(), }); pattern_vec.append(values); } Pattern::Var { name, .. } => { pattern_vec.push(Air::Void { scope: scope.clone(), }); pattern_vec.push(Air::Let { scope: scope.clone(), name: name.clone(), }); pattern_vec.push(Air::Var { scope, constructor: ValueConstructor::public( tipo.clone().into(), ValueConstructorVariant::LocalVariable { location: Span::empty(), }, ), name: clause_properties.original_subject_name().clone(), variant_name: String::new(), }); pattern_vec.append(values); } Pattern::Assign { name, pattern, .. } => { let mut new_vec = vec![]; new_vec.push(Air::Let { scope: scope.clone(), name: name.clone(), }); new_vec.push(Air::Var { scope: scope.clone(), constructor: ValueConstructor::public( tipo.clone().into(), ValueConstructorVariant::LocalVariable { location: Span::empty(), }, ), name: clause_properties.original_subject_name().clone(), variant_name: String::new(), }); new_vec.append(values); self.when_ir( pattern, pattern_vec, &mut new_vec, tipo, clause_properties, scope, ); } Pattern::Discard { .. } => { pattern_vec.push(Air::Void { scope }); pattern_vec.append(values); } Pattern::List { elements, tail, .. } => { for element in elements { check_when_pattern_needs(element, clause_properties); } if let Some(tail) = tail { check_when_pattern_needs(tail, clause_properties); } *clause_properties.needs_constr_var() = false; pattern_vec.push(Air::Void { scope: scope.clone(), }); self.when_recursive_ir( pattern, pattern_vec, values, clause_properties, tipo, scope, ); } Pattern::Constructor { arguments, name: constr_name, .. } => { let mut temp_clause_properties = clause_properties.clone(); *temp_clause_properties.needs_constr_var() = false; if tipo.is_bool() { pattern_vec.push(Air::Bool { scope, value: constr_name == "True", }); } else { for arg in arguments { check_when_pattern_needs(&arg.value, &mut temp_clause_properties); } // find data type definition let data_type = lookup_data_type_by_tipo(self.data_types.clone(), tipo).unwrap(); let (index, _) = data_type .constructors .iter() .enumerate() .find(|(_, dt)| &dt.name == constr_name) .unwrap(); let mut new_vec = vec![Air::Var { constructor: ValueConstructor::public( tipo.clone().into(), ValueConstructorVariant::LocalVariable { location: Span::empty(), }, ), name: temp_clause_properties.clause_var_name().clone(), scope: scope.clone(), variant_name: String::new(), }]; // if only one constructor, no need to check if data_type.constructors.len() > 1 { // push constructor Index pattern_vec.push(Air::Int { value: index.to_string(), scope: scope.clone(), }); } if *temp_clause_properties.needs_constr_var() { self.when_recursive_ir( pattern, pattern_vec, &mut new_vec, clause_properties, tipo, scope, ); } else { self.when_recursive_ir( pattern, pattern_vec, &mut vec![], clause_properties, tipo, scope, ); } } pattern_vec.append(values); // unify clause properties *clause_properties.is_complex_clause() = *clause_properties.is_complex_clause() || *temp_clause_properties.is_complex_clause(); *clause_properties.needs_constr_var() = *clause_properties.needs_constr_var() || *temp_clause_properties.needs_constr_var(); } Pattern::Tuple { elems, .. } => { for elem in elems { check_when_pattern_needs(elem, clause_properties); } *clause_properties.needs_constr_var() = false; self.when_recursive_ir( pattern, pattern_vec, &mut vec![], clause_properties, tipo, scope, ); pattern_vec.append(values); } } } fn when_recursive_ir( &mut self, pattern: &Pattern>, pattern_vec: &mut Vec, values: &mut Vec, clause_properties: &mut ClauseProperties, tipo: &Type, scope: Vec, ) { match pattern { Pattern::Int { .. } => unreachable!(), Pattern::Var { .. } => unreachable!(), Pattern::Assign { .. } => todo!("Nested assign not yet implemented"), Pattern::Discard { .. } => { pattern_vec.push(Air::Void { scope }); pattern_vec.append(values); } Pattern::List { elements, tail, .. } => { let mut names = vec![]; let mut nested_pattern = vec![]; let items_type = &tipo.get_inner_types()[0]; // let mut nested_pattern = vec![]; for element in elements { let name = self.nested_pattern_ir_and_label( element, &mut nested_pattern, items_type, scope.clone(), *clause_properties.is_final_clause(), ); names.push(name.unwrap_or_else(|| "_".to_string())) } let mut tail_name = String::new(); if let Some(tail) = tail { match &**tail { Pattern::Var { name, .. } => { tail_name = name.clone(); } Pattern::Discard { .. } => {} _ => unreachable!("Patterns in tail of list should not allow this"), } } let tail_head_names = names .iter() .enumerate() .filter(|(_, name)| *name != &"_".to_string()) .map(|(index, name)| { if index == 0 { ( clause_properties.original_subject_name().clone(), name.clone(), ) } else { (format!("__tail_{}", index - 1), name.clone()) } }) .collect_vec(); if tail.is_some() && !elements.is_empty() { let tail_var = if elements.len() == 1 { clause_properties.original_subject_name().clone() } else { format!("__tail_{}", elements.len() - 2) }; pattern_vec.push(Air::ListExpose { scope, tipo: tipo.clone().into(), tail_head_names, tail: Some((tail_var, tail_name)), }); } else if !elements.is_empty() { pattern_vec.push(Air::ListExpose { scope, tipo: tipo.clone().into(), tail_head_names, tail: None, }); } pattern_vec.append(&mut nested_pattern); pattern_vec.append(values); } Pattern::Constructor { is_record, name: constr_name, arguments, constructor, tipo, .. } => { let data_type = lookup_data_type_by_tipo(self.data_types.clone(), tipo).unwrap(); let (_, constructor_type) = data_type .constructors .iter() .enumerate() .find(|(_, dt)| &dt.name == constr_name) .unwrap(); let mut nested_pattern = vec![]; if *is_record { let field_map = match constructor { PatternConstructor::Record { field_map, .. } => field_map.clone().unwrap(), }; let mut type_map: IndexMap> = IndexMap::new(); for (index, arg) in tipo.arg_types().unwrap().iter().enumerate() { let label = constructor_type.arguments[index].label.clone().unwrap(); let field_type = arg.clone(); type_map.insert(label, field_type); } let arguments_index = arguments .iter() .filter_map(|item| { let label = item.label.clone().unwrap_or_default(); let field_index = field_map .fields .get(&label) .map(|(index, _)| index) .unwrap_or(&0); let var_name = self.nested_pattern_ir_and_label( &item.value, &mut nested_pattern, type_map.get(&label).unwrap_or( &Type::App { public: true, module: "".to_string(), name: "Discard".to_string(), args: vec![], } .into(), ), scope.clone(), *clause_properties.is_final_clause(), ); var_name.map_or( Some((label.clone(), "_".to_string(), *field_index)), |var_name| Some((label, var_name, *field_index)), ) }) .sorted_by(|item1, item2| item1.2.cmp(&item2.2)) .collect::>(); if !arguments_index.is_empty() { pattern_vec.push(Air::FieldsExpose { indices: arguments_index .iter() .map(|(label, var_name, index)| { let field_type = type_map .get(label) .unwrap_or_else(|| type_map.get_index(*index).unwrap().1); (*index, var_name.clone(), field_type.clone()) }) .collect_vec(), scope, check_last_item: false, }); } } else { 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 arguments_index = arguments .iter() .enumerate() .filter_map(|(index, item)| { let var_name = self.nested_pattern_ir_and_label( &item.value, &mut nested_pattern, type_map.get(&index).unwrap(), scope.clone(), *clause_properties.is_final_clause(), ); var_name.map_or(Some(("_".to_string(), index)), |var_name| { Some((var_name, index)) }) }) .collect::>(); if !arguments_index.is_empty() { pattern_vec.push(Air::FieldsExpose { indices: arguments_index .iter() .map(|(name, index)| { let field_type = type_map.get(index).unwrap(); (*index, name.clone(), field_type.clone()) }) .collect_vec(), scope, check_last_item: false, }); } } pattern_vec.append(values); pattern_vec.append(&mut nested_pattern); } Pattern::Tuple { elems, .. } => { let mut names = vec![]; let mut nested_pattern = vec![]; let items_type = &tipo.get_inner_types(); for (index, element) in elems.iter().enumerate() { let name = self.nested_pattern_ir_and_label( element, &mut nested_pattern, &items_type[index], scope.clone(), *clause_properties.is_final_clause(), ); names.push((name.unwrap_or_else(|| "_".to_string()), index)) } let mut defined_indices = match clause_properties.clone() { ClauseProperties::TupleClause { defined_tuple_indices, .. } => defined_tuple_indices, _ => unreachable!(), }; let mut previous_defined_names = vec![]; for (name, index) in names.clone() { if let Some(defined_index) = defined_indices .iter() .find(|(defined_index, _)| *defined_index == index) { previous_defined_names.push(defined_index.clone()); } else { defined_indices.insert((index, name)); } } for (index, name) in previous_defined_names { let new_name = names .iter() .find(|(_, current_index)| *current_index == index) .map(|(new_name, _)| new_name) .unwrap(); let pattern_type = &tipo.get_inner_types()[index]; pattern_vec.push(Air::Let { scope: scope.clone(), name: new_name.clone(), }); pattern_vec.push(Air::Var { scope: scope.clone(), constructor: ValueConstructor::public( pattern_type.clone(), ValueConstructorVariant::LocalVariable { location: Span::empty(), }, ), name, variant_name: String::new(), }); } match clause_properties { ClauseProperties::TupleClause { defined_tuple_indices, .. } => { *defined_tuple_indices = defined_indices; } _ => unreachable!(), } pattern_vec.append(&mut nested_pattern); pattern_vec.append(values); } } } fn nested_pattern_ir_and_label( &mut self, pattern: &Pattern>, pattern_vec: &mut Vec, pattern_type: &Arc, scope: Vec, final_clause: bool, ) -> Option { match pattern { Pattern::Var { name, .. } => Some(name.clone()), Pattern::Discard { .. } => None, a @ Pattern::List { elements, tail, .. } => { let item_name = format!("__list_item_id_{}", self.id_gen.next()); let new_tail_name = "__tail".to_string(); if elements.is_empty() { pattern_vec.push(Air::ListClauseGuard { scope: scope.clone(), tipo: pattern_type.clone(), tail_name: item_name.clone(), next_tail_name: None, inverse: false, }); pattern_vec.push(Air::Void { scope }); } else { for (index, _) in elements.iter().enumerate() { let prev_tail_name = if index == 0 { item_name.clone() } else { format!("{}_{}", new_tail_name, index - 1) }; let mut clause_properties = ClauseProperties::ListClause { clause_var_name: item_name.clone(), needs_constr_var: false, is_complex_clause: false, original_subject_name: item_name.clone(), current_index: index as i64, final_clause }; let tail_name = format!("{new_tail_name}_{index}"); if elements.len() - 1 == index { if tail.is_some() { pattern_vec.push(Air::ListClauseGuard { scope: scope.clone(), tipo: pattern_type.clone(), tail_name: prev_tail_name, next_tail_name: None, inverse: true, }); self.when_ir( a, pattern_vec, &mut vec![], pattern_type, &mut clause_properties, scope.clone(), ); } else { pattern_vec.push(Air::ListClauseGuard { scope: scope.clone(), tipo: pattern_type.clone(), tail_name: prev_tail_name, next_tail_name: Some(tail_name.clone()), inverse: true, }); pattern_vec.push(Air::Void { scope: scope.clone(), }); pattern_vec.push(Air::ListClauseGuard { scope: scope.clone(), tipo: pattern_type.clone(), tail_name: tail_name.clone(), next_tail_name: None, inverse: false, }); self.when_ir( a, pattern_vec, &mut vec![], pattern_type, &mut clause_properties, scope.clone(), ); } } else { pattern_vec.push(Air::ListClauseGuard { scope: scope.clone(), tipo: pattern_type.clone(), tail_name: prev_tail_name, next_tail_name: Some(tail_name), inverse: true, }); pattern_vec.push(Air::Void { scope: scope.clone() }); }; } } Some(item_name) } a @ Pattern::Constructor { tipo, name: constr_name, .. } => { let id = self.id_gen.next(); let constr_var_name = format!("{constr_name}_{id}"); let data_type = lookup_data_type_by_tipo(self.data_types.clone(), tipo).unwrap(); if data_type.constructors.len() > 1 { if final_clause{ pattern_vec.push(Air::Finally { scope: scope.clone() }); } else { pattern_vec.push(Air::ClauseGuard { scope: scope.clone(), tipo: tipo.clone(), subject_name: constr_var_name.clone(), }); } } let mut clause_properties = ClauseProperties::ConstrClause { clause_var_name: constr_var_name.clone(), needs_constr_var: false, is_complex_clause: false, original_subject_name: constr_var_name.clone(), final_clause }; self.when_ir( a, pattern_vec, &mut vec![], tipo, &mut clause_properties, scope, ); Some(constr_var_name) } a @ Pattern::Tuple { elems, .. } => { let item_name = format!("__tuple_item_id_{}", self.id_gen.next()); let mut clause_properties = ClauseProperties::TupleClause { clause_var_name: item_name.clone(), needs_constr_var: false, is_complex_clause: false, original_subject_name: item_name.clone(), defined_tuple_indices: IndexSet::new(), final_clause }; let mut inner_pattern_vec = vec![]; self.when_ir( a, &mut inner_pattern_vec, &mut vec![], pattern_type, &mut clause_properties, scope.clone(), ); let defined_indices = match clause_properties.clone() { ClauseProperties::TupleClause { defined_tuple_indices, .. } => defined_tuple_indices, _ => unreachable!(), }; pattern_vec.push(Air::TupleClause { scope, tipo: pattern_type.clone(), indices: defined_indices, predefined_indices: IndexSet::new(), subject_name: clause_properties.original_subject_name().to_string(), count: elems.len(), complex_clause: false, }); pattern_vec.append(&mut inner_pattern_vec); Some(item_name) } Pattern::Assign { .. } => todo!("Nested assign is not yet done"), Pattern::Int { .. } => todo!("Nested pattern-match on integers isn't implemented yet. Use when clause-guard as an alternative, or break down the pattern."), } } fn assignment_ir( &mut self, pattern: &Pattern>, pattern_vec: &mut Vec, value_vec: &mut Vec, tipo: &Type, assignment_properties: AssignmentProperties, scope: Vec, ) { if assignment_properties.value_type.is_data() && !tipo.is_data() && !pattern.is_discard() { value_vec.insert( 0, Air::UnWrapData { scope: scope.clone(), tipo: tipo.clone().into(), }, ); } if !assignment_properties.value_type.is_data() && tipo.is_data() && !pattern.is_discard() { value_vec.insert( 0, Air::WrapData { scope: scope.clone(), tipo: assignment_properties.value_type.clone(), }, ) } match pattern { Pattern::Int { .. } => unreachable!(), Pattern::Var { name, .. } => { pattern_vec.push(Air::Let { name: name.clone(), scope: scope.clone(), }); pattern_vec.append(value_vec); if matches!(assignment_properties.kind, AssignmentKind::Expect) && assignment_properties.value_type.is_data() && !tipo.is_data() { let mut assert_vec = vec![]; self.recursive_assert_pattern( pattern, &mut assert_vec, value_vec, tipo, assignment_properties, scope, ); pattern_vec.append(&mut assert_vec); } } Pattern::Assign { .. } => todo!("Assign not yet implemented yet"), Pattern::Discard { .. } => { pattern_vec.push(Air::Let { name: "_".to_string(), scope, }); pattern_vec.append(value_vec); } list @ Pattern::List { .. } => { if matches!(assignment_properties.kind, AssignmentKind::Expect) && assignment_properties.value_type.is_data() && !tipo.is_data() { self.recursive_assert_pattern( list, pattern_vec, value_vec, tipo, assignment_properties, scope, ); } else { self.pattern_ir( list, pattern_vec, value_vec, tipo, assignment_properties, scope, ); } } // TODO: Check constr for assert on all cases constr @ Pattern::Constructor { .. } => { if matches!(assignment_properties.kind, AssignmentKind::Expect) && assignment_properties.value_type.is_data() && !tipo.is_data() { self.recursive_assert_pattern( constr, pattern_vec, value_vec, tipo, assignment_properties, scope, ); } else { self.pattern_ir( constr, pattern_vec, value_vec, tipo, assignment_properties, scope, ); } } tuple @ Pattern::Tuple { .. } => { if matches!(assignment_properties.kind, AssignmentKind::Expect) && assignment_properties.value_type.is_data() && !tipo.is_data() { self.recursive_assert_pattern( tuple, pattern_vec, value_vec, tipo, assignment_properties, scope, ); } else { self.pattern_ir( tuple, pattern_vec, value_vec, tipo, assignment_properties, scope, ); } } } } fn pattern_ir( &mut self, pattern: &Pattern>, pattern_vec: &mut Vec, values: &mut Vec, tipo: &Type, assignment_properties: AssignmentProperties, scope: Vec, ) { match pattern { Pattern::Int { .. } => todo!(), Pattern::Var { .. } => todo!(), Pattern::Assign { .. } => todo!(), Pattern::Discard { .. } => todo!(), Pattern::List { elements, tail, .. } => { let mut elements_vec = vec![]; let mut names = vec![]; for element in elements { match element { Pattern::Var { name, .. } => { names.push(name.clone()); } a @ (Pattern::List { .. } | Pattern::Constructor { .. } | Pattern::Tuple { .. }) => { let mut var_vec = vec![]; let item_name = format!("list_item_id_{}", self.id_gen.next()); names.push(item_name.clone()); var_vec.push(Air::Var { constructor: ValueConstructor::public( Type::App { public: true, module: String::new(), name: String::new(), args: vec![], } .into(), ValueConstructorVariant::LocalVariable { location: Span::empty(), }, ), name: item_name, scope: scope.clone(), variant_name: String::new(), }); self.pattern_ir( a, &mut elements_vec, &mut var_vec, &tipo.get_inner_types()[0], assignment_properties.clone(), scope.clone(), ); } Pattern::Int { .. } => todo!(), Pattern::Assign { .. } => todo!(), Pattern::Discard { .. } => { names.push("_".to_string()); } } } if let Some(tail) = tail { match &**tail { Pattern::Var { name, .. } => names.push(name.clone()), Pattern::Discard { .. } => {} _ => unreachable!(), } } if !names.is_empty() { pattern_vec.push(Air::ListAccessor { names, tail: tail.is_some(), scope, tipo: tipo.clone().into(), check_last_item: true, }); } else { pattern_vec.push(Air::Let { scope, name: "_".to_string(), }) } pattern_vec.append(values); pattern_vec.append(&mut elements_vec); } Pattern::Constructor { arguments, constructor, tipo: constr_tipo, name: constr_name, .. } => { let mut nested_pattern = vec![]; let field_map = match constructor { PatternConstructor::Record { field_map, .. } => field_map.clone(), }; let mut type_map: IndexMap> = IndexMap::new(); for (index, arg) in constr_tipo.arg_types().unwrap().iter().enumerate() { let field_type = arg.clone(); type_map.insert(index, field_type); } let arguments_index = arguments .iter() .enumerate() .filter_map(|(index, item)| { let label = item.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 }; self.extract_arg_and_index( &item.value, field_index, &mut nested_pattern, type_map.get(&field_index).unwrap(), &assignment_properties, &scope, ) .map_or(Some(("_".to_string(), index)), Some) }) .sorted_by(|item1, item2| item1.1.cmp(&item2.1)) .collect::>(); if !arguments_index.is_empty() { pattern_vec.push(Air::FieldsExpose { indices: arguments_index .iter() .map(|(var_name, index)| { let field_type = type_map.get(index).unwrap(); (*index, var_name.clone(), field_type.clone()) }) .collect_vec(), scope: scope.clone(), check_last_item: false, }); } else { pattern_vec.push(Air::Let { scope: scope.clone(), name: "_".to_string(), }); } if matches!(assignment_properties.kind, AssignmentKind::Expect) { let data_type = lookup_data_type_by_tipo(self.data_types.clone(), tipo).unwrap(); let (index, _) = data_type .constructors .iter() .enumerate() .find(|(_, constr)| &constr.name == constr_name) .unwrap(); let constr_name = format!("__{}_{}", constr_name, self.id_gen.next()); pattern_vec.push(Air::Let { scope: scope.clone(), name: constr_name.clone(), }); pattern_vec.append(values); let mut scope = scope; scope.push(self.id_gen.next()); pattern_vec.push(Air::AssertConstr { scope: scope.clone(), constr_index: index, }); pattern_vec.push(Air::Var { scope: scope.clone(), constructor: ValueConstructor::public( tipo.clone().into(), ValueConstructorVariant::LocalVariable { location: Span::empty(), }, ), name: constr_name.clone(), variant_name: String::new(), }); pattern_vec.push(Air::Var { scope, constructor: ValueConstructor::public( tipo.clone().into(), ValueConstructorVariant::LocalVariable { location: Span::empty(), }, ), name: constr_name, variant_name: String::new(), }); } else { pattern_vec.append(values); } pattern_vec.append(&mut nested_pattern); } Pattern::Tuple { elems, .. } => { let mut nested_pattern = vec![]; let mut type_map: IndexMap> = IndexMap::new(); for (index, arg) in tipo.get_inner_types().iter().enumerate() { let field_type = arg.clone(); type_map.insert(index, field_type); } let arguments_index = elems .iter() .enumerate() .filter_map(|(tuple_index, item)| { self.extract_arg_and_index( item, tuple_index, &mut nested_pattern, type_map.get(&tuple_index).unwrap(), &assignment_properties, &scope, ) }) .sorted_by(|item1, item2| item1.1.cmp(&item2.1)) .collect::>(); if !arguments_index.is_empty() { let mut current_index = 0; let mut final_args = vec![]; for index in 0..elems.len() { if arguments_index.get(current_index).is_some() && arguments_index[current_index].1 == index { final_args.push(arguments_index.get(current_index).unwrap().clone()); current_index += 1; } else { final_args.push(("_".to_string(), index)); } } pattern_vec.push(Air::TupleAccessor { scope, names: final_args.into_iter().map(|(item, _)| item).collect_vec(), tipo: tipo.clone().into(), check_last_item: false, }); } else { pattern_vec.push(Air::Let { scope, name: "_".to_string(), }); } pattern_vec.append(values); pattern_vec.append(&mut nested_pattern); } } } pub fn recursive_assert_pattern( &mut self, pattern: &Pattern>, pattern_vec: &mut Vec, value_vec: &mut Vec, tipo: &Type, assignment_properties: AssignmentProperties, scope: Vec, ) { match pattern { Pattern::Int { .. } => unreachable!(), Pattern::Var { name, .. } => { pattern_vec.append(value_vec); self.recursive_assert_tipo(tipo, pattern_vec, name, scope); } Pattern::Assign { .. } => todo!(), Pattern::Discard { .. } => unreachable!(), Pattern::List { elements, tail, .. } => { let mut assert_list_vec = vec![]; let inner_list_type = &tipo.get_inner_types()[0]; let mut names = vec![]; for element in elements { match element { Pattern::Var { name, .. } => { names.push(name.clone()); } Pattern::Assign { .. } => todo!(), l @ (Pattern::List { .. } | Pattern::Constructor { .. } | Pattern::Tuple { .. }) => { let name = format!("list_item_id_{}", self.id_gen.next()); names.push(name.clone()); self.recursive_assert_pattern( l, &mut assert_list_vec, &mut vec![Air::Var { scope: scope.clone(), constructor: ValueConstructor::public( tipo.clone().into(), ValueConstructorVariant::LocalVariable { location: Span::empty(), }, ), name, variant_name: String::new(), }], inner_list_type, assignment_properties.clone(), scope.clone(), ); } _ => {} } } let name = if let Some(tail) = tail { match &**tail { Pattern::Var { name, .. } => name.clone(), _ => format!("__tail_{}", self.id_gen.next()), } } else { format!("__tail_{}", self.id_gen.next()) }; self.recursive_assert_tipo(tipo, &mut assert_list_vec, &name, scope.clone()); names.push(name); pattern_vec.push(Air::ListAccessor { scope, tipo: tipo.clone().into(), names, tail: true, check_last_item: false, }); pattern_vec.append(value_vec); pattern_vec.append(&mut assert_list_vec); } Pattern::Constructor { arguments, constructor, name: constr_name, tipo, .. } => { let mut nested_pattern = vec![]; let field_map = match constructor { PatternConstructor::Record { field_map, .. } => field_map.clone().unwrap(), }; let data_type = lookup_data_type_by_tipo(self.data_types.clone(), tipo).unwrap(); let (index, data_type_constr) = data_type .constructors .iter() .enumerate() .find(|(_, constr)| &constr.name == constr_name) .unwrap(); 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 arguments_index = arguments .iter() .filter_map(|item| { let label = item.label.clone().unwrap_or_default(); let field_index = field_map.fields.get(&label).map(|x| &x.0).unwrap_or(&0); self.extract_arg_and_index( &item.value, *field_index, &mut nested_pattern, type_map.get(field_index).unwrap(), &assignment_properties, &scope, ) }) .sorted_by(|item1, item2| item1.1.cmp(&item2.1)) .collect::>(); let total_fields = data_type_constr.arguments.len(); let mut final_args = vec![]; let mut current_index = 0; for index in 0..total_fields { if arguments_index.get(current_index).is_some() && arguments_index[current_index].1 == index { final_args.push(arguments_index.get(current_index).unwrap().clone()); current_index += 1; } else { let id_next = self.id_gen.next(); final_args.push((format!("__field_{index}_{id_next}"), index)); self.recursive_assert_tipo( type_map.get(&index).unwrap(), &mut nested_pattern, &format!("__field_{index}_{id_next}"), scope.clone(), ) } } let constr_var = format!("__constr_{}", self.id_gen.next()); pattern_vec.push(Air::Let { scope: scope.clone(), name: constr_var.clone(), }); pattern_vec.append(value_vec); let mut scope = scope; scope.push(self.id_gen.next()); pattern_vec.push(Air::AssertConstr { scope: scope.clone(), constr_index: index, }); pattern_vec.push(Air::Var { scope: scope.clone(), constructor: ValueConstructor::public( tipo.clone(), ValueConstructorVariant::LocalVariable { location: Span::empty(), }, ), name: constr_var.clone(), variant_name: String::new(), }); if !arguments_index.is_empty() { pattern_vec.push(Air::FieldsExpose { indices: final_args .iter() .map(|(var_name, index)| { let field_type = type_map.get(index).unwrap(); (*index, var_name.clone(), field_type.clone()) }) .collect_vec(), scope: scope.clone(), check_last_item: true, }); pattern_vec.push(Air::Var { scope, constructor: ValueConstructor::public( tipo.clone(), ValueConstructorVariant::LocalVariable { location: Span::empty(), }, ), name: constr_var, variant_name: String::new(), }); } pattern_vec.append(&mut nested_pattern); } Pattern::Tuple { elems, .. } => { 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 mut nested_pattern = vec![]; let arguments_index = elems .iter() .enumerate() .filter_map(|(index, item)| { let field_index = index; self.extract_arg_and_index( item, field_index, &mut nested_pattern, type_map.get(&field_index).unwrap(), &assignment_properties, &scope, ) }) .sorted_by(|item1, item2| item1.1.cmp(&item2.1)) .collect::>(); let total_fields = type_map.len(); let mut final_args = vec![]; let mut current_index = 0; for index in 0..total_fields { if arguments_index.get(current_index).is_some() && arguments_index[current_index].1 == index { final_args.push(arguments_index.get(current_index).unwrap().clone()); current_index += 1; } else { let id_next = self.id_gen.next(); final_args.push((format!("__tuple_{index}_{id_next}"), index)); self.recursive_assert_tipo( type_map.get(&index).unwrap(), &mut nested_pattern, &format!("__tuple_{index}_{id_next}"), scope.clone(), ) } } if !final_args.is_empty() { pattern_vec.push(Air::TupleAccessor { scope, names: final_args.into_iter().map(|(item, _)| item).collect_vec(), tipo: tipo.clone().into(), check_last_item: true, }); } pattern_vec.append(value_vec); pattern_vec.append(&mut nested_pattern); } } } fn recursive_assert_tipo( &mut self, tipo: &Type, assert_vec: &mut Vec, name: &str, scope: Vec, ) { let mut tipo = tipo.clone().into(); replace_opaque_type(&mut tipo, self.data_types.clone()); if tipo.is_bool() || tipo.is_bytearray() || tipo.is_int() || tipo.is_string() || tipo.is_void() || tipo.get_generic().is_some() || tipo.is_data() { } else if tipo.is_map() { self.used_data_assert_on_list = true; let new_id = self.id_gen.next(); let id_pair = (self.id_gen.next(), self.id_gen.next()); let inner_list_type = &tipo.get_inner_types()[0]; let inner_pair_types = inner_list_type.get_inner_types(); assert_vec.push(Air::Call { scope: scope.clone(), count: 2, tipo: tipo.clone(), }); assert_vec.push(Air::Builtin { scope: scope.clone(), func: DefaultFunction::ChooseUnit, tipo: tipo.clone(), }); assert_vec.push(Air::Call { scope: scope.clone(), count: 2, tipo: tipo.clone(), }); assert_vec.push(Air::Var { scope: scope.clone(), constructor: ValueConstructor::public( tipo.clone(), ValueConstructorVariant::LocalVariable { location: Span::empty(), }, ), name: ASSERT_ON_LIST.to_string(), variant_name: String::new(), }); assert_vec.push(Air::Var { scope: scope.clone(), constructor: ValueConstructor::public( tipo.clone(), ValueConstructorVariant::LocalVariable { location: Span::empty(), }, ), name: name.to_owned(), variant_name: String::new(), }); assert_vec.push(Air::Fn { scope: scope.clone(), params: vec![format!("__pair_{new_id}")], }); assert_vec.push(Air::TupleAccessor { scope: scope.clone(), names: vec![ format!("__pair_fst_{}", id_pair.0), format!("__pair_snd_{}", id_pair.1), ], tipo: inner_list_type.clone(), check_last_item: false, }); assert_vec.push(Air::Var { scope: scope.clone(), constructor: ValueConstructor::public( tipo.clone(), ValueConstructorVariant::LocalVariable { location: Span::empty(), }, ), name: format!("__pair_{new_id}"), variant_name: String::new(), }); self.recursive_assert_tipo( &inner_pair_types[0], assert_vec, &format!("__pair_fst_{}", id_pair.0), scope.clone(), ); self.recursive_assert_tipo( &inner_pair_types[1], assert_vec, &format!("__pair_snd_{}", id_pair.1), scope.clone(), ); assert_vec.push(Air::Void { scope }); } else if tipo.is_list() { self.used_data_assert_on_list = true; let new_id = self.id_gen.next(); let inner_list_type = &tipo.get_inner_types()[0]; assert_vec.push(Air::Call { scope: scope.clone(), count: 2, tipo: tipo.clone(), }); assert_vec.push(Air::Builtin { scope: scope.clone(), func: DefaultFunction::ChooseUnit, tipo: tipo.clone(), }); assert_vec.push(Air::Call { scope: scope.clone(), count: 2, tipo: tipo.clone(), }); assert_vec.push(Air::Var { scope: scope.clone(), constructor: ValueConstructor::public( tipo.clone(), ValueConstructorVariant::LocalVariable { location: Span::empty(), }, ), name: ASSERT_ON_LIST.to_string(), variant_name: String::new(), }); assert_vec.push(Air::Var { scope: scope.clone(), constructor: ValueConstructor::public( tipo.clone(), ValueConstructorVariant::LocalVariable { location: Span::empty(), }, ), name: name.to_owned(), variant_name: String::new(), }); assert_vec.push(Air::Fn { scope: scope.clone(), params: vec![format!("__list_item_{new_id}")], }); assert_vec.push(Air::Let { scope: scope.clone(), name: format!("__list_item_{new_id}"), }); assert_vec.push(Air::UnWrapData { scope: scope.clone(), tipo: inner_list_type.clone(), }); assert_vec.push(Air::Var { scope: scope.clone(), constructor: ValueConstructor::public( tipo.clone(), ValueConstructorVariant::LocalVariable { location: Span::empty(), }, ), name: format!("__list_item_{new_id}"), variant_name: String::new(), }); self.recursive_assert_tipo( inner_list_type, assert_vec, &format!("__list_item_{new_id}"), scope.clone(), ); assert_vec.push(Air::Void { scope }); } else if tipo.is_tuple() { let tuple_inner_types = tipo.get_inner_types(); let mut new_id_list = vec![]; for (index, _) in tuple_inner_types.iter().enumerate() { new_id_list.push((index, self.id_gen.next())); } assert_vec.push(Air::TupleAccessor { scope: scope.clone(), names: new_id_list .iter() .map(|(index, id)| format!("__tuple_index_{index}_{id}")) .collect_vec(), tipo: tipo.clone(), check_last_item: true, }); assert_vec.push(Air::Var { scope: scope.clone(), constructor: ValueConstructor::public( tipo.clone(), ValueConstructorVariant::LocalVariable { location: Span::empty(), }, ), name: name.to_owned(), variant_name: String::new(), }); for (index, name) in new_id_list .into_iter() .map(|(index, id)| (index, format!("__tuple_index_{index}_{id}"))) { self.recursive_assert_tipo( &tuple_inner_types[index], assert_vec, &name, scope.clone(), ); } } else { let data_type = lookup_data_type_by_tipo(self.data_types.clone(), &tipo).unwrap(); let new_id = self.id_gen.next(); assert_vec.push(Air::Call { scope: scope.clone(), count: 2, tipo: tipo.clone(), }); assert_vec.push(Air::Builtin { scope: scope.clone(), func: DefaultFunction::ChooseUnit, tipo: tipo.clone(), }); assert_vec.push(Air::When { scope: scope.clone(), tipo: tipo.clone(), subject_name: format!("__subject_{new_id}"), }); assert_vec.push(Air::Var { scope: scope.clone(), constructor: ValueConstructor::public( tipo.clone(), ValueConstructorVariant::LocalVariable { location: Span::empty(), }, ), name: name.to_owned(), variant_name: String::new(), }); for (index, constr) in data_type.constructors.iter().enumerate() { let arg_indices = constr .arguments .iter() .enumerate() .map(|(index, arg)| { let arg_name = arg .label .clone() .unwrap_or(format!("__field_{index}_{new_id}")); (index, arg_name, arg.tipo.clone()) }) .collect_vec(); assert_vec.push(Air::Clause { scope: scope.clone(), tipo: tipo.clone(), subject_name: format!("__subject_{new_id}"), complex_clause: false, }); assert_vec.push(Air::Int { scope: scope.clone(), value: index.to_string(), }); if !arg_indices.is_empty() { assert_vec.push(Air::FieldsExpose { scope: scope.clone(), indices: arg_indices.clone(), check_last_item: true, }); assert_vec.push(Air::Var { scope: scope.clone(), constructor: ValueConstructor::public( tipo.clone(), ValueConstructorVariant::LocalVariable { location: Span::empty(), }, ), name: name.to_owned(), variant_name: String::new(), }); } for (_, name, tipo) in arg_indices { self.recursive_assert_tipo(&tipo, assert_vec, &name, scope.clone()); } assert_vec.push(Air::Void { scope: scope.clone(), }); } assert_vec.push(Air::ErrorTerm { scope, tipo: tipo.clone(), label: Some("Constr index did not match any type variant".to_string()), }); } } fn extract_arg_and_index( &mut self, item: &Pattern>, field_index: usize, nested_pattern: &mut Vec, tipo: &Type, assignment_properties: &AssignmentProperties, scope: &[u64], ) -> Option<(String, usize)> { { let (discard, var_name) = match item { Pattern::Var { name, .. } => (false, name.clone()), Pattern::Discard { .. } => (true, "".to_string()), a @ Pattern::List { .. } => { let id = self.id_gen.next(); let list_name = format!("__list_{id}"); if matches!(assignment_properties.kind, AssignmentKind::Expect) && assignment_properties.value_type.is_data() && !tipo.is_data() { self.recursive_assert_pattern( a, nested_pattern, &mut vec![Air::Var { scope: scope.to_owned(), constructor: ValueConstructor::public( tipo.clone().into(), ValueConstructorVariant::LocalVariable { location: Span::empty(), }, ), name: list_name.clone(), variant_name: String::new(), }], tipo, assignment_properties.clone(), scope.to_owned(), ); } else { self.pattern_ir( a, nested_pattern, &mut vec![Air::Var { scope: scope.to_owned(), constructor: ValueConstructor::public( tipo.clone().into(), ValueConstructorVariant::LocalVariable { location: Span::empty(), }, ), name: list_name.clone(), variant_name: String::new(), }], tipo, assignment_properties.clone(), scope.to_owned(), ); } (false, list_name) } a @ Pattern::Constructor { tipo, name: constr_name, .. } => { let id = self.id_gen.next(); let constr_name = format!("{constr_name}_{id}"); if matches!(assignment_properties.kind, AssignmentKind::Expect) && assignment_properties.value_type.is_data() && !tipo.is_data() { self.recursive_assert_pattern( a, nested_pattern, &mut vec![Air::Var { scope: scope.to_owned(), constructor: ValueConstructor::public( tipo.clone(), ValueConstructorVariant::LocalVariable { location: Span::empty(), }, ), name: constr_name.clone(), variant_name: String::new(), }], tipo, assignment_properties.clone(), scope.to_owned(), ); } else { self.pattern_ir( a, nested_pattern, &mut vec![Air::Var { scope: scope.to_owned(), constructor: ValueConstructor::public( tipo.clone(), ValueConstructorVariant::LocalVariable { location: Span::empty(), }, ), name: constr_name.clone(), variant_name: String::new(), }], tipo, assignment_properties.clone(), scope.to_owned(), ); } (false, constr_name) } a @ Pattern::Tuple { .. } => { let id = self.id_gen.next(); let tuple_name = format!("__tuple_name_{id}"); if matches!(assignment_properties.kind, AssignmentKind::Expect) && assignment_properties.value_type.is_data() && !tipo.is_data() { self.recursive_assert_pattern( a, nested_pattern, &mut vec![Air::Var { scope: scope.to_owned(), constructor: ValueConstructor::public( tipo.clone().into(), ValueConstructorVariant::LocalVariable { location: Span::empty(), }, ), name: tuple_name.clone(), variant_name: String::new(), }], tipo, assignment_properties.clone(), scope.to_owned(), ); } else { self.pattern_ir( a, nested_pattern, &mut vec![Air::Var { scope: scope.to_owned(), constructor: ValueConstructor::public( tipo.clone().into(), ValueConstructorVariant::LocalVariable { location: Span::empty(), }, ), name: tuple_name.clone(), variant_name: String::new(), }], tipo, assignment_properties.clone(), scope.to_owned(), ); } (false, tuple_name) } Pattern::Int { .. } => todo!(), Pattern::Assign { .. } => todo!(), }; if discard { None } else { Some((var_name, field_index)) } } } fn define_ir(&mut self, ir_stack: &mut Vec) { let mut func_components = IndexMap::new(); let mut func_index_map = IndexMap::new(); let recursion_func_map = IndexMap::new(); self.define_ir_recurse( ir_stack, &mut func_components, &mut func_index_map, recursion_func_map, false, ); let mut final_func_dep_ir = IndexMap::new(); let mut to_be_defined = IndexMap::new(); let mut dependency_map = IndexMap::new(); let mut dependency_vec = vec![]; let mut func_keys = func_components .clone() .into_iter() .filter(|(_, val)| !val.defined_by_zero_arg) .map(|(key, val)| (key, val.defined_by_zero_arg)) .collect_vec(); // deal with function dependencies by sorting order in which we iter over them. while let Some(function) = func_keys.pop() { let funct_comp = func_components.get(&function.0).unwrap(); if dependency_map.contains_key(&function.0) { dependency_map.shift_remove(&function.0); } dependency_map.insert(function.0, function.1); func_keys.extend( funct_comp .dependencies .iter() .map(|key| { ( key.clone(), func_components.get(key).unwrap().defined_by_zero_arg, ) }) .collect_vec(), ); } dependency_vec.extend( dependency_map .iter() .filter(|(_, defined_in_zero_arg)| !**defined_in_zero_arg) .map(|(key, _)| key.clone()) .collect_vec(), ); for func in dependency_vec { if self.defined_functions.contains_key(&func) { continue; } let funt_comp = func_components.get(&func).unwrap(); let func_scope = func_index_map.get(&func).unwrap(); let mut dep_ir = vec![]; if !funt_comp.args.is_empty() { // deal with function dependencies handle_func_dependencies_ir( &mut dep_ir, funt_comp, &func_components, &mut self.defined_functions, &func_index_map, func_scope, &mut to_be_defined, ); final_func_dep_ir.insert(func, dep_ir); } else { // since zero arg functions are run at compile time we need to pull all deps // note anon functions are not included in the above. They exist in a function anyway let mut defined_functions = IndexMap::new(); // deal with function dependencies in zero arg functions handle_func_dependencies_ir( &mut dep_ir, funt_comp, &func_components, &mut defined_functions, &func_index_map, func_scope, &mut to_be_defined, ); let mut final_zero_arg_ir = dep_ir; final_zero_arg_ir.extend(funt_comp.ir.clone()); self.convert_opaque_type_to_inner_ir(&mut final_zero_arg_ir); self.zero_arg_functions.insert(func, final_zero_arg_ir); // zero arg functions don't contain the dependencies since they are pre-evaluated // As such we add functions to defined only after dependencies for all other functions are calculated } } while let Some(func) = to_be_defined.pop() { let mut dep_ir = vec![]; let mut defined_functions = IndexMap::new(); // deal with function dependencies in zero arg functions let funt_comp = func_components.get(&func.0).unwrap(); let func_scope = func_index_map.get(&func.0).unwrap(); handle_func_dependencies_ir( &mut dep_ir, funt_comp, &func_components, &mut defined_functions, &func_index_map, func_scope, &mut to_be_defined, ); let mut final_zero_arg_ir = dep_ir; final_zero_arg_ir.extend(funt_comp.ir.clone()); self.convert_opaque_type_to_inner_ir(&mut final_zero_arg_ir); self.zero_arg_functions.insert(func.0, final_zero_arg_ir); } for (index, ir) in ir_stack.clone().into_iter().enumerate().rev() { { let temp_func_index_map = func_index_map.clone(); let to_insert = temp_func_index_map .into_iter() .filter(|func| { get_common_ancestor(&func.1, &ir.scope()) == ir.scope() && !self.defined_functions.contains_key(&func.0) && !self.zero_arg_functions.contains_key(&func.0) && !(*dependency_map.get(&func.0).unwrap()) }) .collect_vec(); for (function_access_key, scopes) in to_insert.into_iter() { func_index_map.remove(&function_access_key); self.defined_functions .insert(function_access_key.clone(), ()); let mut full_func_ir = final_func_dep_ir.get(&function_access_key).unwrap().clone(); let func_comp = func_components.get(&function_access_key).unwrap().clone(); // zero arg functions are not recursive if !func_comp.args.is_empty() { let mut recursion_ir = vec![]; handle_recursion_ir(&function_access_key, &func_comp, &mut recursion_ir); full_func_ir.push(Air::DefineFunc { scope: scopes.clone(), func_name: function_access_key.function_name.clone(), module_name: function_access_key.module_name.clone(), params: func_comp.args.clone(), recursive: func_comp.recursive, variant_name: function_access_key.variant_name.clone(), }); full_func_ir.extend(recursion_ir); for ir in full_func_ir.into_iter().rev() { ir_stack.insert(index, ir); } } else { full_func_ir.extend(func_comp.ir.clone()); self.zero_arg_functions .insert(function_access_key, full_func_ir); } } } } } fn define_ir_recurse( &mut self, ir_stack: &mut [Air], func_components: &mut IndexMap, func_index_map: &mut IndexMap>, mut recursion_func_map: IndexMap, in_zero_arg_func: bool, ) { self.define_ir_processor(ir_stack, func_components, func_index_map, in_zero_arg_func); let mut recursion_func_map_to_add = recursion_func_map.clone(); for func_index in func_index_map.clone().iter() { let func = func_index.0; let function_components = func_components.get_mut(func).unwrap(); let mut function_ir = function_components.ir.clone(); let in_zero_arg = function_components.args.is_empty() || in_zero_arg_func; let mut skip = false; for ir in function_ir.clone() { if let Air::Var { constructor: ValueConstructor { variant: ValueConstructorVariant::ModuleFn { name: func_name, module, .. }, .. }, variant_name, .. } = ir { if recursion_func_map.contains_key(&FunctionAccessKey { module_name: module.clone(), function_name: func_name.clone(), variant_name: variant_name.clone(), }) && func.clone() == (FunctionAccessKey { module_name: module.clone(), function_name: func_name.clone(), variant_name: variant_name.clone(), }) { skip = true; } else if func.clone() == (FunctionAccessKey { module_name: module.clone(), function_name: func_name.clone(), variant_name: variant_name.clone(), }) { recursion_func_map_to_add.insert( FunctionAccessKey { module_name: module.clone(), function_name: func_name.clone(), variant_name: variant_name.clone(), }, (), ); } } } recursion_func_map = recursion_func_map_to_add.clone(); if !skip { let mut inner_func_components = IndexMap::new(); let mut inner_func_index_map = IndexMap::new(); self.define_ir_recurse( &mut function_ir, &mut inner_func_components, &mut inner_func_index_map, recursion_func_map.clone(), in_zero_arg, ); function_components.ir = function_ir; //now unify for item in inner_func_components { if let Some(entry) = func_components.get_mut(&item.0) { entry.defined_by_zero_arg = entry.defined_by_zero_arg && item.1.defined_by_zero_arg } else { func_components.insert(item.0, item.1); } } for item in inner_func_index_map { if let Some(entry) = func_index_map.get_mut(&item.0) { *entry = get_common_ancestor(entry, &item.1); } else { func_index_map.insert(item.0, item.1); } } } } } fn define_ir_processor( &mut self, ir_stack: &mut [Air], func_components: &mut IndexMap, func_index_map: &mut IndexMap>, in_zero_arg_func: bool, ) { let mut to_be_defined_map: IndexMap> = IndexMap::new(); for (index, ir) in ir_stack.to_vec().iter().enumerate().rev() { match ir { Air::Var { scope, constructor, .. } => { if let ValueConstructorVariant::ModuleFn { name, module, builtin, .. } = &constructor.variant { if builtin.is_none() { let non_variant_function_key = FunctionAccessKey { module_name: module.clone(), function_name: name.clone(), variant_name: String::new(), }; let function = *self.functions.get(&non_variant_function_key).unwrap(); let mut func_ir = vec![]; self.build_ir(&function.body, &mut func_ir, scope.to_vec()); let param_types = constructor.tipo.arg_types().unwrap(); let mut generics_type_map: IndexMap> = IndexMap::new(); for (index, arg) in function.arguments.iter().enumerate() { if arg.tipo.is_generic() { let mut map = generics_type_map.into_iter().collect_vec(); map.append(&mut get_generics_and_type( &arg.tipo, ¶m_types[index], )); generics_type_map = map.into_iter().collect(); } } let (variant_name, func_ir) = monomorphize(func_ir, generics_type_map, &constructor.tipo); let function_key = FunctionAccessKey { module_name: module.clone(), function_name: non_variant_function_key.function_name, variant_name: variant_name.clone(), }; ir_stack[index] = Air::Var { scope: scope.clone(), constructor: constructor.clone(), name: name.clone(), variant_name: variant_name.clone(), }; if let Some(scope_prev) = to_be_defined_map.get(&function_key) { let new_scope = get_common_ancestor(scope, scope_prev); to_be_defined_map.insert(function_key, new_scope); } else if func_components.get(&function_key).is_some() { to_be_defined_map.insert(function_key.clone(), scope.to_vec()); } else { to_be_defined_map.insert(function_key.clone(), scope.to_vec()); let mut func_calls = IndexMap::new(); for ir in func_ir.clone().into_iter() { if let Air::Var { constructor: ValueConstructor { variant: ValueConstructorVariant::ModuleFn { name: func_name, module, .. }, tipo, .. }, .. } = ir { let current_func = FunctionAccessKey { module_name: module.clone(), function_name: func_name.clone(), variant_name: String::new(), }; let current_func_as_variant = FunctionAccessKey { module_name: module.clone(), function_name: func_name.clone(), variant_name: variant_name.clone(), }; let function = self.functions.get(¤t_func); if function_key.clone() == current_func_as_variant { func_calls.insert(current_func_as_variant, ()); } else if let (Some(function), Type::Fn { .. }) = (function, &*tipo) { let mut generics_type_map: IndexMap> = IndexMap::new(); let param_types = tipo.arg_types().unwrap(); for (index, arg) in function.arguments.iter().enumerate() { if arg.tipo.is_generic() { let mut map = generics_type_map.into_iter().collect_vec(); map.append(&mut get_generics_and_type( &arg.tipo, ¶m_types[index], )); generics_type_map = map.into_iter().collect(); } } let mut func_ir = vec![]; self.build_ir( &function.body, &mut func_ir, scope.to_vec(), ); let (variant_name, _) = monomorphize(func_ir, generics_type_map, &tipo); func_calls.insert( FunctionAccessKey { module_name: current_func.module_name, function_name: current_func.function_name, variant_name, }, (), ); } else { func_calls.insert(current_func, ()); } } } let mut args = vec![]; for arg in function.arguments.iter() { match &arg.arg_name { ArgName::Named { name, .. } => { args.push(name.clone()); } _ => { args.push("_".to_string()); } } } let recursive = if func_calls.get(&function_key).is_some() { func_calls.remove(&function_key); true } else { false }; func_components.insert( function_key, FuncComponents { ir: func_ir, dependencies: func_calls.keys().cloned().collect_vec(), recursive, args, defined_by_zero_arg: in_zero_arg_func, }, ); } } } } a => { let scope = a.scope(); for func in to_be_defined_map.clone().iter() { if get_common_ancestor(&scope, func.1) == scope.to_vec() { if let Some(index_scope) = func_index_map.get(func.0) { if get_common_ancestor(index_scope, func.1) == scope.to_vec() { func_index_map.insert(func.0.clone(), scope.clone()); to_be_defined_map.shift_remove(func.0); } else { to_be_defined_map.insert( func.0.clone(), get_common_ancestor(index_scope, func.1), ); } } else { func_index_map.insert(func.0.clone(), scope.clone()); to_be_defined_map.shift_remove(func.0); } } } } } } // Still to be defined for func in to_be_defined_map.clone().iter() { let index_scope = func_index_map.get(func.0).unwrap(); func_index_map.insert(func.0.clone(), get_common_ancestor(func.1, index_scope)); } } fn convert_opaque_type_to_inner_ir(&mut self, ir_stack: &mut Vec) { let mut indices_to_remove = vec![]; for (index, ir) in ir_stack.clone().into_iter().enumerate() { match ir { Air::Var { scope, constructor, name, variant_name, } => { let mut replaced_type = constructor.tipo.clone(); replace_opaque_type(&mut replaced_type, self.data_types.clone()); ir_stack[index] = Air::Var { scope, constructor: ValueConstructor { public: constructor.public, variant: constructor.variant, tipo: replaced_type, }, name, variant_name, }; } Air::List { tipo, scope, count, tail, } => { let mut replaced_type = tipo.clone(); replace_opaque_type(&mut replaced_type, self.data_types.clone()); ir_stack[index] = Air::List { scope, tipo: replaced_type, count, tail, }; } Air::ListAccessor { tipo, scope, names, tail, check_last_item, } => { let mut replaced_type = tipo.clone(); replace_opaque_type(&mut replaced_type, self.data_types.clone()); ir_stack[index] = Air::ListAccessor { scope, tipo: replaced_type, names, tail, check_last_item, }; } Air::ListExpose { tipo, scope, tail_head_names, tail, } => { let mut replaced_type = tipo.clone(); replace_opaque_type(&mut replaced_type, self.data_types.clone()); ir_stack[index] = Air::ListExpose { scope, tipo: replaced_type, tail_head_names, tail, }; } Air::Builtin { tipo, scope, func } => { let mut replaced_type = tipo.clone(); replace_opaque_type(&mut replaced_type, self.data_types.clone()); ir_stack[index] = Air::Builtin { scope, func, tipo: replaced_type, }; } Air::BinOp { tipo, scope, name, count, } => { let mut replaced_type = tipo.clone(); replace_opaque_type(&mut replaced_type, self.data_types.clone()); ir_stack[index] = Air::BinOp { scope, name, count, tipo: replaced_type, }; } Air::When { tipo, scope, subject_name, } => { let mut replaced_type = tipo.clone(); replace_opaque_type(&mut replaced_type, self.data_types.clone()); ir_stack[index] = Air::When { scope, tipo: replaced_type, subject_name, }; } Air::Clause { tipo, scope, subject_name, complex_clause, } => { let mut replaced_type = tipo.clone(); replace_opaque_type(&mut replaced_type, self.data_types.clone()); ir_stack[index] = Air::Clause { scope, tipo: replaced_type, subject_name, complex_clause, }; } Air::ListClause { tipo, scope, tail_name, next_tail_name, complex_clause, } => { let mut replaced_type = tipo.clone(); replace_opaque_type(&mut replaced_type, self.data_types.clone()); ir_stack[index] = Air::ListClause { scope, tipo: replaced_type, tail_name, next_tail_name, complex_clause, }; } Air::TupleClause { tipo, scope, indices, predefined_indices, subject_name, count, complex_clause, } => { let mut replaced_type = tipo.clone(); replace_opaque_type(&mut replaced_type, self.data_types.clone()); ir_stack[index] = Air::TupleClause { scope, tipo: replaced_type, indices, predefined_indices, subject_name, count, complex_clause, }; } Air::ClauseGuard { tipo, scope, subject_name, } => { let mut replaced_type = tipo.clone(); replace_opaque_type(&mut replaced_type, self.data_types.clone()); ir_stack[index] = Air::ClauseGuard { scope, subject_name, tipo: replaced_type, }; } Air::ListClauseGuard { tipo, scope, tail_name, next_tail_name, inverse, } => { let mut replaced_type = tipo.clone(); replace_opaque_type(&mut replaced_type, self.data_types.clone()); ir_stack[index] = Air::ListClauseGuard { scope, tipo: replaced_type, tail_name, next_tail_name, inverse, }; } Air::Tuple { tipo, scope, count } => { let mut replaced_type = tipo.clone(); replace_opaque_type(&mut replaced_type, self.data_types.clone()); ir_stack[index] = Air::Tuple { scope, tipo: replaced_type, count, }; } Air::TupleIndex { tipo, scope, tuple_index, } => { let mut replaced_type = tipo.clone(); replace_opaque_type(&mut replaced_type, self.data_types.clone()); ir_stack[index] = Air::TupleIndex { scope, tipo: replaced_type, tuple_index, }; } Air::Todo { tipo, scope, label } => { let mut replaced_type = tipo.clone(); replace_opaque_type(&mut replaced_type, self.data_types.clone()); ir_stack[index] = Air::Todo { scope, label, tipo: replaced_type, }; } Air::ErrorTerm { tipo, scope, label } => { let mut replaced_type = tipo.clone(); replace_opaque_type(&mut replaced_type, self.data_types.clone()); ir_stack[index] = Air::ErrorTerm { scope, tipo: replaced_type, label, }; } Air::Trace { tipo, scope } => { let mut replaced_type = tipo.clone(); replace_opaque_type(&mut replaced_type, self.data_types.clone()); ir_stack[index] = Air::Trace { scope, tipo: replaced_type, }; } Air::TupleAccessor { tipo, scope, names, check_last_item, } => { let mut replaced_type = tipo.clone(); replace_opaque_type(&mut replaced_type, self.data_types.clone()); ir_stack[index] = Air::TupleAccessor { scope, names, tipo: replaced_type, check_last_item, }; } Air::RecordUpdate { highest_index, indices, scope, tipo, } => { let mut new_indices = vec![]; for (ind, tipo) in indices { let mut replaced_type = tipo.clone(); replace_opaque_type(&mut replaced_type, self.data_types.clone()); new_indices.push((ind, replaced_type)); } ir_stack[index] = Air::RecordUpdate { scope, indices: new_indices, highest_index, tipo, }; } Air::Record { constr_index, tipo, count, scope, } => { if check_replaceable_opaque_type(&tipo, &self.data_types) { indices_to_remove.push(index); } else { let mut replaced_type = tipo.clone(); replace_opaque_type(&mut replaced_type, self.data_types.clone()); ir_stack[index] = Air::Record { scope, constr_index, tipo: replaced_type, count, }; } } Air::RecordAccess { record_index, tipo, scope, } => { let record = ir_stack[index + 1].clone(); let record_type = record.tipo(); if let Some(record_type) = record_type { if check_replaceable_opaque_type(&record_type, &self.data_types) { indices_to_remove.push(index); } else { let mut replaced_type = tipo.clone(); replace_opaque_type(&mut replaced_type, self.data_types.clone()); ir_stack[index] = Air::RecordAccess { scope, record_index, tipo: replaced_type, }; } } else { let mut replaced_type = tipo.clone(); replace_opaque_type(&mut replaced_type, self.data_types.clone()); ir_stack[index] = Air::RecordAccess { scope, record_index, tipo: replaced_type, }; } } Air::FieldsExpose { indices, scope, check_last_item, } => { let record = ir_stack[index + 1].clone(); let record_type = record.tipo(); if let Some(record_type) = record_type { if check_replaceable_opaque_type(&record_type, &self.data_types) { ir_stack[index] = Air::Let { scope, name: indices[0].1.clone(), }; } else { let mut new_indices = vec![]; for (ind, name, tipo) in indices { let mut replaced_type = tipo.clone(); replace_opaque_type(&mut replaced_type, self.data_types.clone()); new_indices.push((ind, name, replaced_type)); } ir_stack[index] = Air::FieldsExpose { scope, indices: new_indices, check_last_item, }; } } else { let mut new_indices = vec![]; for (ind, name, tipo) in indices { let mut replaced_type = tipo.clone(); replace_opaque_type(&mut replaced_type, self.data_types.clone()); new_indices.push((ind, name, replaced_type)); } ir_stack[index] = Air::FieldsExpose { scope, indices: new_indices, check_last_item, }; } } Air::Call { scope, count, tipo } => { let mut replaced_type = tipo.clone(); replace_opaque_type(&mut replaced_type, self.data_types.clone()); ir_stack[index] = Air::Call { scope, tipo: replaced_type, count, }; } Air::If { scope, tipo } => { let mut replaced_type = tipo.clone(); replace_opaque_type(&mut replaced_type, self.data_types.clone()); ir_stack[index] = Air::If { scope, tipo: replaced_type, }; } Air::UnWrapData { scope, tipo } => { let mut replaced_type = tipo.clone(); replace_opaque_type(&mut replaced_type, self.data_types.clone()); ir_stack[index] = Air::UnWrapData { scope, tipo: replaced_type, }; } Air::WrapData { scope, tipo } => { let mut replaced_type = tipo.clone(); replace_opaque_type(&mut replaced_type, self.data_types.clone()); ir_stack[index] = Air::WrapData { scope, tipo: replaced_type, }; } _ => {} } } for index in indices_to_remove.into_iter().rev() { ir_stack.remove(index); } } fn uplc_code_gen(&mut self, ir_stack: &mut 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[0].clone() } fn gen_uplc(&mut self, ir: Air, arg_stack: &mut Vec>) { match ir { Air::Int { value, .. } => { let integer = value.parse().unwrap(); let term = Term::Constant(UplcConstant::Integer(integer).into()); arg_stack.push(term); } Air::String { value, .. } => { let term = Term::Constant(UplcConstant::String(value).into()); arg_stack.push(term); } Air::ByteArray { bytes, .. } => { let term = Term::Constant(UplcConstant::ByteString(bytes).into()); arg_stack.push(term); } Air::Bool { value, .. } => { let term = Term::Constant(UplcConstant::Bool(value).into()); arg_stack.push(term); } 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 { 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::Constant( UplcConstant::Bool(constr_name == "True").into(), )); } else if constructor.tipo.is_void() { arg_stack.push(Term::Constant(UplcConstant::Unit.into())); } else { let data_type = lookup_data_type_by_tipo( self.data_types.clone(), &constructor.tipo, ) .unwrap(); let (constr_index, _) = data_type .constructors .iter() .enumerate() .find(|(_, x)| x.name == *constr_name) .unwrap(); let fields = Term::Constant( UplcConstant::ProtoList(UplcType::Data, vec![]).into(), ); let term = apply_wrap( apply_wrap( Term::Builtin(DefaultFunction::ConstrData), Term::Constant( UplcConstant::Integer(constr_index.try_into().unwrap()) .into(), ), ), fields, ); 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()) } } let list_type = tipo.get_inner_types()[0].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 = convert_constants_to_data(convert_keys); let convert_values = 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, convert_constants_to_data(constants), ) .into(), ) }; arg_stack.push(list); } else { let mut term = if tail { arg_stack.pop().unwrap() } else if tipo.is_map() { Term::Constant( UplcConstant::ProtoList( UplcType::Pair(UplcType::Data.into(), UplcType::Data.into()), vec![], ) .into(), ) } else { Term::Constant(UplcConstant::ProtoList(UplcType::Data, vec![]).into()) }; for arg in args.into_iter().rev() { let list_item = if tipo.is_map() { arg } else { convert_type_to_data(arg, &list_type) }; term = apply_wrap( apply_wrap( Term::Builtin(DefaultFunction::MkCons).force_wrap(), list_item, ), term, ); } arg_stack.push(term); } } Air::ListAccessor { names, tail, 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(); term = apply_wrap( list_access_to_uplc( &names, &id_list, tail, 0, term, inner_types, check_last_item, true, ), value, ); arg_stack.push(term); } Air::ListExpose { tail_head_names, tail, tipo, .. } => { let mut term = arg_stack.pop().unwrap(); if let Some((tail_var, tail_name)) = tail { term = apply_wrap( Term::Lambda { parameter_name: Name { text: tail_name, unique: 0.into(), } .into(), body: term.into(), }, apply_wrap( Term::Builtin(DefaultFunction::TailList).force_wrap(), Term::Var( Name { text: tail_var, unique: 0.into(), } .into(), ), ), ); } for (tail_var, head_name) in tail_head_names.into_iter().rev() { let head_list = if tipo.is_map() { apply_wrap( Term::Force(Term::Builtin(DefaultFunction::HeadList).into()), Term::Var( Name { text: tail_var, unique: 0.into(), } .into(), ), ) } else { convert_data_to_type( apply_wrap( Term::Builtin(DefaultFunction::HeadList).force_wrap(), Term::Var( Name { text: tail_var, unique: 0.into(), } .into(), ), ), &tipo.get_inner_types()[0], ) }; term = apply_wrap( Term::Lambda { parameter_name: Name { text: head_name, unique: 0.into(), } .into(), body: term.into(), }, 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 { parameter_name: Name { text: param.clone(), unique: 0.into(), } .into(), body: term.into(), }; } 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 = apply_wrap(term, arg); } arg_stack.push(term); } else { let term = arg_stack.pop().unwrap(); let zero_arg_functions = self.zero_arg_functions.clone(); let mut anon_func = true; if let Term::Var(name) = term.clone() { let text = &name.text; for ( FunctionAccessKey { module_name, function_name, variant_name, }, ir, ) in zero_arg_functions.into_iter() { let name_module = format!("{module_name}_{function_name}{variant_name}"); let name = format!("{function_name}{variant_name}"); if text == &name || text == &name_module { let mut term = self.uplc_code_gen(&mut ir.clone()); term = builder::constr_get_field(term); term = builder::constr_fields_exposer(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()).0.unwrap(); arg_stack.push(evaluated_term.try_into().unwrap()); anon_func = false; } } } if anon_func { arg_stack.push(term); } } } Air::Builtin { func, tipo, .. } => match func { DefaultFunction::FstPair | DefaultFunction::SndPair | DefaultFunction::HeadList => { let id = self.id_gen.next(); let mut term: Term = func.into(); for _ in 0..func.force_count() { term = term.force_wrap(); } term = apply_wrap( term, Term::Var( Name { text: format!("__arg_{id}"), unique: 0.into(), } .into(), ), ); let inner_type = if matches!(func, DefaultFunction::SndPair) { tipo.get_inner_types()[0].get_inner_types()[1].clone() } else { tipo.get_inner_types()[0].get_inner_types()[0].clone() }; term = convert_data_to_type(term, &inner_type); term = Term::Lambda { parameter_name: Name { text: format!("__arg_{id}"), unique: 0.into(), } .into(), body: term.into(), }; arg_stack.push(term); } DefaultFunction::MkCons => todo!(), DefaultFunction::MkPairData => todo!(), _ => { let mut term = Term::Builtin(func); for _ in 0..func.force_count() { term = term.force_wrap(); } arg_stack.push(term); } }, Air::BinOp { name, tipo, .. } => { let left = arg_stack.pop().unwrap(); let right = arg_stack.pop().unwrap(); let default_builtin = if tipo.is_int() { DefaultFunction::EqualsInteger } else if tipo.is_string() { DefaultFunction::EqualsString } else if tipo.is_bytearray() { DefaultFunction::EqualsByteString } else { DefaultFunction::EqualsData }; let term = match name { BinOp::And => delayed_if_else( left, right, Term::Constant(UplcConstant::Bool(false).into()), ), BinOp::Or => delayed_if_else( left, Term::Constant(UplcConstant::Bool(true).into()), right, ), BinOp::Eq => { if tipo.is_bool() { let term = delayed_if_else( left, right.clone(), if_else( right, Term::Constant(UplcConstant::Bool(false).into()), Term::Constant(UplcConstant::Bool(true).into()), ), ); arg_stack.push(term); return; } else if tipo.is_map() { let term = apply_wrap( apply_wrap( default_builtin.into(), apply_wrap(DefaultFunction::MapData.into(), left), ), apply_wrap(DefaultFunction::MapData.into(), right), ); arg_stack.push(term); return; } else if tipo.is_tuple() && matches!(tipo.get_uplc_type(), UplcType::Pair(_, _)) { let term = apply_wrap( apply_wrap( default_builtin.into(), apply_wrap( DefaultFunction::MapData.into(), apply_wrap( apply_wrap( Term::Builtin(DefaultFunction::MkCons).force_wrap(), left, ), Term::Constant( UplcConstant::ProtoList( UplcType::Pair( UplcType::Data.into(), UplcType::Data.into(), ), vec![], ) .into(), ), ), ), ), apply_wrap( DefaultFunction::MapData.into(), apply_wrap( apply_wrap( Term::Builtin(DefaultFunction::MkCons).force_wrap(), right, ), Term::Constant( UplcConstant::ProtoList( UplcType::Pair( UplcType::Data.into(), UplcType::Data.into(), ), vec![], ) .into(), ), ), ), ); arg_stack.push(term); return; } else if tipo.is_list() || tipo.is_tuple() { let term = apply_wrap( apply_wrap( default_builtin.into(), apply_wrap(DefaultFunction::ListData.into(), left), ), apply_wrap(DefaultFunction::ListData.into(), right), ); arg_stack.push(term); return; } else if tipo.is_void() { arg_stack.push(Term::Constant(UplcConstant::Bool(true).into())); return; } apply_wrap(apply_wrap(default_builtin.into(), left), right) } BinOp::NotEq => { if tipo.is_bool() { let term = delayed_if_else( left, if_else( right.clone(), Term::Constant(UplcConstant::Bool(false).into()), Term::Constant(UplcConstant::Bool(true).into()), ), right, ); arg_stack.push(term); return; } else if tipo.is_map() { let term = if_else( apply_wrap( apply_wrap( default_builtin.into(), apply_wrap(DefaultFunction::MapData.into(), left), ), apply_wrap(DefaultFunction::MapData.into(), right), ), Term::Constant(UplcConstant::Bool(false).into()), Term::Constant(UplcConstant::Bool(true).into()), ); arg_stack.push(term); return; } else if tipo.is_tuple() && matches!(tipo.get_uplc_type(), UplcType::Pair(_, _)) { let mut term = apply_wrap( apply_wrap( default_builtin.into(), apply_wrap( DefaultFunction::MapData.into(), apply_wrap( apply_wrap( Term::Builtin(DefaultFunction::MkCons).force_wrap(), left, ), Term::Constant( UplcConstant::ProtoList( UplcType::Pair( UplcType::Data.into(), UplcType::Data.into(), ), vec![], ) .into(), ), ), ), ), apply_wrap( DefaultFunction::MapData.into(), apply_wrap( apply_wrap( Term::Builtin(DefaultFunction::MkCons).force_wrap(), right, ), Term::Constant( UplcConstant::ProtoList( UplcType::Pair( UplcType::Data.into(), UplcType::Data.into(), ), vec![], ) .into(), ), ), ), ); term = if_else( term, Term::Constant(UplcConstant::Bool(false).into()), Term::Constant(UplcConstant::Bool(true).into()), ); arg_stack.push(term); return; } else if tipo.is_list() || tipo.is_tuple() { let term = if_else( apply_wrap( apply_wrap( default_builtin.into(), apply_wrap(DefaultFunction::ListData.into(), left), ), apply_wrap(DefaultFunction::ListData.into(), right), ), Term::Constant(UplcConstant::Bool(false).into()), Term::Constant(UplcConstant::Bool(true).into()), ); arg_stack.push(term); return; } else if tipo.is_void() { arg_stack.push(Term::Constant(UplcConstant::Bool(false).into())); return; } if_else( apply_wrap(apply_wrap(default_builtin.into(), left), right), Term::Constant(UplcConstant::Bool(false).into()), Term::Constant(UplcConstant::Bool(true).into()), ) } BinOp::LtInt => apply_wrap( apply_wrap(DefaultFunction::LessThanInteger.into(), left), right, ), BinOp::LtEqInt => apply_wrap( apply_wrap(DefaultFunction::LessThanEqualsInteger.into(), left), right, ), BinOp::GtEqInt => apply_wrap( apply_wrap(DefaultFunction::LessThanEqualsInteger.into(), right), left, ), BinOp::GtInt => apply_wrap( apply_wrap(DefaultFunction::LessThanInteger.into(), right), left, ), BinOp::AddInt => { apply_wrap(apply_wrap(DefaultFunction::AddInteger.into(), left), right) } BinOp::SubInt => apply_wrap( apply_wrap(DefaultFunction::SubtractInteger.into(), left), right, ), BinOp::MultInt => apply_wrap( apply_wrap(DefaultFunction::MultiplyInteger.into(), left), right, ), BinOp::DivInt => apply_wrap( apply_wrap(DefaultFunction::DivideInteger.into(), left), right, ), BinOp::ModInt => { apply_wrap(apply_wrap(DefaultFunction::ModInteger.into(), left), 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 = Term::Lambda { parameter_name: Name { text: param.clone(), unique: 0.into(), } .into(), body: func_body.into(), }; } if !recursive { term = apply_wrap( Term::Lambda { parameter_name: Name { text: func_name, unique: 0.into(), } .into(), body: term.into(), }, func_body, ); arg_stack.push(term); } else { func_body = Term::Lambda { parameter_name: Name { text: func_name.clone(), unique: 0.into(), } .into(), body: func_body.into(), }; term = apply_wrap( Term::Lambda { parameter_name: Name { text: func_name.clone(), unique: 0.into(), } .into(), body: apply_wrap( Term::Lambda { parameter_name: Name { text: func_name.clone(), unique: 0.into(), } .into(), body: term.into(), }, apply_wrap( Term::Var( Name { text: func_name.clone(), unique: 0.into(), } .into(), ), Term::Var( Name { text: func_name, unique: 0.into(), } .into(), ), ), ) .into(), }, func_body, ); arg_stack.push(term); } } Air::Let { name, .. } => { let arg = arg_stack.pop().unwrap(); let mut term = arg_stack.pop().unwrap(); term = apply_wrap( Term::Lambda { parameter_name: Name { text: name, unique: 0.into(), } .into(), body: term.into(), }, arg, ); arg_stack.push(term); } Air::UnWrapData { tipo, .. } => { let mut term = arg_stack.pop().unwrap(); term = convert_data_to_type(term, &tipo); arg_stack.push(term); } Air::WrapData { tipo, .. } => { let mut term = arg_stack.pop().unwrap(); term = convert_type_to_data(term, &tipo); arg_stack.push(term); } Air::AssertConstr { constr_index, .. } => { let constr = arg_stack.pop().unwrap(); let mut term = arg_stack.pop().unwrap(); let error_term = apply_wrap( apply_wrap( Term::Builtin(DefaultFunction::Trace).force_wrap(), Term::Constant( UplcConstant::String( "Asserted on incorrect constructor variant.".to_string(), ) .into(), ), ), Term::Delay(Term::Error.into()), ) .force_wrap(); term = delayed_if_else( apply_wrap( apply_wrap( DefaultFunction::EqualsInteger.into(), Term::Constant(UplcConstant::Integer(constr_index.into()).into()), ), constr_index_exposer(constr), ), term, error_term, ); arg_stack.push(term); } Air::When { subject_name, tipo, .. } => { let subject = arg_stack.pop().unwrap(); let mut term = arg_stack.pop().unwrap(); term = if tipo.is_int() || tipo.is_bytearray() || tipo.is_string() || tipo.is_list() || tipo.is_tuple() || tipo.is_bool() { apply_wrap( Term::Lambda { parameter_name: Name { text: subject_name, unique: 0.into(), } .into(), body: term.into(), }, subject, ) } else { apply_wrap( Term::Lambda { parameter_name: Name { text: subject_name, unique: 0.into(), } .into(), body: term.into(), }, constr_index_exposer(subject), ) }; arg_stack.push(term); } Air::Clause { tipo, subject_name, complex_clause, .. } => { // clause to compare let clause = arg_stack.pop().unwrap(); // the body to be run if the clause matches let body = arg_stack.pop().unwrap(); // the next branch in the when expression let mut term = arg_stack.pop().unwrap(); if tipo.is_bool() { if complex_clause { let other_clauses = term; if matches!(clause, Term::Constant(boolean ) if matches!(boolean.as_ref(), UplcConstant::Bool(true))) { term = if_else( Term::Var( Name { text: subject_name, unique: 0.into(), } .into(), ), Term::Delay(body.into()), Term::Var( Name { text: "__other_clauses_delayed".to_string(), unique: 0.into(), } .into(), ), ) .force_wrap(); } else { term = if_else( Term::Var( Name { text: subject_name, unique: 0.into(), } .into(), ), Term::Var( Name { text: "__other_clauses_delayed".to_string(), unique: 0.into(), } .into(), ), Term::Delay(body.into()), ) .force_wrap(); } term = apply_wrap( Term::Lambda { parameter_name: Name { text: "__other_clauses_delayed".to_string(), unique: 0.into(), } .into(), body: term.into(), }, Term::Delay(other_clauses.into()), ); } else if matches!(clause, Term::Constant(boolean) if matches!(boolean.as_ref(), UplcConstant::Bool(true))) { term = delayed_if_else( Term::Var( Name { text: subject_name, unique: 0.into(), } .into(), ), body, term, ); } else { term = delayed_if_else( Term::Var( Name { text: subject_name, unique: 0.into(), } .into(), ), term, body, ); } } else { let checker = if tipo.is_int() { apply_wrap( DefaultFunction::EqualsInteger.into(), Term::Var( Name { text: subject_name, unique: 0.into(), } .into(), ), ) } else if tipo.is_bytearray() { apply_wrap( DefaultFunction::EqualsByteString.into(), Term::Var( Name { text: subject_name, unique: 0.into(), } .into(), ), ) } else if tipo.is_string() { apply_wrap( DefaultFunction::EqualsString.into(), Term::Var( Name { text: subject_name, unique: 0.into(), } .into(), ), ) } else if tipo.is_list() || tipo.is_tuple() { unreachable!("{:#?}", tipo) } else { apply_wrap( DefaultFunction::EqualsInteger.into(), Term::Var( Name { text: subject_name, unique: 0.into(), } .into(), ), ) }; if complex_clause { term = apply_wrap( Term::Lambda { parameter_name: Name { text: "__other_clauses_delayed".to_string(), unique: 0.into(), } .into(), body: if_else( apply_wrap(checker, clause), Term::Delay(body.into()), Term::Var( Name { text: "__other_clauses_delayed".to_string(), unique: 0.into(), } .into(), ), ) .force_wrap() .into(), }, Term::Delay(term.into()), ); } else { term = delayed_if_else(apply_wrap(checker, clause), body, term); } } arg_stack.push(term); } Air::ListClause { tail_name, next_tail_name, complex_clause, .. } => { // discard to pop off let _ = arg_stack.pop().unwrap(); let body = arg_stack.pop().unwrap(); let mut term = arg_stack.pop().unwrap(); let arg = if let Some(next_tail_name) = next_tail_name { apply_wrap( Term::Lambda { parameter_name: Name { text: next_tail_name, unique: 0.into(), } .into(), body: term.into(), }, apply_wrap( Term::Builtin(DefaultFunction::TailList).force_wrap(), Term::Var( Name { text: tail_name.clone(), unique: 0.into(), } .into(), ), ), ) } else { term }; if complex_clause { term = choose_list( Term::Var( Name { text: tail_name, unique: 0.into(), } .into(), ), Term::Delay(body.into()), Term::Var( Name { text: "__other_clauses_delayed".to_string(), unique: 0.into(), } .into(), ), ) .force_wrap(); term = apply_wrap( Term::Lambda { parameter_name: Name { text: "__other_clauses_delayed".into(), unique: 0.into(), } .into(), body: term.into(), }, Term::Delay(arg.into()), ); } else { term = delayed_choose_list( Term::Var( Name { text: tail_name, unique: 0.into(), } .into(), ), body, arg, ); } arg_stack.push(term); } Air::WrapClause { .. } => { let _ = arg_stack.pop().unwrap(); let mut term = arg_stack.pop().unwrap(); let arg = arg_stack.pop().unwrap(); term = apply_wrap( Term::Lambda { parameter_name: Name { text: "__other_clauses_delayed".into(), unique: 0.into(), } .into(), body: term.into(), }, Term::Delay(arg.into()), ); arg_stack.push(term); } Air::ClauseGuard { subject_name, tipo, .. } => { let condition = arg_stack.pop().unwrap(); let then = arg_stack.pop().unwrap(); if tipo.is_bool() { let mut term = Term::Var( Name { text: "__other_clauses_delayed".to_string(), unique: 0.into(), } .into(), ); if matches!(condition, Term::Constant(boolean) if matches!(boolean.as_ref(), UplcConstant::Bool(true))) { term = if_else( Term::Var( Name { text: subject_name, unique: 0.into(), } .into(), ), Term::Delay(then.into()), term, ) .force_wrap(); } else { term = if_else( Term::Var( Name { text: subject_name, unique: 0.into(), } .into(), ), term, Term::Delay(then.into()), ) .force_wrap(); } arg_stack.push(term); } else { let checker = if tipo.is_int() { apply_wrap( DefaultFunction::EqualsInteger.into(), Term::Var( Name { text: subject_name, unique: 0.into(), } .into(), ), ) } else if tipo.is_bytearray() { apply_wrap( DefaultFunction::EqualsByteString.into(), Term::Var( Name { text: subject_name, unique: 0.into(), } .into(), ), ) } else if tipo.is_string() { apply_wrap( DefaultFunction::EqualsString.into(), Term::Var( Name { text: subject_name, unique: 0.into(), } .into(), ), ) } else if tipo.is_list() || tipo.is_tuple() { unreachable!() } else { apply_wrap( DefaultFunction::EqualsInteger.into(), constr_index_exposer(Term::Var( Name { text: subject_name, unique: 0.into(), } .into(), )), ) }; let term = if_else( apply_wrap(checker, condition), Term::Delay(then.into()), Term::Var( Name { text: "__other_clauses_delayed".to_string(), unique: 0.into(), } .into(), ), ) .force_wrap(); arg_stack.push(term); } } Air::ListClauseGuard { tail_name, next_tail_name, inverse, .. } => { // discard to pop off let _ = arg_stack.pop().unwrap(); // 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 { apply_wrap( Term::Lambda { parameter_name: Name { text: next_tail_name, unique: 0.into(), } .into(), body: term.into(), }, apply_wrap( Term::Builtin(DefaultFunction::TailList).force_wrap(), Term::Var( Name { text: tail_name.clone(), unique: 0.into(), } .into(), ), ), ) } else { term }; if !inverse { term = choose_list( Term::Var( Name { text: tail_name, unique: 0.into(), } .into(), ), Term::Delay(term.into()), Term::Var( Name { text: "__other_clauses_delayed".to_string(), unique: 0.into(), } .into(), ), ) .force_wrap(); } else { term = choose_list( Term::Var( Name { text: tail_name, unique: 0.into(), } .into(), ), Term::Var( Name { text: "__other_clauses_delayed".to_string(), unique: 0.into(), } .into(), ), Term::Delay(term.into()), ) .force_wrap(); } 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 = delayed_if_else(condition, then, term); arg_stack.push(term); } Air::Record { constr_index, tipo, count, .. } => { let mut arg_vec = vec![]; for _ in 0..count { arg_vec.push(arg_stack.pop().unwrap()); } let mut term = Term::Constant(UplcConstant::ProtoList(UplcType::Data, vec![]).into()); for (index, arg) in arg_vec.iter().enumerate().rev() { term = apply_wrap( apply_wrap( Term::Builtin(DefaultFunction::MkCons).force_wrap(), convert_type_to_data(arg.clone(), &tipo.arg_types().unwrap()[index]), ), term, ); } term = apply_wrap( apply_wrap( DefaultFunction::ConstrData.into(), Term::Constant(UplcConstant::Integer(constr_index.into()).into()), ), 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()).0.unwrap(); term = evaluated_term.try_into().unwrap(); } arg_stack.push(term); } Air::RecordAccess { record_index, tipo, .. } => { let constr = arg_stack.pop().unwrap(); let mut term = apply_wrap( apply_wrap( Term::Var( Name { text: CONSTR_GET_FIELD.to_string(), unique: 0.into(), } .into(), ), apply_wrap( Term::Var( Name { text: CONSTR_FIELDS_EXPOSER.to_string(), unique: 0.into(), } .into(), ), constr, ), ), Term::Constant(UplcConstant::Integer(record_index.into()).into()), ); term = 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(); term = if !indices.is_empty() { list_access_to_uplc( &names, &id_list, false, current_index, term, inner_types, check_last_item, false, ) } else { term }; term = apply_wrap( term, apply_wrap( Term::Var( Name { text: CONSTR_FIELDS_EXPOSER.to_string(), unique: 0.into(), } .into(), ), value, ), ); 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 = 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 = apply_wrap( apply_wrap( DefaultFunction::MkPairData.into(), convert_type_to_data(args[0].clone(), &tuple_sub_types[0]), ), convert_type_to_data(args[1].clone(), &tuple_sub_types[1]), ); arg_stack.push(term); } else { let mut term = Term::Constant(UplcConstant::ProtoList(UplcType::Data, vec![]).into()); for (arg, tipo) in args.into_iter().zip(tuple_sub_types.into_iter()).rev() { term = apply_wrap( apply_wrap( Term::Builtin(DefaultFunction::MkCons).force_wrap(), convert_type_to_data(arg, &tipo), ), term, ); } arg_stack.push(term); } } Air::Todo { label, .. } => { let term = apply_wrap( apply_wrap( Term::Builtin(DefaultFunction::Trace).force_wrap(), Term::Constant( UplcConstant::String( label.unwrap_or_else(|| "aiken::todo".to_string()), ) .into(), ), ), Term::Delay(Term::Error.into()), ) .force_wrap(); arg_stack.push(term); } Air::RecordUpdate { highest_index, indices, .. } => { let tail_name_prefix = "__tail_index".to_string(); let record = arg_stack.pop().unwrap(); let mut args = IndexMap::new(); let mut unchanged_field_indices = vec![]; let mut prev_index = 0; for (index, tipo) in indices .iter() .sorted_by(|(index1, _), (index2, _)| index1.cmp(index2)) .rev() { let arg = arg_stack.pop().unwrap(); args.insert(*index, (tipo.clone(), arg)); for field_index in prev_index..*index { unchanged_field_indices.push(field_index); } prev_index = *index; } unchanged_field_indices.reverse(); let mut term = apply_wrap( Term::Builtin(DefaultFunction::TailList).force_wrap(), Term::Var( Name { text: format!("{tail_name_prefix}_{highest_index}"), unique: 0.into(), } .into(), ), ); 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 = apply_wrap( apply_wrap( Term::Builtin(DefaultFunction::MkCons).force_wrap(), convert_type_to_data(arg.clone(), tipo), ), term, ); } else { term = apply_wrap( apply_wrap( Term::Builtin(DefaultFunction::MkCons).force_wrap(), apply_wrap( Term::Builtin(DefaultFunction::HeadList).force_wrap(), Term::Var( Name { text: tail_name, unique: 0.into(), } .into(), ), ), ), term, ) } } term = apply_wrap( apply_wrap( Term::Builtin(DefaultFunction::ConstrData), Term::Constant(UplcConstant::Integer(0.into()).into()), ), term, ); if !unchanged_field_indices.is_empty() { prev_index = highest_index; for index in unchanged_field_indices.into_iter() { let tail_name = format!("{tail_name_prefix}_{prev_index}"); let prev_tail_name = format!("{tail_name_prefix}_{index}"); let mut tail_list = Term::Var( Name { text: prev_tail_name, unique: 0.into(), } .into(), ); if index < prev_index { for _ in index..prev_index { tail_list = apply_wrap( Term::Builtin(DefaultFunction::TailList).force_wrap(), tail_list, ); } term = apply_wrap( Term::Lambda { parameter_name: Name { text: tail_name, unique: 0.into(), } .into(), body: term.into(), }, tail_list, ); } prev_index = index; } } let tail_name = format!("{tail_name_prefix}_{prev_index}"); let prev_tail_name = format!("{tail_name_prefix}_0"); let mut tail_list = Term::Var( Name { text: prev_tail_name.clone(), unique: 0.into(), } .into(), ); for _ in 0..prev_index { tail_list = apply_wrap( Term::Builtin(DefaultFunction::TailList).force_wrap(), tail_list, ); } if prev_index != 0 { term = apply_wrap( Term::Lambda { parameter_name: Name { text: tail_name, unique: 0.into(), } .into(), body: term.into(), }, tail_list, ); } self.needs_field_access = true; term = apply_wrap( Term::Lambda { parameter_name: Name { text: prev_tail_name, unique: 0.into(), } .into(), body: term.into(), }, apply_wrap( Term::Var( Name { text: CONSTR_FIELDS_EXPOSER.to_string(), unique: 0.into(), } .into(), ), record, ), ); arg_stack.push(term); } Air::UnOp { op, .. } => { let value = arg_stack.pop().unwrap(); let term = match op { UnOp::Not => if_else( value, Term::Constant(UplcConstant::Bool(false).into()), Term::Constant(UplcConstant::Bool(true).into()), ), UnOp::Negate => apply_wrap( apply_wrap( DefaultFunction::SubtractInteger.into(), Term::Constant(UplcConstant::Integer(0.into()).into()), ), 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 = convert_data_to_type( apply_wrap( Term::Builtin(DefaultFunction::FstPair) .force_wrap() .force_wrap(), term, ), &tipo.get_inner_types()[0], ); } else { term = convert_data_to_type( apply_wrap( Term::Builtin(DefaultFunction::SndPair) .force_wrap() .force_wrap(), term, ), &tipo.get_inner_types()[1], ); } } else { self.needs_field_access = true; term = convert_data_to_type( apply_wrap( apply_wrap( Term::Var( Name { text: CONSTR_GET_FIELD.to_string(), unique: 0.into(), } .into(), ), term, ), Term::Constant(UplcConstant::Integer(tuple_index.into()).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 = apply_wrap( Term::Lambda { parameter_name: Name { text: format!("__tuple_{list_id}"), unique: 0.into(), } .into(), body: apply_wrap( Term::Lambda { parameter_name: Name { text: names[0].clone(), unique: 0.into(), } .into(), body: apply_wrap( Term::Lambda { parameter_name: Name { text: names[1].clone(), unique: 0.into(), } .into(), body: term.into(), }, convert_data_to_type( apply_wrap( Term::Builtin(DefaultFunction::SndPair) .force_wrap() .force_wrap(), Term::Var( Name { text: format!("__tuple_{list_id}"), unique: 0.into(), } .into(), ), ), &inner_types[1], ), ) .into(), }, convert_data_to_type( apply_wrap( Term::Builtin(DefaultFunction::FstPair) .force_wrap() .force_wrap(), Term::Var( Name { text: format!("__tuple_{list_id}"), unique: 0.into(), } .into(), ), ), &inner_types[0], ), ) .into(), }, value, ); } else { let mut id_list = vec![]; id_list.push(list_id); for _ in 0..names.len() { id_list.push(self.id_gen.next()); } term = apply_wrap( list_access_to_uplc( &names, &id_list, false, 0, term, tipo.get_inner_types(), check_last_item, false, ), value, ); } arg_stack.push(term); } Air::Trace { .. } => { let text = arg_stack.pop().unwrap(); let term = arg_stack.pop().unwrap(); let term = apply_wrap( apply_wrap(Term::Builtin(DefaultFunction::Trace).force_wrap(), text), Term::Delay(term.into()), ) .force_wrap(); arg_stack.push(term); } Air::ErrorTerm { label, .. } => { if let Some(label) = label { let term = apply_wrap( apply_wrap( Term::Builtin(DefaultFunction::Trace).force_wrap(), Term::Constant(UplcConstant::String(label).into()), ), Term::Delay(Term::Error.into()), ) .force_wrap(); arg_stack.push(term); } else { arg_stack.push(Term::Error) } } Air::TupleClause { 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 = apply_wrap( Term::Lambda { parameter_name: Name { text: "__other_clauses_delayed".to_string(), unique: 0.into(), } .into(), body: term.into(), }, Term::Delay(next_clause.into()), ) } if tuple_types.len() == 2 { for (index, name) in indices.iter() { if *index == 0 { term = apply_wrap( Term::Lambda { parameter_name: Name { text: name.clone(), unique: 0.into(), } .into(), body: term.into(), }, convert_data_to_type( apply_wrap( Term::Builtin(DefaultFunction::FstPair) .force_wrap() .force_wrap(), Term::Var( Name { text: subject_name.clone(), unique: 0.into(), } .into(), ), ), &tuple_types[*index].clone(), ), ); } else { term = apply_wrap( Term::Lambda { parameter_name: Name { text: name.clone(), unique: 0.into(), } .into(), body: term.into(), }, convert_data_to_type( apply_wrap( Term::Builtin(DefaultFunction::SndPair) .force_wrap() .force_wrap(), Term::Var( Name { text: subject_name.clone(), unique: 0.into(), } .into(), ), ), &tuple_types[*index].clone(), ), ); } } } else { for (index, name) in indices.iter() { term = apply_wrap( Term::Lambda { parameter_name: Name { text: name.clone(), unique: 0.into(), } .into(), body: term.into(), }, convert_data_to_type( apply_wrap( Term::Builtin(DefaultFunction::HeadList).force_wrap(), repeat_tail_list( Term::Var( Name { text: subject_name.clone(), unique: 0.into(), } .into(), ), *index, ), ), &tuple_types[*index].clone(), ), ); } } arg_stack.push(term); } } } }