use std::sync::Arc; use uplc::{builder::EXPECT_ON_LIST, builtins::DefaultFunction}; use crate::{ ast::Span, tipo::{Type, ValueConstructor, ValueConstructorVariant}, IdGenerator, }; use super::{air::Air, scope::Scope}; /// A builder for [`Air`]. pub struct AirStack<'a> { pub id_gen: &'a mut IdGenerator, pub scope: Scope, pub air: Vec, } impl<'a> AirStack<'a> { /// Create a new [`AirStack`] with an [`IdGenerator`] pub fn new(id_gen: &'a mut IdGenerator) -> Self { AirStack { id_gen, scope: Scope::default(), air: vec![], } } /// Create a new [`AirStack`] with an [`IdGenerator`] and [`Scope`]. pub fn with_scope(id_gen: &'a mut IdGenerator, scope: Scope) -> Self { AirStack { id_gen, scope, air: vec![], } } /// Create a new empty [`AirStack`] with the current stack's scope. pub fn empty_with_scope(&mut self) -> Self { AirStack::with_scope(&mut self.id_gen, self.scope.clone()) } /// Increment the [`Scope`] fn new_scope(&mut self) { self.scope.push(self.id_gen.next()); } /// Merge two [`AirStack`]'s together while maintaining the current stack's [`Scope`] pub fn merge(&mut self, mut other: AirStack) { self.air.append(&mut other.air); } pub fn merge_child(&mut self, mut other: AirStack) { let pattern = self.scope.common_ancestor(&other.scope); for ir in other.air.iter_mut() { ir.scope_mut().replace(&pattern, self.scope.clone()); } self.merge(other); } pub fn merge_children(&mut self, stacks: Vec) { for stack in stacks { self.merge_child(stack) } } pub fn complete(self) -> Vec { self.air } pub fn sequence(&mut self, stacks: Vec) { for stack in stacks { self.merge(stack) } } pub fn integer(&mut self, value: String) { self.new_scope(); self.air.push(Air::Int { scope: self.scope.clone(), value, }); } pub fn string(&mut self, value: impl ToString) { self.new_scope(); self.air.push(Air::String { scope: self.scope.clone(), value: value.to_string(), }); } pub fn byte_array(&mut self, bytes: Vec) { self.new_scope(); self.air.push(Air::ByteArray { scope: self.scope.clone(), bytes, }); } pub fn builtin(&mut self, func: DefaultFunction, tipo: Arc, args: Vec) { self.new_scope(); self.air.push(Air::Builtin { scope: self.scope.clone(), count: args.len(), func, tipo, }); self.merge_children(args); } pub fn var( &mut self, constructor: ValueConstructor, name: impl ToString, variant_name: impl ToString, ) { self.new_scope(); self.air.push(Air::Var { scope: self.scope.clone(), constructor, name: name.to_string(), variant_name: variant_name.to_string(), }); } pub fn local_var(&mut self, tipo: Arc, name: impl ToString) { self.new_scope(); self.air.push(Air::Var { scope: self.scope.clone(), constructor: ValueConstructor::public( tipo, ValueConstructorVariant::LocalVariable { location: Span::empty(), }, ), name: name.to_string(), variant_name: String::new(), }); } pub fn anonymous_function(&mut self, params: Vec, body: AirStack) { self.new_scope(); self.air.push(Air::Fn { scope: self.scope.clone(), params, }); self.merge_child(body); } pub fn list(&mut self, tipo: Arc, elements: Vec, tail: Option) { self.new_scope(); self.air.push(Air::List { scope: self.scope.clone(), count: elements.len(), tipo, tail: tail.is_some(), }); self.merge_children(elements); if let Some(tail) = tail { self.merge_child(tail); } } pub fn record(&mut self, tipo: Arc, tag: usize, fields: Vec) { self.new_scope(); self.air.push(Air::Record { scope: self.scope.clone(), tag, tipo, count: fields.len(), }); self.merge_children(fields); } pub fn call(&mut self, tipo: Arc, fun: AirStack, args: Vec) { self.new_scope(); self.air.push(Air::Call { scope: self.scope.clone(), count: args.len(), tipo, }); self.merge_child(fun); self.merge_children(args); } pub fn binop( &mut self, name: crate::ast::BinOp, tipo: Arc, left: AirStack, right: AirStack, ) { self.new_scope(); self.air.push(Air::BinOp { scope: self.scope.clone(), name, tipo, }); self.merge_child(left); self.merge_child(right); } pub fn unop(&mut self, op: crate::ast::UnOp, value: AirStack) { self.new_scope(); self.air.push(Air::UnOp { scope: self.scope.clone(), op, }); self.merge_child(value); } pub fn let_assignment(&mut self, name: impl ToString, value: AirStack) { self.new_scope(); self.air.push(Air::Let { scope: self.scope.clone(), name: name.to_string(), }); self.merge_child(value); } pub fn expect_list_from_data( &mut self, tipo: Arc, name: impl ToString, unwrap_function: AirStack, ) { self.new_scope(); self.air.push(Air::Builtin { scope: self.scope.clone(), func: DefaultFunction::ChooseUnit, tipo: tipo.clone(), count: DefaultFunction::ChooseUnit.arity(), }); self.new_scope(); self.air.push(Air::Call { scope: self.scope.clone(), count: 2, tipo, }); self.local_var(tipo.clone(), EXPECT_ON_LIST); self.local_var(tipo, name); self.merge_child(unwrap_function); } pub fn wrap_data(&mut self, tipo: Arc) { self.new_scope(); self.air.push(Air::WrapData { scope: self.scope.clone(), tipo, }) } pub fn un_wrap_data(&mut self, tipo: Arc) { self.new_scope(); self.air.push(Air::UnWrapData { scope: self.scope.clone(), tipo, }) } pub fn void(&mut self) { self.new_scope(); self.air.push(Air::Void { scope: self.scope.clone(), }) } pub fn tuple_accessor( &mut self, tipo: Arc, names: Vec, check_last_item: bool, value: AirStack, ) { self.new_scope(); self.air.push(Air::TupleAccessor { scope: self.scope.clone(), names, tipo, check_last_item, }); self.merge_child(value); } pub fn fields_expose( &mut self, indices: Vec<(usize, String, Arc)>, check_last_item: bool, value: AirStack, ) { self.new_scope(); self.air.push(Air::FieldsExpose { scope: self.scope.clone(), indices, check_last_item, }); self.merge_child(value); } pub fn clause( &mut self, tipo: Arc, subject_name: impl ToString, tag: usize, complex_clause: bool, body: AirStack, ) { self.new_scope(); self.air.push(Air::Clause { scope: self.scope.clone(), subject_name: subject_name.to_string(), complex_clause, tipo, }); self.integer(tag.to_string()); self.merge_child(body); } pub fn trace(&mut self, tipo: Arc) { self.new_scope(); self.air.push(Air::Trace { scope: self.scope.clone(), tipo, }) } pub fn error(&mut self, tipo: Arc) { self.new_scope(); self.air.push(Air::ErrorTerm { scope: self.scope.clone(), tipo, }) } pub fn expect_constr_from_data(&mut self, tipo: Arc, when_stack: AirStack) { self.new_scope(); self.air.push(Air::Builtin { scope: self.scope.clone(), func: DefaultFunction::ChooseUnit, tipo: tipo.clone(), count: DefaultFunction::ChooseUnit.arity(), }); self.merge_child(when_stack); } pub fn when( &mut self, tipo: Arc, subject_name: impl ToString, subject_stack: AirStack, clauses_stack: AirStack, else_stack: AirStack, ) { self.new_scope(); self.air.push(Air::When { scope: self.scope.clone(), subject_name: subject_name.to_string(), tipo, }); self.merge_child(subject_stack); self.merge_child(clauses_stack); self.merge_child(else_stack); } pub fn list_accessor( &mut self, tipo: Arc, names: Vec, tail: bool, check_last_item: bool, value: AirStack, ) { self.new_scope(); self.air.push(Air::ListAccessor { scope: self.scope.clone(), names, tail, check_last_item, tipo, }); self.merge_child(value); } pub fn expect_constr(&mut self, tag: usize, value: AirStack) { self.new_scope(); self.air.push(Air::AssertConstr { scope: self.scope.clone(), constr_index: tag, }); } pub fn expect_bool(&mut self, is_true: bool, value: AirStack) { self.new_scope(); self.air.push(Air::AssertBool { scope: self.scope.clone(), is_true, }); self.merge_child(value); } pub fn if_branch(&mut self, tipo: Arc, condition: AirStack, branch_body: AirStack) { self.new_scope(); self.air.push(Air::If { scope: self.scope.clone(), tipo, }); self.merge_child(condition); self.merge_child(branch_body); } pub fn record_access(&mut self, tipo: Arc, record_index: u64, record: AirStack) { self.new_scope(); self.air.push(Air::RecordAccess { scope: self.scope.clone(), record_index, tipo, }); self.merge_child(record); } pub fn record_update( &mut self, tipo: Arc, highest_index: usize, indices: Vec<(usize, Arc)>, update: AirStack, ) { self.new_scope(); self.air.push(Air::RecordUpdate { scope: self.scope.clone(), highest_index, indices, tipo, }); self.merge_child(update); } pub fn tuple(&mut self, tipo: Arc, elems: Vec) { self.new_scope(); self.air.push(Air::Tuple { scope: self.scope.clone(), count: elems.len(), tipo, }); self.merge_children(elems); } pub fn tuple_index(&mut self, tipo: Arc, tuple_index: usize, tuple: AirStack) { self.new_scope(); self.air.push(Air::TupleIndex { scope: self.scope.clone(), tuple_index, tipo, }); self.merge_child(tuple); } }