From eeb662128060f93c3a734f90c1100fd59f4900a1 Mon Sep 17 00:00:00 2001 From: Michael Schimmel Date: Tue, 24 Feb 2026 07:27:13 +0100 Subject: [PATCH] Formatting --- src/ast/compiler/analyzer.rs | 576 +++++++++++++++++++------------- src/ast/compiler/optimizer.rs | 251 +++++++++++--- src/ast/compiler/specializer.rs | 158 +++++++-- src/ast/compiler/tco.rs | 2 +- src/ast/environment.rs | 27 +- src/ast/vm.rs | 19 +- src/integration_test.rs | 14 +- 7 files changed, 713 insertions(+), 334 deletions(-) diff --git a/src/ast/compiler/analyzer.rs b/src/ast/compiler/analyzer.rs index d0155f7..e0f910d 100644 --- a/src/ast/compiler/analyzer.rs +++ b/src/ast/compiler/analyzer.rs @@ -1,223 +1,353 @@ -use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, NodeMetrics, TypedNode}; -use crate::ast::types::Purity; -use std::collections::{HashMap, HashSet}; -use std::rc::Rc; - -pub struct Analyzer<'a> { - global_purity: &'a HashMap, - /// Stack of currently visiting lambdas to detect direct recursion. - lambda_stack: Vec, - /// Map of global index to its Lambda identity if known. - globals_to_lambdas: HashMap, - /// Set of identities that were found to be recursive. - recursive_identities: HashSet, -} - -impl<'a> Analyzer<'a> { - pub fn analyze(node: &TypedNode, global_purity: &'a HashMap) -> AnalyzedNode { - let mut analyzer = Self { - global_purity, - lambda_stack: Vec::new(), - globals_to_lambdas: HashMap::new(), - recursive_identities: HashSet::new(), - }; - - // First pass: map globals to their lambda identities - analyzer.collect_globals(node); - - // Second pass: full analysis (decorating TypedNode into AnalyzedNode) - analyzer.visit(Rc::new(node.clone())) - } - - fn collect_globals(&mut self, node: &TypedNode) { - match &node.kind { - BoundKind::DefGlobal { global_index, value, .. } => { - if let BoundKind::Lambda { .. } = &value.kind { - self.globals_to_lambdas.insert(*global_index, value.identity.clone()); - } - self.collect_globals(value); - } - BoundKind::Block { exprs } => { - for e in exprs { self.collect_globals(e); } - } - _ => { - node.kind.for_each_child(|child| self.collect_globals(child)); - } - } - } - - fn visit(&mut self, node_rc: Rc) -> AnalyzedNode { - let node = &*node_rc; - let mut is_recursive = false; - - let (new_kind, purity) = match &node.kind { - BoundKind::Constant(v) => (BoundKind::Constant(v.clone()), Purity::Pure), - BoundKind::Nop => (BoundKind::Nop, Purity::Pure), - BoundKind::Parameter { name, slot } => { - (BoundKind::Parameter { name: name.clone(), slot: *slot }, Purity::Pure) - } - - BoundKind::Get { addr, name } => { - let p = match addr { - Address::Global(idx) => self.global_purity.get(idx).cloned().unwrap_or(Purity::Pure), - _ => Purity::Pure, - }; - (BoundKind::Get { addr: *addr, name: name.clone() }, p) - } - - BoundKind::Set { addr, value } => { - let val_m = self.visit(Rc::new((**value).clone())); - (BoundKind::Set { addr: *addr, value: Box::new(val_m) }, Purity::Impure) - } - - BoundKind::DefLocal { name, slot, value, captured_by } => { - let val_m = self.visit(Rc::new((**value).clone())); - let p = val_m.ty.purity; - (BoundKind::DefLocal { name: name.clone(), slot: *slot, value: Box::new(val_m), captured_by: captured_by.clone() }, p) - } - - BoundKind::DefGlobal { name, global_index, value } => { - let val_m = self.visit(Rc::new((**value).clone())); - let p = val_m.ty.purity; - (BoundKind::DefGlobal { name: name.clone(), global_index: *global_index, value: Box::new(val_m) }, p) - } - - BoundKind::If { cond, then_br, else_br } => { - let cond_m = self.visit(Rc::new((**cond).clone())); - let then_m = self.visit(Rc::new((**then_br).clone())); - let else_m = else_br.as_ref().map(|e| self.visit(Rc::new((**e).clone()))); - - let mut p = cond_m.ty.purity.min(then_m.ty.purity); - if let Some(ref em) = else_m { p = p.min(em.ty.purity); } - (BoundKind::If { cond: Box::new(cond_m), then_br: Box::new(then_m), else_br: else_m.map(Box::new) }, p) - } - - BoundKind::Lambda { params, upvalues, body, positional_count } => { - self.lambda_stack.push(node.identity.clone()); - let params_m = self.visit(params.clone()); - let body_m = self.visit(body.clone()); - self.lambda_stack.pop(); - - is_recursive = self.recursive_identities.contains(&node.identity); - (BoundKind::Lambda { params: Rc::new(params_m), upvalues: upvalues.clone(), body: Rc::new(body_m), positional_count: *positional_count }, Purity::Pure) - } - - BoundKind::Call { callee, args } => { - let callee_m = self.visit(Rc::new((**callee).clone())); - let args_m = self.visit(Rc::new((**args).clone())); - - if let BoundKind::Get { addr: Address::Global(idx), .. } = &callee.kind - && let Some(lambda_id) = self.globals_to_lambdas.get(idx) - && self.lambda_stack.contains(lambda_id) - { - self.recursive_identities.insert(lambda_id.clone()); - is_recursive = true; - } - - let p_func = if let BoundKind::Get { addr: Address::Global(idx), .. } = &callee.kind { - self.global_purity.get(idx).cloned().unwrap_or(Purity::Impure) - } else { - Purity::Impure - }; - let p = callee_m.ty.purity.min(args_m.ty.purity).min(p_func); - (BoundKind::Call { callee: Box::new(callee_m), args: Box::new(args_m) }, p) - } - - BoundKind::Again { args } => { - let args_m = self.visit(Rc::new((**args).clone())); - if let Some(lambda_id) = self.lambda_stack.last() { - self.recursive_identities.insert(lambda_id.clone()); - is_recursive = true; - } - (BoundKind::Again { args: Box::new(args_m) }, Purity::Impure) - } - - BoundKind::Block { exprs } => { - let mut new_exprs = Vec::with_capacity(exprs.len()); - let mut p = Purity::Pure; - for e in exprs { - let em = self.visit(Rc::new(e.clone())); - p = p.min(em.ty.purity); - new_exprs.push(em); - } - (BoundKind::Block { exprs: new_exprs }, p) - } - - BoundKind::Tuple { elements } => { - let mut new_elements = Vec::with_capacity(elements.len()); - let mut p = Purity::Pure; - for e in elements { - let em = self.visit(Rc::new(e.clone())); - p = p.min(em.ty.purity); - new_elements.push(em); - } - (BoundKind::Tuple { elements: new_elements }, p) - } - - BoundKind::Record { fields } => { - let mut new_fields = Vec::with_capacity(fields.len()); - let mut p = Purity::Pure; - for (k, v) in fields { - let km = self.visit(Rc::new(k.clone())); - let vm = self.visit(Rc::new(v.clone())); - p = p.min(km.ty.purity).min(vm.ty.purity); - new_fields.push((km, vm)); - } - (BoundKind::Record { fields: new_fields }, p) - } - - BoundKind::Expansion { original_call, bound_expanded } => { - let expanded_m = self.visit(Rc::new((**bound_expanded).clone())); - (BoundKind::Expansion { original_call: original_call.clone(), bound_expanded: Box::new(expanded_m.clone()) }, expanded_m.ty.purity) - } - - BoundKind::Extension(_) => (BoundKind::Nop, Purity::Impure), - }; - - crate::ast::nodes::Node { - identity: node.identity.clone(), - kind: new_kind, - ty: NodeMetrics { - original: node_rc, - purity, - is_recursive, - }, - } - } -} - -trait NodeExt { - fn for_each_child(&self, f: F); -} - -impl NodeExt for BoundKind { - fn for_each_child(&self, mut f: F) { - match self { - BoundKind::If { cond, then_br, else_br } => { - f(cond); f(then_br); if let Some(e) = else_br { f(e); } - } - BoundKind::DefLocal { value, .. } | BoundKind::DefGlobal { value, .. } | BoundKind::Set { value, .. } => { - f(value); - } - BoundKind::Lambda { params, body, .. } => { - f(params); f(body); - } - BoundKind::Call { callee, args } => { - f(callee); f(args); - } - BoundKind::Block { exprs } => { - for e in exprs { f(e); } - } - BoundKind::Tuple { elements } => { - for e in elements { f(e); } - } - BoundKind::Record { fields } => { - for (k, v) in fields { f(k); f(v); } - } - BoundKind::Expansion { bound_expanded, .. } => { - f(bound_expanded); - } - _ => {} - } - } -} +use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, NodeMetrics, TypedNode}; +use crate::ast::types::Purity; +use std::collections::{HashMap, HashSet}; +use std::rc::Rc; + +pub struct Analyzer<'a> { + global_purity: &'a HashMap, + /// Stack of currently visiting lambdas to detect direct recursion. + lambda_stack: Vec, + /// Map of global index to its Lambda identity if known. + globals_to_lambdas: HashMap, + /// Set of identities that were found to be recursive. + recursive_identities: HashSet, +} + +impl<'a> Analyzer<'a> { + pub fn analyze(node: &TypedNode, global_purity: &'a HashMap) -> AnalyzedNode { + let mut analyzer = Self { + global_purity, + lambda_stack: Vec::new(), + globals_to_lambdas: HashMap::new(), + recursive_identities: HashSet::new(), + }; + + // First pass: map globals to their lambda identities + analyzer.collect_globals(node); + + // Second pass: full analysis (decorating TypedNode into AnalyzedNode) + analyzer.visit(Rc::new(node.clone())) + } + + fn collect_globals(&mut self, node: &TypedNode) { + match &node.kind { + BoundKind::DefGlobal { + global_index, + value, + .. + } => { + if let BoundKind::Lambda { .. } = &value.kind { + self.globals_to_lambdas + .insert(*global_index, value.identity.clone()); + } + self.collect_globals(value); + } + BoundKind::Block { exprs } => { + for e in exprs { + self.collect_globals(e); + } + } + _ => { + node.kind + .for_each_child(|child| self.collect_globals(child)); + } + } + } + + fn visit(&mut self, node_rc: Rc) -> AnalyzedNode { + let node = &*node_rc; + let mut is_recursive = false; + + let (new_kind, purity) = match &node.kind { + BoundKind::Constant(v) => (BoundKind::Constant(v.clone()), Purity::Pure), + BoundKind::Nop => (BoundKind::Nop, Purity::Pure), + BoundKind::Parameter { name, slot } => ( + BoundKind::Parameter { + name: name.clone(), + slot: *slot, + }, + Purity::Pure, + ), + + BoundKind::Get { addr, name } => { + let p = match addr { + Address::Global(idx) => { + self.global_purity.get(idx).cloned().unwrap_or(Purity::Pure) + } + _ => Purity::Pure, + }; + ( + BoundKind::Get { + addr: *addr, + name: name.clone(), + }, + p, + ) + } + + BoundKind::Set { addr, value } => { + let val_m = self.visit(Rc::new((**value).clone())); + ( + BoundKind::Set { + addr: *addr, + value: Box::new(val_m), + }, + Purity::Impure, + ) + } + + BoundKind::DefLocal { + name, + slot, + value, + captured_by, + } => { + let val_m = self.visit(Rc::new((**value).clone())); + let p = val_m.ty.purity; + ( + BoundKind::DefLocal { + name: name.clone(), + slot: *slot, + value: Box::new(val_m), + captured_by: captured_by.clone(), + }, + p, + ) + } + + BoundKind::DefGlobal { + name, + global_index, + value, + } => { + let val_m = self.visit(Rc::new((**value).clone())); + let p = val_m.ty.purity; + ( + BoundKind::DefGlobal { + name: name.clone(), + global_index: *global_index, + value: Box::new(val_m), + }, + p, + ) + } + + BoundKind::If { + cond, + then_br, + else_br, + } => { + let cond_m = self.visit(Rc::new((**cond).clone())); + let then_m = self.visit(Rc::new((**then_br).clone())); + let else_m = else_br.as_ref().map(|e| self.visit(Rc::new((**e).clone()))); + + let mut p = cond_m.ty.purity.min(then_m.ty.purity); + if let Some(ref em) = else_m { + p = p.min(em.ty.purity); + } + ( + BoundKind::If { + cond: Box::new(cond_m), + then_br: Box::new(then_m), + else_br: else_m.map(Box::new), + }, + p, + ) + } + + BoundKind::Lambda { + params, + upvalues, + body, + positional_count, + } => { + self.lambda_stack.push(node.identity.clone()); + let params_m = self.visit(params.clone()); + let body_m = self.visit(body.clone()); + self.lambda_stack.pop(); + + is_recursive = self.recursive_identities.contains(&node.identity); + ( + BoundKind::Lambda { + params: Rc::new(params_m), + upvalues: upvalues.clone(), + body: Rc::new(body_m), + positional_count: *positional_count, + }, + Purity::Pure, + ) + } + + BoundKind::Call { callee, args } => { + let callee_m = self.visit(Rc::new((**callee).clone())); + let args_m = self.visit(Rc::new((**args).clone())); + + if let BoundKind::Get { + addr: Address::Global(idx), + .. + } = &callee.kind + && let Some(lambda_id) = self.globals_to_lambdas.get(idx) + && self.lambda_stack.contains(lambda_id) + { + self.recursive_identities.insert(lambda_id.clone()); + is_recursive = true; + } + + let p_func = if let BoundKind::Get { + addr: Address::Global(idx), + .. + } = &callee.kind + { + self.global_purity + .get(idx) + .cloned() + .unwrap_or(Purity::Impure) + } else { + Purity::Impure + }; + let p = callee_m.ty.purity.min(args_m.ty.purity).min(p_func); + ( + BoundKind::Call { + callee: Box::new(callee_m), + args: Box::new(args_m), + }, + p, + ) + } + + BoundKind::Again { args } => { + let args_m = self.visit(Rc::new((**args).clone())); + if let Some(lambda_id) = self.lambda_stack.last() { + self.recursive_identities.insert(lambda_id.clone()); + is_recursive = true; + } + ( + BoundKind::Again { + args: Box::new(args_m), + }, + Purity::Impure, + ) + } + + BoundKind::Block { exprs } => { + let mut new_exprs = Vec::with_capacity(exprs.len()); + let mut p = Purity::Pure; + for e in exprs { + let em = self.visit(Rc::new(e.clone())); + p = p.min(em.ty.purity); + new_exprs.push(em); + } + (BoundKind::Block { exprs: new_exprs }, p) + } + + BoundKind::Tuple { elements } => { + let mut new_elements = Vec::with_capacity(elements.len()); + let mut p = Purity::Pure; + for e in elements { + let em = self.visit(Rc::new(e.clone())); + p = p.min(em.ty.purity); + new_elements.push(em); + } + ( + BoundKind::Tuple { + elements: new_elements, + }, + p, + ) + } + + BoundKind::Record { fields } => { + let mut new_fields = Vec::with_capacity(fields.len()); + let mut p = Purity::Pure; + for (k, v) in fields { + let km = self.visit(Rc::new(k.clone())); + let vm = self.visit(Rc::new(v.clone())); + p = p.min(km.ty.purity).min(vm.ty.purity); + new_fields.push((km, vm)); + } + (BoundKind::Record { fields: new_fields }, p) + } + + BoundKind::Expansion { + original_call, + bound_expanded, + } => { + let expanded_m = self.visit(Rc::new((**bound_expanded).clone())); + ( + BoundKind::Expansion { + original_call: original_call.clone(), + bound_expanded: Box::new(expanded_m.clone()), + }, + expanded_m.ty.purity, + ) + } + + BoundKind::Extension(_) => (BoundKind::Nop, Purity::Impure), + }; + + crate::ast::nodes::Node { + identity: node.identity.clone(), + kind: new_kind, + ty: NodeMetrics { + original: node_rc, + purity, + is_recursive, + }, + } + } +} + +trait NodeExt { + fn for_each_child(&self, f: F); +} + +impl NodeExt for BoundKind { + fn for_each_child(&self, mut f: F) { + match self { + BoundKind::If { + cond, + then_br, + else_br, + } => { + f(cond); + f(then_br); + if let Some(e) = else_br { + f(e); + } + } + BoundKind::DefLocal { value, .. } + | BoundKind::DefGlobal { value, .. } + | BoundKind::Set { value, .. } => { + f(value); + } + BoundKind::Lambda { params, body, .. } => { + f(params); + f(body); + } + BoundKind::Call { callee, args } => { + f(callee); + f(args); + } + BoundKind::Block { exprs } => { + for e in exprs { + f(e); + } + } + BoundKind::Tuple { elements } => { + for e in elements { + f(e); + } + } + BoundKind::Record { fields } => { + for (k, v) in fields { + f(k); + f(v); + } + } + BoundKind::Expansion { bound_expanded, .. } => { + f(bound_expanded); + } + _ => {} + } + } +} diff --git a/src/ast/compiler/optimizer.rs b/src/ast/compiler/optimizer.rs index a70b93f..00443a3 100644 --- a/src/ast/compiler/optimizer.rs +++ b/src/ast/compiler/optimizer.rs @@ -342,7 +342,9 @@ impl Optimizer { } let then_br = Box::new(self.visit_node((**then_br).clone(), sub, path)); - let else_br = else_br.as_ref().map(|e| Box::new(self.visit_node((**e).clone(), sub, path))); + let else_br = else_br + .as_ref() + .map(|e| Box::new(self.visit_node((**e).clone(), sub, path))); ( BoundKind::If { cond: Box::new(cond_opt), @@ -361,8 +363,12 @@ impl Optimizer { } } - for slot in &info.assigned_locals { sub.assigned_locals.insert(*slot); } - for idx in &info.assigned_globals { sub.assigned_globals.insert(*idx); } + for slot in &info.assigned_locals { + sub.assigned_locals.insert(*slot); + } + for idx in &info.assigned_globals { + sub.assigned_globals.insert(*idx); + } let mut new_exprs = Vec::with_capacity(exprs.len()); let last_idx = exprs.len().saturating_sub(1); @@ -397,7 +403,9 @@ impl Optimizer { _ => e.ty.purity >= Purity::SideEffectFree, }; - if removable { continue; } + if removable { + continue; + } } let opt = self.visit_node(e.clone(), sub, path); @@ -435,7 +443,9 @@ impl Optimizer { let mut info = UsageInfo::default(); self.collect_usage(&node, &mut info); - for idx in &info.assigned_globals { sub.assigned_globals.insert(*idx); } + for idx in &info.assigned_globals { + sub.assigned_globals.insert(*idx); + } let mut new_upvalues = Vec::new(); let mut mapping = Vec::new(); @@ -447,11 +457,15 @@ impl Optimizer { let mut inlined_val = None; match capture_addr { Address::Local(slot) => { - if let Some(val) = sub.locals.get(slot) { inlined_val = Some(val.clone()); } + if let Some(val) = sub.locals.get(slot) { + inlined_val = Some(val.clone()); + } sub.captured_slots.insert(*slot); } Address::Upvalue(idx) => { - if let Some(val) = sub.upvalues.get(idx) { inlined_val = Some(val.clone()); } + if let Some(val) = sub.upvalues.get(idx) { + inlined_val = Some(val.clone()); + } } _ => {} } @@ -470,7 +484,8 @@ impl Optimizer { } } - let params_node = self.visit_node(params.as_ref().clone(), &mut next_inner_subs, path); + let params_node = + self.visit_node(params.as_ref().clone(), &mut next_inner_subs, path); self.collect_parameter_slots(¶ms_node, &mut next_inner_subs); let body_node = self.visit_node(body.as_ref().clone(), &mut next_inner_subs, path); @@ -497,7 +512,9 @@ impl Optimizer { ref value, ref captured_by, } => { - if !captured_by.is_empty() { sub.captured_slots.insert(slot); } + if !captured_by.is_empty() { + sub.captured_slots.insert(slot); + } let value_opt = Box::new(self.visit_node((**value).clone(), sub, path)); if let BoundKind::Constant(val) = &value_opt.kind { sub.add_local(slot, val.clone()); @@ -563,7 +580,8 @@ impl Optimizer { ref bound_expanded, } => { path.inlining_depth += 1; - let bound_expanded = Box::new(self.visit_node((**bound_expanded).clone(), sub, path)); + let bound_expanded = + Box::new(self.visit_node((**bound_expanded).clone(), sub, path)); path.inlining_depth -= 1; ( BoundKind::Expansion { @@ -696,8 +714,14 @@ impl Optimizer { let mut slot_index = 0; self.map_params_to_args(params, &arg_vals, &mut slot_index, sub); - if slot_index != arg_vals.len() { return None; } - if sub.locals.is_empty() && sub.ast_locals.is_empty() && sub.upvalues.is_empty() && !arg_vals.is_empty() { + if slot_index != arg_vals.len() { + return None; + } + if sub.locals.is_empty() + && sub.ast_locals.is_empty() + && sub.upvalues.is_empty() + && !arg_vals.is_empty() + { return None; } @@ -707,10 +731,14 @@ impl Optimizer { fn flatten_tuple(&self, node: AnalyzedNode, into: &mut Vec) { match node.kind { BoundKind::Tuple { elements } => { - for el in elements { self.flatten_tuple(el, into); } + for el in elements { + self.flatten_tuple(el, into); + } } BoundKind::Record { fields } => { - for (_, v) in fields { self.flatten_tuple(v, into); } + for (_, v) in fields { + self.flatten_tuple(v, into); + } } BoundKind::Nop => {} _ => into.push(node), @@ -730,8 +758,14 @@ impl Optimizer { if let BoundKind::Constant(val) = &arg.kind { sub.add_local(*slot, val.clone()); } else if let BoundKind::Lambda { upvalues, .. } = &arg.kind { - if upvalues.is_empty() { sub.add_ast_local(*slot, arg.clone()); } - } else if let BoundKind::Get { addr: Address::Global(_), .. } = &arg.kind { + if upvalues.is_empty() { + sub.add_ast_local(*slot, arg.clone()); + } + } else if let BoundKind::Get { + addr: Address::Global(_), + .. + } = &arg.kind + { sub.add_ast_local(*slot, arg.clone()); } } @@ -764,7 +798,9 @@ impl Optimizer { } // Fallback: Continue flat matching (original behavior) - for el in elements { self.map_params_to_args(el, args, offset, sub); } + for el in elements { + self.map_params_to_args(el, args, offset, sub); + } } _ => {} } @@ -776,19 +812,28 @@ impl Optimizer { if let Value::Object(obj) = v && let Some(closure) = obj.as_any().downcast_ref::() { - info.used_identities.insert(closure.function_node.identity.clone()); + info.used_identities + .insert(closure.function_node.identity.clone()); self.collect_usage(&closure.function_node, info); } } BoundKind::Get { addr, .. } => match addr { - Address::Local(slot) => { info.used_locals.insert(*slot); } - Address::Global(idx) => { info.used_globals.insert(*idx); } + Address::Local(slot) => { + info.used_locals.insert(*slot); + } + Address::Global(idx) => { + info.used_globals.insert(*idx); + } _ => {} }, BoundKind::Set { addr, value } => { match addr { - Address::Local(slot) => { info.assigned_locals.insert(*slot); } - Address::Global(idx) => { info.assigned_globals.insert(*idx); } + Address::Local(slot) => { + info.assigned_locals.insert(*slot); + } + Address::Global(idx) => { + info.assigned_globals.insert(*idx); + } _ => {} } self.collect_usage(value, info); @@ -798,17 +843,31 @@ impl Optimizer { self.collect_usage(params, info); self.collect_usage(body, info); } - BoundKind::Block { exprs } => { for e in exprs { self.collect_usage(e, info); } } - BoundKind::If { cond, then_br, else_br } => { + BoundKind::Block { exprs } => { + for e in exprs { + self.collect_usage(e, info); + } + } + BoundKind::If { + cond, + then_br, + else_br, + } => { self.collect_usage(cond, info); self.collect_usage(then_br, info); - if let Some(e) = else_br { self.collect_usage(e, info); } + if let Some(e) = else_br { + self.collect_usage(e, info); + } } BoundKind::Call { callee, args } => { self.collect_usage(callee, info); self.collect_usage(args, info); } - BoundKind::Tuple { elements } => { for e in elements { self.collect_usage(e, info); } } + BoundKind::Tuple { elements } => { + for e in elements { + self.collect_usage(e, info); + } + } BoundKind::Record { fields } => { for (k, v) in fields { self.collect_usage(k, info); @@ -857,46 +916,103 @@ impl SubstitutionMap { } } - fn add_ast_local(&mut self, slot: u32, node: AnalyzedNode) { self.ast_locals.insert(slot, node); } + fn add_ast_local(&mut self, slot: u32, node: AnalyzedNode) { + self.ast_locals.insert(slot, node); + } fn map_slot(&mut self, old_slot: u32) -> u32 { - if let Some(&new_slot) = self.slot_mapping.get(&old_slot) { return new_slot; } + if let Some(&new_slot) = self.slot_mapping.get(&old_slot) { + return new_slot; + } let new_slot = self.next_slot; self.slot_mapping.insert(old_slot, new_slot); self.next_slot += 1; new_slot } - fn add_local(&mut self, slot: u32, val: Value) { self.locals.insert(slot, val); } - fn add_upvalue(&mut self, idx: u32, val: Value) { self.upvalues.insert(idx, val); } - fn add_global(&mut self, idx: u32, val: Value) { self.globals.insert(idx, val); } + fn add_local(&mut self, slot: u32, val: Value) { + self.locals.insert(slot, val); + } + fn add_upvalue(&mut self, idx: u32, val: Value) { + self.upvalues.insert(idx, val); + } + fn add_global(&mut self, idx: u32, val: Value) { + self.globals.insert(idx, val); + } fn reindex_upvalues(&self, node: AnalyzedNode, mapping: &[Option]) -> AnalyzedNode { let (new_kind, metrics) = match node.kind { - BoundKind::Get { addr: Address::Upvalue(idx), name } => { - if let Some(res) = mapping.get(idx as usize) && let Some(new_idx) = res { - (BoundKind::Get { addr: Address::Upvalue(*new_idx), name }, node.ty.clone()) + BoundKind::Get { + addr: Address::Upvalue(idx), + name, + } => { + if let Some(res) = mapping.get(idx as usize) + && let Some(new_idx) = res + { + ( + BoundKind::Get { + addr: Address::Upvalue(*new_idx), + name, + }, + node.ty.clone(), + ) } else { - (BoundKind::Get { addr: Address::Upvalue(idx), name }, node.ty.clone()) + ( + BoundKind::Get { + addr: Address::Upvalue(idx), + name, + }, + node.ty.clone(), + ) } } - BoundKind::Lambda { params, upvalues, body, positional_count } => { + BoundKind::Lambda { + params, + upvalues, + body, + positional_count, + } => { let mut next_upvalues = Vec::new(); for addr in upvalues { - if let Address::Upvalue(idx) = addr && let Some(res) = mapping.get(idx as usize) && let Some(new_idx) = res { + if let Address::Upvalue(idx) = addr + && let Some(res) = mapping.get(idx as usize) + && let Some(new_idx) = res + { next_upvalues.push(Address::Upvalue(*new_idx)); continue; } next_upvalues.push(addr); } - (BoundKind::Lambda { params, upvalues: next_upvalues, body, positional_count }, node.ty.clone()) + ( + BoundKind::Lambda { + params, + upvalues: next_upvalues, + body, + positional_count, + }, + node.ty.clone(), + ) } - BoundKind::If { cond, then_br, else_br } => { + BoundKind::If { + cond, + then_br, + else_br, + } => { let cond = Box::new(self.reindex_upvalues(*cond, mapping)); let then_br = Box::new(self.reindex_upvalues(*then_br, mapping)); let else_br = else_br.map(|e| Box::new(self.reindex_upvalues(*e, mapping))); - (BoundKind::If { cond, then_br, else_br }, node.ty.clone()) + ( + BoundKind::If { + cond, + then_br, + else_br, + }, + node.ty.clone(), + ) } BoundKind::Block { exprs } => { - let exprs = exprs.into_iter().map(|e| self.reindex_upvalues(e, mapping)).collect(); + let exprs = exprs + .into_iter() + .map(|e| self.reindex_upvalues(e, mapping)) + .collect(); (BoundKind::Block { exprs }, node.ty.clone()) } BoundKind::Call { callee, args } => { @@ -904,28 +1020,67 @@ impl SubstitutionMap { let args = Box::new(self.reindex_upvalues(*args, mapping)); (BoundKind::Call { callee, args }, node.ty.clone()) } - BoundKind::DefLocal { name, slot, value, captured_by } => { + BoundKind::DefLocal { + name, + slot, + value, + captured_by, + } => { let value = Box::new(self.reindex_upvalues(*value, mapping)); - (BoundKind::DefLocal { name, slot, value, captured_by }, node.ty.clone()) + ( + BoundKind::DefLocal { + name, + slot, + value, + captured_by, + }, + node.ty.clone(), + ) } - BoundKind::DefGlobal { name, global_index, value } => { + BoundKind::DefGlobal { + name, + global_index, + value, + } => { let value = Box::new(self.reindex_upvalues(*value, mapping)); - (BoundKind::DefGlobal { name, global_index, value }, node.ty.clone()) + ( + BoundKind::DefGlobal { + name, + global_index, + value, + }, + node.ty.clone(), + ) } BoundKind::Set { addr, value } => { let value = Box::new(self.reindex_upvalues(*value, mapping)); (BoundKind::Set { addr, value }, node.ty.clone()) } BoundKind::Tuple { elements } => { - let elements = elements.into_iter().map(|e| self.reindex_upvalues(e, mapping)).collect(); + let elements = elements + .into_iter() + .map(|e| self.reindex_upvalues(e, mapping)) + .collect(); (BoundKind::Tuple { elements }, node.ty.clone()) } BoundKind::Record { fields } => { - let fields = fields.into_iter().map(|(k, v)| (self.reindex_upvalues(k, mapping), self.reindex_upvalues(v, mapping))).collect(); + let fields = fields + .into_iter() + .map(|(k, v)| { + ( + self.reindex_upvalues(k, mapping), + self.reindex_upvalues(v, mapping), + ) + }) + .collect(); (BoundKind::Record { fields }, node.ty.clone()) } k => (k, node.ty.clone()), }; - Node { identity: node.identity.clone(), kind: new_kind, ty: metrics } + Node { + identity: node.identity.clone(), + kind: new_kind, + ty: metrics, + } } } diff --git a/src/ast/compiler/specializer.rs b/src/ast/compiler/specializer.rs index 1ab53fb..197fe39 100644 --- a/src/ast/compiler/specializer.rs +++ b/src/ast/compiler/specializer.rs @@ -16,7 +16,9 @@ pub type RtlLookupFunc = Rc Option<(Value, Static pub trait FunctionRegistry { fn resolve(&self, addr: Address) -> Option; - fn resolve_analyzed(&self, _addr: Address) -> Option { None } + fn resolve_analyzed(&self, _addr: Address) -> Option { + None + } } pub type MonoCache = HashMap; @@ -52,7 +54,7 @@ impl Specializer { BoundKind::Call { callee, args } => { let (new_callee, new_args, _ret_ty) = self.specialize_call_logic(*callee, *args, node.ty.original.ty.clone()); - + let new_metrics = node.ty.clone(); ( BoundKind::Call { @@ -63,28 +65,76 @@ impl Specializer { ) } - BoundKind::If { cond, then_br, else_br } => { + BoundKind::If { + cond, + then_br, + else_br, + } => { let cond = Box::new(self.visit_node(*cond)); let then_br = Box::new(self.visit_node(*then_br)); let else_br = else_br.map(|e| Box::new(self.visit_node(*e))); - (BoundKind::If { cond, then_br, else_br }, node.ty.clone()) + ( + BoundKind::If { + cond, + then_br, + else_br, + }, + node.ty.clone(), + ) } BoundKind::Block { exprs } => { let exprs = exprs.into_iter().map(|e| self.visit_node(e)).collect(); (BoundKind::Block { exprs }, node.ty.clone()) } - BoundKind::Lambda { params, upvalues, body, positional_count } => { + BoundKind::Lambda { + params, + upvalues, + body, + positional_count, + } => { let params = Rc::new(self.visit_node(params.as_ref().clone())); let body = Rc::new(self.visit_node((*body).clone())); - (BoundKind::Lambda { params, upvalues, body, positional_count }, node.ty.clone()) + ( + BoundKind::Lambda { + params, + upvalues, + body, + positional_count, + }, + node.ty.clone(), + ) } - BoundKind::DefLocal { name, slot, value, captured_by } => { + BoundKind::DefLocal { + name, + slot, + value, + captured_by, + } => { let value = Box::new(self.visit_node(*value)); - (BoundKind::DefLocal { name, slot, value, captured_by }, node.ty.clone()) + ( + BoundKind::DefLocal { + name, + slot, + value, + captured_by, + }, + node.ty.clone(), + ) } - BoundKind::DefGlobal { name, global_index, value } => { + BoundKind::DefGlobal { + name, + global_index, + value, + } => { let value = Box::new(self.visit_node(*value)); - (BoundKind::DefGlobal { name, global_index, value }, node.ty.clone()) + ( + BoundKind::DefGlobal { + name, + global_index, + value, + }, + node.ty.clone(), + ) } BoundKind::Set { addr, value } => { let value = Box::new(self.visit_node(*value)); @@ -101,9 +151,18 @@ impl Specializer { .collect(); (BoundKind::Record { fields }, node.ty.clone()) } - BoundKind::Expansion { original_call, bound_expanded } => { + BoundKind::Expansion { + original_call, + bound_expanded, + } => { let bound_expanded = Box::new(self.visit_node(*bound_expanded)); - (BoundKind::Expansion { original_call, bound_expanded }, node.ty.clone()) + ( + BoundKind::Expansion { + original_call, + bound_expanded, + }, + node.ty.clone(), + ) } k => (k, node.ty.clone()), }; @@ -130,11 +189,12 @@ impl Specializer { return (new_callee, new_args, original_ty); }; - let arg_types: Vec = if let StaticType::Tuple(elements) = &new_args.ty.original.ty { - elements.clone() - } else { - vec![new_args.ty.original.ty.clone()] - }; + let arg_types: Vec = + if let StaticType::Tuple(elements) = &new_args.ty.original.ty { + elements.clone() + } else { + vec![new_args.ty.original.ty.clone()] + }; if arg_types.iter().any(|t| matches!(t, StaticType::Any)) { return (new_callee, new_args, original_ty); @@ -146,10 +206,14 @@ impl Specializer { }; if let Some((val, ret_ty)) = self.cache.borrow().get(&key) { - let specialized_callee = self.make_constant_node(val.clone(), StaticType::Function(Box::new(Signature { - params: StaticType::Tuple(arg_types), - ret: ret_ty.clone(), - })), &new_callee); + let specialized_callee = self.make_constant_node( + val.clone(), + StaticType::Function(Box::new(Signature { + params: StaticType::Tuple(arg_types), + ret: ret_ty.clone(), + })), + &new_callee, + ); return (specialized_callee, new_args, ret_ty.clone()); } @@ -157,11 +221,17 @@ impl Specializer { && let BoundKind::Get { name, .. } = &new_callee.kind && let Some((val, ret_ty)) = rtl_lookup(&name.name, &arg_types) { - self.cache.borrow_mut().insert(key.clone(), (val.clone(), ret_ty.clone())); - let specialized_callee = self.make_constant_node(val.clone(), StaticType::Function(Box::new(Signature { - params: StaticType::Tuple(arg_types), - ret: ret_ty.clone(), - })), &new_callee); + self.cache + .borrow_mut() + .insert(key.clone(), (val.clone(), ret_ty.clone())); + let specialized_callee = self.make_constant_node( + val.clone(), + StaticType::Function(Box::new(Signature { + params: StaticType::Tuple(arg_types), + ret: ret_ty.clone(), + })), + &new_callee, + ); return (specialized_callee, new_args, ret_ty); } @@ -172,20 +242,27 @@ impl Specializer { return (new_callee, new_args, original_ty); } - if let Some(compiler) = &self.compiler + if let Some(compiler) = &self.compiler && let Some(func_node) = self.registry.as_ref().and_then(|r| r.resolve(address)) && let Ok((compiled_val, ret_ty)) = compiler(func_node, &arg_types) { - self.cache.borrow_mut().insert(key, (compiled_val.clone(), ret_ty.clone())); + self.cache + .borrow_mut() + .insert(key, (compiled_val.clone(), ret_ty.clone())); let flat_elements = self.flatten_tuple(new_args.clone()); - let flat_types = flat_elements.iter().map(|e| e.ty.original.ty.clone()).collect(); + let flat_types = flat_elements + .iter() + .map(|e| e.ty.original.ty.clone()) + .collect(); let flattened_args = Node { identity: new_args.identity.clone(), - kind: BoundKind::Tuple { elements: flat_elements }, + kind: BoundKind::Tuple { + elements: flat_elements, + }, ty: NodeMetrics { original: Rc::new(Node { identity: new_args.identity.clone(), - kind: BoundKind::Tuple { elements: vec![] }, + kind: BoundKind::Tuple { elements: vec![] }, ty: StaticType::Tuple(flat_types), }), purity: new_args.ty.purity, @@ -193,17 +270,26 @@ impl Specializer { }, }; - let specialized_callee = self.make_constant_node(compiled_val, StaticType::Function(Box::new(Signature { - params: flattened_args.ty.original.ty.clone(), - ret: ret_ty.clone(), - })), &new_callee); + let specialized_callee = self.make_constant_node( + compiled_val, + StaticType::Function(Box::new(Signature { + params: flattened_args.ty.original.ty.clone(), + ret: ret_ty.clone(), + })), + &new_callee, + ); return (specialized_callee, flattened_args, ret_ty); } (new_callee, new_args, original_ty) } - fn make_constant_node(&self, val: Value, ty: StaticType, template: &AnalyzedNode) -> AnalyzedNode { + fn make_constant_node( + &self, + val: Value, + ty: StaticType, + template: &AnalyzedNode, + ) -> AnalyzedNode { let typed_original = Rc::new(Node { identity: template.identity.clone(), kind: BoundKind::Constant(val.clone()), diff --git a/src/ast/compiler/tco.rs b/src/ast/compiler/tco.rs index e67a47a..3dfdf7c 100644 --- a/src/ast/compiler/tco.rs +++ b/src/ast/compiler/tco.rs @@ -1,8 +1,8 @@ use crate::ast::compiler::bound_nodes::{AnalyzedNode, BoundKind}; use crate::ast::nodes::Node; use crate::ast::types::StaticType; -use std::rc::Rc; use std::fmt::Debug; +use std::rc::Rc; #[derive(Clone)] pub struct RuntimeMetadata { diff --git a/src/ast/environment.rs b/src/ast/environment.rs index 946306e..46c7c42 100644 --- a/src/ast/environment.rs +++ b/src/ast/environment.rs @@ -198,7 +198,7 @@ impl Environment { pub fn link(&self, node: TypedNode) -> ExecNode { // 1. Analyze let analyzed = Analyzer::analyze(&node, &self.global_purity.borrow()); - + // 2. Collect Analyzed Lambdas LambdaCollector::collect(&analyzed, &mut self.typed_function_registry.borrow_mut()); @@ -218,13 +218,18 @@ impl Environment { pub fn instantiate(&self, node: ExecNode) -> Rc { let global_values = self.global_values.clone(); - if let BoundKind::Lambda { params, upvalues, body, positional_count } = &node.kind + if let BoundKind::Lambda { + params, + upvalues, + body, + positional_count, + } = &node.kind && upvalues.is_empty() { let closure = Rc::new(crate::ast::vm::Closure::new( - params.ty.original.clone(), - body.ty.original.clone(), - body.clone(), + params.ty.original.clone(), + body.ty.original.clone(), + body.clone(), vec![], *positional_count, )); @@ -294,7 +299,8 @@ impl Environment { registry: untyped_reg.clone(), analyzed_registry: typed_reg.clone(), }); - let sub_rtl_lookup = Rc::new(|name: &str, args: &[StaticType]| intrinsics::lookup(name, args)); + let sub_rtl_lookup = + Rc::new(|name: &str, args: &[StaticType]| intrinsics::lookup(name, args)); let sub_specializer = Specializer::new( Some(sub_registry), @@ -335,7 +341,9 @@ impl Environment { pub fn run_script(&self, source: &str) -> Result { if self.debug_mode { let (res, logs) = self.run_debug(source)?; - for line in logs { println!("{}", line); } + for line in logs { + println!("{}", line); + } res } else { let compiled = self.compile(source)?; @@ -363,7 +371,10 @@ impl Environment { if let Some(closure) = next_obj.as_any().downcast_ref::() { result = vm.run_with_args_observed(&mut observer, closure, next_args); } else { - result = Err(format!("Tail call target is not a closure: {}", next_obj.type_name())); + result = Err(format!( + "Tail call target is not a closure: {}", + next_obj.type_name() + )); break; } } diff --git a/src/ast/vm.rs b/src/ast/vm.rs index 1618a4b..64dcb9e 100644 --- a/src/ast/vm.rs +++ b/src/ast/vm.rs @@ -262,11 +262,7 @@ impl VM { } #[inline(always)] - fn eval_core( - &mut self, - obs: &mut O, - node: &ExecNode, - ) -> Result { + fn eval_core(&mut self, obs: &mut O, node: &ExecNode) -> Result { match &node.kind { BoundKind::Nop => Ok(Value::Void), BoundKind::Constant(v) => Ok(v.clone()), @@ -380,14 +376,11 @@ impl VM { if node.ty.is_tail { match func_val { Value::Object(obj) => { - return Ok(Value::TailCallRequest(Box::new((obj, arg_vals)))) + return Ok(Value::TailCallRequest(Box::new((obj, arg_vals)))); } Value::Function(f) => return Ok((f.func)(arg_vals)), _ => { - return Err(format!( - "Tail call target is not a function: {}", - func_val - )) + return Err(format!("Tail call target is not a function: {}", func_val)); } } } @@ -423,10 +416,7 @@ impl VM { res => break res, } } else { - break Err(format!( - "Object is not a closure: {}", - obj.type_name() - )); + break Err(format!("Object is not a closure: {}", obj.type_name())); } } _ => break Err(format!("Attempt to call non-function: {}", func_val)), @@ -494,7 +484,6 @@ impl VM { } } - pub fn resolve_tail_calls(&mut self, mut result: Value) -> Value { while let Value::TailCallRequest(payload) = result { let (next_obj, next_args) = *payload; diff --git a/src/integration_test.rs b/src/integration_test.rs index 9c6cc3d..6141393 100644 --- a/src/integration_test.rs +++ b/src/integration_test.rs @@ -291,17 +291,25 @@ mod tests { #[test] fn test_nested_destructuring_optimization() { let env = Environment::new(); - + // 1. Tuple-to-Tuple let source_tuple = "((fn [[x y]] (+ x y)) [10 20])"; assert_eq!(format!("{}", env.run_script(source_tuple).unwrap()), "30"); let dump_tuple = env.dump_ast(source_tuple).unwrap(); - assert!(dump_tuple.contains("Constant: 30"), "Nested tuple should be folded to 30. Dump:\n{}", dump_tuple); + assert!( + dump_tuple.contains("Constant: 30"), + "Nested tuple should be folded to 30. Dump:\n{}", + dump_tuple + ); // 2. Record-to-Tuple let source_record = "((fn [[x y]] (+ x y)) {:a 5 :b 7})"; assert_eq!(format!("{}", env.run_script(source_record).unwrap()), "12"); let dump_record = env.dump_ast(source_record).unwrap(); - assert!(dump_record.contains("Constant: 12"), "Record-to-Tuple destructuring should be folded to 12. Dump:\n{}", dump_record); + assert!( + dump_record.contains("Constant: 12"), + "Record-to-Tuple destructuring should be folded to 12. Dump:\n{}", + dump_record + ); } }