From e7628e5cdfb5b82d44625e70050d986820296ad2 Mon Sep 17 00:00:00 2001 From: Michael Schimmel Date: Thu, 19 Feb 2026 16:25:17 +0100 Subject: [PATCH] Add symbol name to bound kinds The `Get`, `DefLocal`, and `DefGlobal` bound kinds now store the symbol name associated with the variable. This information is useful for debugging and provides more context in the compiled output. --- src/ast/compiler/binder.rs | 7 ++++++- src/ast/compiler/bound_nodes.rs | 15 ++++++++++----- src/ast/compiler/dumper.rs | 10 +++++----- src/ast/compiler/specializer.rs | 25 +++++++++++++++---------- src/ast/compiler/tco.rs | 8 ++++---- src/ast/compiler/type_checker.rs | 12 ++++++------ src/ast/vm.rs | 12 ++++++------ 7 files changed, 52 insertions(+), 37 deletions(-) diff --git a/src/ast/compiler/binder.rs b/src/ast/compiler/binder.rs index eff69cc..87d8251 100644 --- a/src/ast/compiler/binder.rs +++ b/src/ast/compiler/binder.rs @@ -104,7 +104,10 @@ impl Binder { UntypedKind::Identifier(sym) => { let addr = self.resolve_variable(sym)?; - Ok(self.make_node(node.identity.clone(), BoundKind::Get(addr))) + Ok(self.make_node(node.identity.clone(), BoundKind::Get { + addr, + name: sym.clone() + })) }, UntypedKind::If { cond, then_br, else_br } => { @@ -143,12 +146,14 @@ impl Binder { // 3. Return Node if self.functions.len() == 1 { Ok(self.make_node(node.identity.clone(), BoundKind::DefGlobal { + name: name.clone(), global_index: slot_or_idx, value: Box::new(val_node) })) } else { let captured_by = self.capture_map.get(&node.identity).cloned().unwrap_or_default(); Ok(self.make_node(node.identity.clone(), BoundKind::DefLocal { + name: name.clone(), slot: slot_or_idx, value: Box::new(val_node) , captured_by diff --git a/src/ast/compiler/bound_nodes.rs b/src/ast/compiler/bound_nodes.rs index b1c83d8..0309b5b 100644 --- a/src/ast/compiler/bound_nodes.rs +++ b/src/ast/compiler/bound_nodes.rs @@ -1,6 +1,6 @@ use std::rc::Rc; use crate::ast::types::{Value, StaticType, Identity}; -use crate::ast::nodes::Node; +use crate::ast::nodes::{Node, Symbol}; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Address { @@ -27,7 +27,10 @@ pub enum BoundKind { Constant(Value), // Variable Access (Resolved) - Get(Address), + Get { + addr: Address, + name: Symbol, + }, // Variable Update (Assignment) Set { @@ -37,6 +40,7 @@ pub enum BoundKind { // Variable Declaration (Local) DefLocal { + name: Symbol, slot: u32, value: Box, T>>, captured_by: Vec, @@ -50,6 +54,7 @@ pub enum BoundKind { // Global Definition (Locals are implicit by stack position) DefGlobal { + name: Symbol, global_index: u32, value: Box, T>>, }, @@ -109,11 +114,11 @@ impl BoundKind { match self { BoundKind::Nop => "NOP".to_string(), BoundKind::Constant(v) => format!("CONST({})", v), - BoundKind::Get(addr) => format!("GET({:?})", addr), + BoundKind::Get { addr, name } => format!("GET({}, {:?})", name.name, addr), BoundKind::Set { addr, .. } => format!("SET({:?})", addr), - BoundKind::DefLocal { slot, .. } => format!("DEF_LOCAL(Slot:{})", slot), + BoundKind::DefLocal { name, slot, .. } => format!("DEF_LOCAL({}, Slot:{})", name.name, slot), BoundKind::If { .. } => "IF".to_string(), - BoundKind::DefGlobal { global_index, .. } => format!("DEF_GLOBAL(Idx:{})", global_index), + BoundKind::DefGlobal { name, global_index, .. } => format!("DEF_GLOBAL({}, Idx:{})", name.name, global_index), BoundKind::Lambda { upvalues, .. } => format!("LAMBDA(Captures:{})", upvalues.len()), BoundKind::Call { .. } => "CALL".to_string(), BoundKind::TailCall { .. } => "T-CALL".to_string(), diff --git a/src/ast/compiler/dumper.rs b/src/ast/compiler/dumper.rs index 3c3d484..84efaed 100644 --- a/src/ast/compiler/dumper.rs +++ b/src/ast/compiler/dumper.rs @@ -35,7 +35,7 @@ impl Dumper { match &node.kind { BoundKind::Nop => self.log("Nop", node), BoundKind::Constant(v) => self.log(&format!("Constant: {}", v), node), - BoundKind::Get(addr) => self.log(&format!("Get: {:?}", addr), node), + BoundKind::Get { addr, name } => self.log(&format!("Get: {} ({:?})", name.name, addr), node), BoundKind::Set { addr, value } => { self.log(&format!("Set: {:?}", addr), node); @@ -44,13 +44,13 @@ impl Dumper { self.indent -= 1; } - BoundKind::DefLocal { slot, value, captured_by } => { + BoundKind::DefLocal { name, slot, value, captured_by } => { let capture_info = if captured_by.is_empty() { String::from("not captured") } else { format!("captured by {} lambdas", captured_by.len()) }; - self.log(&format!("DefLocal (Slot: {}, {})", slot, capture_info), node); + self.log(&format!("DefLocal (Name: '{}', Slot: {}, {})", name.name, slot, capture_info), node); self.indent += 1; if !captured_by.is_empty() { @@ -64,8 +64,8 @@ impl Dumper { self.indent -= 1; } - BoundKind::DefGlobal { global_index, value } => { - self.log(&format!("DefGlobal (Index: {})", global_index), node); + BoundKind::DefGlobal { name, global_index, value } => { + self.log(&format!("DefGlobal (Name: '{}', Index: {})", name.name, global_index), node); self.indent += 1; self.visit(value); self.indent -= 1; diff --git a/src/ast/compiler/specializer.rs b/src/ast/compiler/specializer.rs index c1e06da..2440a29 100644 --- a/src/ast/compiler/specializer.rs +++ b/src/ast/compiler/specializer.rs @@ -69,13 +69,13 @@ impl Specializer { let body = Rc::new(self.visit_node((*body).clone())); (BoundKind::Lambda { param_count, upvalues, body }, node.ty) }, - BoundKind::DefLocal { slot, value, captured_by } => { + BoundKind::DefLocal { name, slot, value, captured_by } => { let value = Box::new(self.visit_node(*value)); - (BoundKind::DefLocal { slot, value, captured_by }, node.ty) + (BoundKind::DefLocal { name, slot, value, captured_by }, node.ty) }, - BoundKind::DefGlobal { global_index, value } => { + BoundKind::DefGlobal { name, global_index, value } => { let value = Box::new(self.visit_node(*value)); - (BoundKind::DefGlobal { global_index, value }, node.ty) + (BoundKind::DefGlobal { name, global_index, value }, node.ty) }, BoundKind::Set { addr, value } => { let value = Box::new(self.visit_node(*value)); @@ -111,7 +111,7 @@ impl Specializer { let new_args: Vec = args.into_iter().map(|a| self.visit_node(a)).collect(); // 2. Check if this call is a candidate (Callee is Get(Address)) - let address = if let BoundKind::Get(addr) = &new_callee.kind { + let address = if let BoundKind::Get { addr, .. } = &new_callee.kind { *addr } else { // Not a direct call to a named function/variable @@ -195,6 +195,7 @@ mod tests { use super::*; use crate::ast::types::{Identity, NodeIdentity, SourceLocation, StaticType, Value, Signature}; use crate::ast::compiler::bound_nodes::{BoundKind, Address, BoundNode, TypedNode}; + use crate::ast::nodes::Symbol; use std::rc::Rc; fn make_identity() -> Identity { @@ -235,6 +236,7 @@ mod tests { // Setup Registry with a function definition let mut registry = MockRegistry::new(); let addr = Address::Local(0); + let name = Symbol::from("test_func"); // Def: (fn [x] x) -- generic identity let func_node = BoundNode { @@ -257,7 +259,7 @@ mod tests { let spec = Specializer::new(Some(Rc::new(registry)), Some(compiler), None); // Call(Get(Local(0)), [Arg(Int)]) - let callee = make_typed_node(BoundKind::Get(addr), StaticType::Any); + let callee = make_typed_node(BoundKind::Get { addr, name: name.clone() }, StaticType::Any); let arg = make_typed_node(BoundKind::Constant(Value::Int(1)), StaticType::Int); let call_node = make_typed_node( @@ -285,10 +287,12 @@ mod tests { #[test] fn test_specialize_skips_unknown_types() { let spec = Specializer::new(None, None, None); + let name0 = Symbol::from("f"); + let name1 = Symbol::from("x"); // Call(Get(Local(0)), [Get(Local(1))]) where arg is Any - let callee = make_typed_node(BoundKind::Get(Address::Local(0)), StaticType::Function(Box::new(Signature { params: vec![StaticType::Any], ret: StaticType::Void }))); - let arg = make_typed_node(BoundKind::Get(Address::Local(1)), StaticType::Any); + let callee = make_typed_node(BoundKind::Get { addr: Address::Local(0), name: name0 }, StaticType::Function(Box::new(Signature { params: vec![StaticType::Any], ret: StaticType::Void }))); + let arg = make_typed_node(BoundKind::Get { addr: Address::Local(1), name: name1 }, StaticType::Any); let call_node = make_typed_node( BoundKind::Call { callee: Box::new(callee), args: vec![arg] }, @@ -299,7 +303,7 @@ mod tests { // Should remain a generic Call because arg type is Any if let BoundKind::Call { callee, .. } = result.kind { - if let BoundKind::Get(_) = callee.kind { + if let BoundKind::Get { .. } = callee.kind { // Correct: Still a Get, not a Constant(Function) } else { panic!("Expected generic Call to Get, got {:?}", callee.kind); @@ -315,6 +319,7 @@ mod tests { let spec = Specializer::new(None, None, None); let addr = Address::Local(0); + let name = Symbol::from("cached_func"); let arg_types = vec![StaticType::Int]; let key = MonoCacheKey { address: addr, arg_types: arg_types.clone() }; @@ -325,7 +330,7 @@ mod tests { spec.cache.borrow_mut().insert(key, (specialized_val.clone(), ret_ty.clone())); // Create the call node: Call(Get(0), [Arg(Int)]) - let callee = make_typed_node(BoundKind::Get(addr), StaticType::Any); + let callee = make_typed_node(BoundKind::Get { addr, name }, StaticType::Any); let arg = make_typed_node(BoundKind::Constant(Value::Int(1)), StaticType::Int); let call_node = make_typed_node( diff --git a/src/ast/compiler/tco.rs b/src/ast/compiler/tco.rs index 644bf91..517759b 100644 --- a/src/ast/compiler/tco.rs +++ b/src/ast/compiler/tco.rs @@ -95,15 +95,15 @@ impl TCO { ..node } }, - BoundKind::DefLocal { slot, value, captured_by } => { + BoundKind::DefLocal { name, slot, value, captured_by } => { Node { - kind: BoundKind::DefLocal { slot, value: Box::new(Self::transform(*value, false)), captured_by }, + kind: BoundKind::DefLocal { name, slot, value: Box::new(Self::transform(*value, false)), captured_by }, ..node } }, - BoundKind::DefGlobal { global_index, value } => { + BoundKind::DefGlobal { name, global_index, value } => { Node { - kind: BoundKind::DefGlobal { global_index, value: Box::new(Self::transform(*value, false)) }, + kind: BoundKind::DefGlobal { name, global_index, value: Box::new(Self::transform(*value, false)) }, ..node } }, diff --git a/src/ast/compiler/type_checker.rs b/src/ast/compiler/type_checker.rs index 8e3a4b0..49bf810 100644 --- a/src/ast/compiler/type_checker.rs +++ b/src/ast/compiler/type_checker.rs @@ -63,13 +63,13 @@ impl TypeChecker { (BoundKind::Constant(v), ty) }, - BoundKind::Get(addr) => { + BoundKind::Get { addr, name } => { let ty = if let Address::Global(idx) = addr { self.global_types.borrow().get(&idx).cloned().unwrap_or(StaticType::Any) } else { ctx.get_type(addr) }; - (BoundKind::Get(addr), ty) + (BoundKind::Get { addr, name }, ty) }, BoundKind::Set { addr, value } => { @@ -85,20 +85,20 @@ impl TypeChecker { (BoundKind::Set { addr, value: Box::new(val_typed) }, ty) }, - BoundKind::DefLocal { slot, value, captured_by } => { + BoundKind::DefLocal { name, slot, value, captured_by } => { let val_typed = self.check_node(*value, ctx)?; let ty = val_typed.ty.clone(); ctx.set_local_type(slot, ty.clone()); - (BoundKind::DefLocal { slot, value: Box::new(val_typed), captured_by }, ty) + (BoundKind::DefLocal { name, slot, value: Box::new(val_typed), captured_by }, ty) }, - BoundKind::DefGlobal { global_index, value } => { + BoundKind::DefGlobal { name, global_index, value } => { let val_typed = self.check_node(*value, ctx)?; let ty = val_typed.ty.clone(); self.global_types.borrow_mut().insert(global_index, ty.clone()); - (BoundKind::DefGlobal { global_index, value: Box::new(val_typed) }, ty) + (BoundKind::DefGlobal { name, global_index, value: Box::new(val_typed) }, ty) }, BoundKind::If { cond, then_br, else_br } => { diff --git a/src/ast/vm.rs b/src/ast/vm.rs index 8f6f22f..4826106 100644 --- a/src/ast/vm.rs +++ b/src/ast/vm.rs @@ -102,7 +102,7 @@ macro_rules! dispatch_eval { BoundKind::Nop => Ok(Value::Void), BoundKind::Constant(v) => Ok(v.clone()), - BoundKind::DefGlobal { global_index, value } => { + BoundKind::DefGlobal { global_index, value, .. } => { let val = $self.$eval_method($($observer,)? value)?; let idx = *global_index as usize; let mut globals = $self.globals.borrow_mut(); @@ -113,7 +113,7 @@ macro_rules! dispatch_eval { Ok(val) }, - BoundKind::Get(addr) => $self.get_value(*addr), + BoundKind::Get { addr, .. } => $self.get_value(*addr), BoundKind::Set { addr, value } => { let val = $self.$eval_method($($observer,)? value)?; @@ -121,7 +121,7 @@ macro_rules! dispatch_eval { Ok(val) }, - BoundKind::DefLocal { slot, value, captured_by } => { + BoundKind::DefLocal { slot, value, captured_by, .. } => { let val = $self.$eval_method($($observer,)? value)?; let final_val = if !captured_by.is_empty() { Value::Cell(Rc::new(RefCell::new(val))) @@ -454,7 +454,7 @@ impl VM { #[cfg(test)] mod tests { use super::*; - use crate::ast::nodes::Node; + use crate::ast::nodes::{Node, Symbol}; use crate::ast::types::{SourceLocation, NodeIdentity, StaticType}; fn make_dummy_identity() -> Rc { @@ -531,7 +531,7 @@ mod tests { callee: Box::new(Node { identity: id.clone(), ty: StaticType::Any, - kind: BoundKind::Get(Address::Local(1)), + kind: BoundKind::Get { addr: Address::Local(1), name: Symbol::from("f") }, }), args: vec![], }, @@ -540,7 +540,7 @@ mod tests { Node { identity: id.clone(), ty: StaticType::Int, - kind: BoundKind::Get(Address::Local(0)), + kind: BoundKind::Get { addr: Address::Local(0), name: Symbol::from("x") }, }, ], },