use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, LocalSlot}; use crate::ast::types::{Purity, Value}; use crate::ast::vm::Closure; use std::cell::RefCell; use std::collections::HashSet; use std::rc::Rc; use super::substitution_map::SubstitutionMap; use super::utils::UsageInfo; pub struct Inliner<'a> { pub globals: &'a Option>>>, pub root_purity: &'a Option>>>, } impl<'a> Inliner<'a> { pub fn new( globals: &'a Option>>>, root_purity: &'a Option>>>, ) -> Self { Self { globals, root_purity, } } pub fn is_inlinable_value(&self, val: &Value, addr: Address) -> bool { let type_ok = match val { Value::Int(_) | Value::Float(_) | Value::Bool(_) | Value::Text(_) | Value::Keyword(_) | Value::Record(_, _) | Value::DateTime(_) => true, Value::Object(obj) => { if let Some(closure) = obj.as_any().downcast_ref::() { closure.upvalues.is_empty() && !closure.function_node.ty.is_recursive } else { false } } _ => false, }; if !type_ok { return false; } if let Address::Global(idx) = addr { if let Some(purity_rc) = &self.root_purity { let purity = purity_rc .borrow() .get(idx.0 as usize) .cloned() .unwrap_or(Purity::Impure); return purity >= Purity::Pure; } return false; } true } pub fn prepare_beta_reduction( &self, params: &AnalyzedNode, arg_vals: &[Rc], body: &AnalyzedNode, sub: &mut SubstitutionMap, ) -> Option<()> { let mut body_usage = UsageInfo::default(); body_usage.collect(body); let mut slot_index = 0; self.map_params_to_args(params, arg_vals, &mut slot_index, sub, &body_usage); if slot_index != arg_vals.len() { return None; } let mut param_slots = HashSet::new(); self.collect_parameter_slots_set(params, &mut param_slots); for slot in param_slots { let addr = Address::Local(slot); if (body_usage.is_used(&addr) || body_usage.is_assigned(&addr)) && sub.get_value(&addr).is_none() && !sub.ast_substitutions.contains_key(&addr) { return None; } } if sub.values.is_empty() && sub.ast_substitutions.is_empty() && !arg_vals.is_empty() { return None; } Some(()) } pub fn map_params_to_args( &self, pattern: &AnalyzedNode, args: &[Rc], offset: &mut usize, sub: &mut SubstitutionMap, body_usage: &UsageInfo, ) { match &pattern.kind { BoundKind::Define { addr, .. } => { if let Some(arg) = args.get(*offset) && !body_usage.is_assigned(addr) { if let BoundKind::Constant(val) = &arg.kind { sub.add_value(*addr, val.clone()); } else if let BoundKind::Lambda { upvalues, .. } = &arg.kind && upvalues.is_empty() { sub.add_ast_substitution(*addr, (**arg).clone()); } else if let BoundKind::Get { addr: Address::Global(_), .. } = &arg.kind { sub.add_ast_substitution(*addr, (**arg).clone()); } } if let Address::Local(slot) = addr { sub.map_slot(*slot); } *offset += 1; } BoundKind::Tuple { elements } => { for el in elements { self.map_params_to_args(el, args, offset, sub, body_usage); } } _ => {} } } pub fn collect_parameter_slots_set(&self, node: &AnalyzedNode, slots: &mut HashSet) { match &node.kind { BoundKind::Define { addr: Address::Local(slot), .. } => { slots.insert(*slot); } BoundKind::Tuple { elements } => { for el in elements { self.collect_parameter_slots_set(el, slots); } } _ => {} } } pub fn collect_parameter_slots(&self, node: &AnalyzedNode, sub: &mut SubstitutionMap) { match &node.kind { BoundKind::Define { addr: Address::Local(slot), .. } => { sub.slot_mapping.insert(*slot, *slot); sub.next_slot = sub.next_slot.max(slot.0 + 1); } BoundKind::Tuple { elements } => { for el in elements { self.collect_parameter_slots(el, sub); } } _ => {} } } }