Refactor bound node types for clarity

Introduce generic `CompilerPhase` trait to unify different stages of the
bound AST. Rename `LocalSlot` to `VirtualId` and introduce `StackOffset`
for clearer distinction between compile-time and run-time addressing.
Update type aliases and implementations to reflect these changes.
This commit is contained in:
Michael Schimmel
2026-03-13 19:20:44 +01:00
parent d035da9d91
commit e5d82ee2b6
15 changed files with 403 additions and 458 deletions
+31 -24
View File
@@ -10,12 +10,12 @@ use std::path::{Path, PathBuf};
use std::rc::Rc;
use crate::ast::compiler::bound_nodes::{
Address, AnalyzedNode, BoundKind, GlobalAnalyzedRegistry, GlobalFunctionRegistry, GlobalIdx,
Node,
Address, AnalyzedNode, BoundKind, ExecNode, GlobalAnalyzedRegistry, GlobalFunctionRegistry, GlobalIdx,
Node, VirtualId,
};
use crate::ast::compiler::dumper::Dumper;
use crate::ast::compiler::lambda_collector::LambdaCollector;
use crate::ast::compiler::lowering::{Lowering, ExecNode};
use crate::ast::compiler::lowering::Lowering;
use crate::ast::compiler::macros::{MacroEvaluator, MacroExpander, MacroRegistry};
use crate::ast::compiler::optimizer::Optimizer;
use crate::ast::compiler::specializer::{FunctionRegistry, MonoCache, Specializer};
@@ -89,19 +89,11 @@ pub struct Environment {
}
struct EnvFunctionRegistry {
registry: Rc<RefCell<GlobalFunctionRegistry>>,
analyzed_registry: Rc<RefCell<GlobalAnalyzedRegistry>>,
}
impl FunctionRegistry for EnvFunctionRegistry {
fn resolve(&self, addr: Address) -> Option<Rc<Node>> {
if let Address::Global(idx) = addr {
self.registry.borrow().get(&idx).cloned()
} else {
None
}
}
fn resolve_analyzed(&self, addr: Address) -> Option<Rc<AnalyzedNode>> {
fn resolve(&self, addr: Address<VirtualId>) -> Option<Rc<Node<crate::ast::compiler::bound_nodes::AnalyzedPhase>>> {
if let Address::Global(idx) = addr {
self.analyzed_registry.borrow().get(&idx).cloned()
} else {
@@ -406,7 +398,7 @@ impl Environment {
if !current_scope.locals.contains_key(sym) {
let mut slot_count = self.root_slot_count.borrow_mut();
let slot = crate::ast::compiler::bound_nodes::LocalSlot(*slot_count);
let slot = VirtualId(*slot_count);
current_scope.locals.insert(
sym.clone(),
crate::ast::compiler::binder::LocalInfo {
@@ -484,15 +476,15 @@ impl Environment {
LambdaCollector::collect(&bound_ast, &mut self.function_registry.borrow_mut());
let checker = TypeChecker::new(self.root_types.clone());
let wrapped_ast = if let crate::ast::compiler::bound_nodes::BoundKind::Lambda { .. } = bound_ast.kind {
let wrapped_ast = if let BoundKind::Lambda { .. } = bound_ast.kind {
bound_ast
} else {
crate::ast::compiler::bound_nodes::Node {
Node {
identity: bound_ast.identity.clone(),
kind: crate::ast::compiler::bound_nodes::BoundKind::Lambda {
params: std::rc::Rc::new(crate::ast::compiler::bound_nodes::Node {
kind: BoundKind::Lambda {
params: std::rc::Rc::new(Node {
identity: bound_ast.identity.clone(),
kind: crate::ast::compiler::bound_nodes::BoundKind::Tuple { elements: vec![] },
kind: BoundKind::Tuple { elements: vec![] },
ty: (),
}),
upvalues: vec![],
@@ -668,7 +660,7 @@ impl Environment {
{
let closure = Rc::new(crate::ast::vm::Closure::new(
params.clone(),
body.ty.original.clone(),
body.ty.original.clone(),
body.clone(),
Vec::new(),
*positional_count,
@@ -717,13 +709,11 @@ impl Environment {
fn specialize_node(&self, node: AnalyzedNode) -> AnalyzedNode {
let registry = Rc::new(EnvFunctionRegistry {
registry: self.function_registry.clone(),
analyzed_registry: self.typed_function_registry.clone(),
});
let rtl_lookup = Rc::new(|name: &str, args: &[StaticType]| intrinsics::lookup(name, args));
let typed_reg = self.typed_function_registry.clone();
let syntax_reg = self.function_registry.clone();
let mono_cache = self.monomorph_cache.clone();
let root_values = self.root_values.clone();
let root_types = self.root_types.clone();
@@ -731,12 +721,30 @@ impl Environment {
let optimization = self.optimization;
let compiler = Rc::new(
move |func_template: Rc<Node>,
move |func_template: Rc<Node<crate::ast::compiler::bound_nodes::AnalyzedPhase>>,
arg_types: &[StaticType]|
-> Result<(Value, StaticType), String> {
let mut diag = Diagnostics::new();
let checker = TypeChecker::new(root_types.clone());
let retyped_ast = checker.check(func_template.as_ref(), arg_types, &mut diag);
// For specialization, we re-type-check the analyzed node.
// Note: AnalyzedNode.ty.original is the TypedNode.
// But TypeChecker expects Node<BoundPhase>. We need to go from TypedNode -> BoundNode.
// However, since TypedNode is just Node<TypedPhase>, we can't easily "un-type" it.
// Instead, we access the original AST from the binder if we had it,
// OR we make the TypeChecker generic.
// For now, we assume the TypedNode can be used where a BoundNode is expected
// if we strip the metadata.
// A better way is to store the BoundNode in NodeMetrics as well.
// Temporary fix: Re-binding from source would be too expensive.
// Let's assume for now that we can specialize directly on the TypedNode
// or that we need a small helper to transform TypedNode -> BoundNode.
// Realizing that TypedNode (StaticType) is very similar to BoundNode (()),
// we can just use the internal transform.
let retyped_ast = checker.check_node_as_bound(func_template.ty.original.as_ref(), arg_types, &mut diag);
if diag.has_errors() {
return Err(diag
@@ -750,7 +758,6 @@ impl Environment {
let analyzed = Analyzer::analyze(&retyped_ast, &root_purity.borrow());
let sub_registry = Rc::new(EnvFunctionRegistry {
registry: syntax_reg.clone(),
analyzed_registry: typed_reg.clone(),
});
let sub_rtl_lookup =