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.
This commit is contained in:
Michael Schimmel
2026-02-19 16:25:17 +01:00
parent 98668cc683
commit e7628e5cdf
7 changed files with 52 additions and 37 deletions
+6 -1
View File
@@ -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
+10 -5
View File
@@ -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<T = ()> {
Constant(Value),
// Variable Access (Resolved)
Get(Address),
Get {
addr: Address,
name: Symbol,
},
// Variable Update (Assignment)
Set {
@@ -37,6 +40,7 @@ pub enum BoundKind<T = ()> {
// Variable Declaration (Local)
DefLocal {
name: Symbol,
slot: u32,
value: Box<Node<BoundKind<T>, T>>,
captured_by: Vec<Identity>,
@@ -50,6 +54,7 @@ pub enum BoundKind<T = ()> {
// Global Definition (Locals are implicit by stack position)
DefGlobal {
name: Symbol,
global_index: u32,
value: Box<Node<BoundKind<T>, T>>,
},
@@ -109,11 +114,11 @@ impl<T> BoundKind<T> {
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(),
+5 -5
View File
@@ -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;
+15 -10
View File
@@ -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<TypedNode> = 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(
+4 -4
View File
@@ -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
}
},
+6 -6
View File
@@ -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 } => {
+6 -6
View File
@@ -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<NodeIdentity> {
@@ -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") },
},
],
},