Refactor: Remove explicit upvalue analysis pass
The `UpvalueAnalyzer` pass is no longer necessary as the binder now directly tracks captures. This commit removes the `UpvalueAnalyzer` struct and associated logic from `src/ast/compiler/upvalues.rs`. The `Binder::bind_root` function now returns the `captures` map, which is then processed by a new `CapturePass` in `src/ast/environment.rs` before type checking. This consolidates capture logic within the binder and its subsequent processing steps.
This commit is contained in:
+58
-54
@@ -8,6 +8,7 @@ use std::rc::Rc;
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct LocalInfo {
|
struct LocalInfo {
|
||||||
slot: u32,
|
slot: u32,
|
||||||
|
identity: Identity,
|
||||||
// Note: Binder doesn't strictly need the type anymore,
|
// Note: Binder doesn't strictly need the type anymore,
|
||||||
// but it might be useful for built-ins during resolution.
|
// but it might be useful for built-ins during resolution.
|
||||||
// For now we keep it as Any or Unknown.
|
// For now we keep it as Any or Unknown.
|
||||||
@@ -28,7 +29,7 @@ impl CompilerScope {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn define(&mut self, sym: &Symbol) -> Result<u32, String> {
|
fn define(&mut self, sym: &Symbol, identity: Identity) -> Result<u32, String> {
|
||||||
if self.locals.contains_key(sym) {
|
if self.locals.contains_key(sym) {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"Variable '{}' is already defined in this scope level.",
|
"Variable '{}' is already defined in this scope level.",
|
||||||
@@ -40,6 +41,7 @@ impl CompilerScope {
|
|||||||
sym.clone(),
|
sym.clone(),
|
||||||
LocalInfo {
|
LocalInfo {
|
||||||
slot,
|
slot,
|
||||||
|
identity,
|
||||||
_ty: StaticType::Any,
|
_ty: StaticType::Any,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -59,14 +61,16 @@ enum ScopeKind {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct FunctionCompiler {
|
struct FunctionCompiler {
|
||||||
|
identity: Identity,
|
||||||
scope: CompilerScope,
|
scope: CompilerScope,
|
||||||
upvalues: Vec<Address>,
|
upvalues: Vec<Address>,
|
||||||
kind: ScopeKind,
|
kind: ScopeKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FunctionCompiler {
|
impl FunctionCompiler {
|
||||||
fn new(kind: ScopeKind) -> Self {
|
fn new(kind: ScopeKind, identity: Identity) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
identity,
|
||||||
scope: CompilerScope::new(),
|
scope: CompilerScope::new(),
|
||||||
upvalues: Vec::new(),
|
upvalues: Vec::new(),
|
||||||
kind,
|
kind,
|
||||||
@@ -76,7 +80,8 @@ impl FunctionCompiler {
|
|||||||
fn define_variable(
|
fn define_variable(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: &Symbol,
|
name: &Symbol,
|
||||||
globals: &Rc<RefCell<HashMap<Symbol, u32>>>,
|
identity: Identity,
|
||||||
|
globals: &Rc<RefCell<HashMap<Symbol, (u32, Identity)>>>,
|
||||||
) -> Result<Address, String> {
|
) -> Result<Address, String> {
|
||||||
match self.kind {
|
match self.kind {
|
||||||
ScopeKind::Root => {
|
ScopeKind::Root => {
|
||||||
@@ -88,11 +93,11 @@ impl FunctionCompiler {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
let idx = globals_map.len() as u32;
|
let idx = globals_map.len() as u32;
|
||||||
globals_map.insert(name.clone(), idx);
|
globals_map.insert(name.clone(), (idx, identity));
|
||||||
Ok(Address::Global(idx))
|
Ok(Address::Global(idx))
|
||||||
}
|
}
|
||||||
ScopeKind::Local => {
|
ScopeKind::Local => {
|
||||||
let slot = self.scope.define(name)?;
|
let slot = self.scope.define(name, identity)?;
|
||||||
Ok(Address::Local(slot))
|
Ok(Address::Local(slot))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -110,48 +115,53 @@ impl FunctionCompiler {
|
|||||||
|
|
||||||
pub struct Binder {
|
pub struct Binder {
|
||||||
functions: Vec<FunctionCompiler>,
|
functions: Vec<FunctionCompiler>,
|
||||||
// Globals mapping: Symbol -> Index
|
// Globals mapping: Symbol -> (Index, DefinitionIdentity)
|
||||||
globals: Rc<RefCell<HashMap<Symbol, u32>>>,
|
globals: Rc<RefCell<HashMap<Symbol, (u32, Identity)>>>,
|
||||||
// Map of Declaration Identity -> List of Lambda Identities that capture it
|
// Map of Declaration Identity -> List of Lambda Identities that capture it
|
||||||
capture_map: HashMap<Identity, Vec<Identity>>,
|
capture_map: HashMap<Identity, std::collections::HashSet<Identity>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Binder {
|
impl Binder {
|
||||||
pub fn new(globals: Rc<RefCell<HashMap<Symbol, u32>>>) -> Self {
|
pub fn new(globals: Rc<RefCell<HashMap<Symbol, (u32, Identity)>>>) -> Self {
|
||||||
Self::with_boxed(globals, HashMap::new())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn with_boxed(
|
|
||||||
globals: Rc<RefCell<HashMap<Symbol, u32>>>,
|
|
||||||
captures: HashMap<Identity, Vec<Identity>>,
|
|
||||||
) -> Self {
|
|
||||||
let mut binder = Self {
|
let mut binder = Self {
|
||||||
functions: Vec::new(),
|
functions: Vec::new(),
|
||||||
globals,
|
globals,
|
||||||
capture_map: captures,
|
capture_map: HashMap::new(),
|
||||||
};
|
};
|
||||||
binder
|
binder.functions.push(FunctionCompiler::new(
|
||||||
.functions
|
ScopeKind::Root,
|
||||||
.push(FunctionCompiler::new(ScopeKind::Root));
|
Rc::new(crate::ast::types::NodeIdentity {
|
||||||
|
location: crate::ast::types::SourceLocation { line: 0, col: 0 },
|
||||||
|
}),
|
||||||
|
));
|
||||||
binder
|
binder
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bind_root(
|
pub fn bind_root(
|
||||||
globals: Rc<RefCell<HashMap<Symbol, u32>>>,
|
globals: Rc<RefCell<HashMap<Symbol, (u32, Identity)>>>,
|
||||||
node: &Node<UntypedKind>,
|
node: &Node<UntypedKind>,
|
||||||
) -> Result<BoundNode, String> {
|
) -> Result<(BoundNode, HashMap<Identity, Vec<Identity>>), String> {
|
||||||
let captures = crate::ast::compiler::upvalues::UpvalueAnalyzer::analyze(node);
|
let mut binder = Self::new(globals);
|
||||||
let mut binder = Self::with_boxed(globals, captures);
|
let bound = binder.bind(node)?;
|
||||||
binder.bind(node)
|
|
||||||
|
// Convert HashSet to sorted Vec
|
||||||
|
let final_captures = binder
|
||||||
|
.capture_map
|
||||||
|
.into_iter()
|
||||||
|
.map(|(k, v)| (k, v.into_iter().collect()))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Ok((bound, final_captures))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn declare_variable(
|
fn declare_variable(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: &Symbol,
|
name: &Symbol,
|
||||||
|
identity: Identity,
|
||||||
_kind: crate::ast::compiler::bound_nodes::DeclarationKind,
|
_kind: crate::ast::compiler::bound_nodes::DeclarationKind,
|
||||||
) -> Result<Address, String> {
|
) -> Result<Address, String> {
|
||||||
let current_fn = self.functions.last_mut().unwrap();
|
let current_fn = self.functions.last_mut().unwrap();
|
||||||
current_fn.define_variable(name, &self.globals)
|
current_fn.define_variable(name, identity, &self.globals)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bind(&mut self, node: &Node<UntypedKind>) -> Result<BoundNode, String> {
|
pub fn bind(&mut self, node: &Node<UntypedKind>) -> Result<BoundNode, String> {
|
||||||
@@ -203,14 +213,10 @@ impl Binder {
|
|||||||
if let UntypedKind::Parameter(ref name) = target.kind {
|
if let UntypedKind::Parameter(ref name) = target.kind {
|
||||||
let addr = self.declare_variable(
|
let addr = self.declare_variable(
|
||||||
name,
|
name,
|
||||||
|
node.identity.clone(), // Identity of the Def node
|
||||||
crate::ast::compiler::bound_nodes::DeclarationKind::Variable,
|
crate::ast::compiler::bound_nodes::DeclarationKind::Variable,
|
||||||
)?;
|
)?;
|
||||||
let val_node = self.bind(value)?;
|
let val_node = self.bind(value)?;
|
||||||
let captured_by = self
|
|
||||||
.capture_map
|
|
||||||
.get(&target.identity)
|
|
||||||
.cloned()
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
Ok(self.make_node(
|
Ok(self.make_node(
|
||||||
node.identity.clone(),
|
node.identity.clone(),
|
||||||
@@ -219,7 +225,7 @@ impl Binder {
|
|||||||
addr,
|
addr,
|
||||||
kind: crate::ast::compiler::bound_nodes::DeclarationKind::Variable,
|
kind: crate::ast::compiler::bound_nodes::DeclarationKind::Variable,
|
||||||
value: Box::new(val_node),
|
value: Box::new(val_node),
|
||||||
captured_by,
|
captured_by: Vec::new(), // Will be filled by post-pass
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
@@ -265,7 +271,10 @@ impl Binder {
|
|||||||
|
|
||||||
UntypedKind::Lambda { params, body } => {
|
UntypedKind::Lambda { params, body } => {
|
||||||
let identity = node.identity.clone();
|
let identity = node.identity.clone();
|
||||||
self.functions.push(FunctionCompiler::new(ScopeKind::Local));
|
self.functions.push(FunctionCompiler::new(
|
||||||
|
ScopeKind::Local,
|
||||||
|
identity.clone(),
|
||||||
|
));
|
||||||
|
|
||||||
// 1. Bind the parameter pattern/tuple
|
// 1. Bind the parameter pattern/tuple
|
||||||
let params_bound = self.bind_pattern(params, DeclarationKind::Parameter)?;
|
let params_bound = self.bind_pattern(params, DeclarationKind::Parameter)?;
|
||||||
@@ -409,7 +418,13 @@ impl Binder {
|
|||||||
if let Some(info) = self.functions[i].scope.resolve(sym) {
|
if let Some(info) = self.functions[i].scope.resolve(sym) {
|
||||||
let mut addr = Address::Local(info.slot);
|
let mut addr = Address::Local(info.slot);
|
||||||
|
|
||||||
|
// Record the capture for each lambda level in between
|
||||||
for k in (i + 1)..=current_fn_idx {
|
for k in (i + 1)..=current_fn_idx {
|
||||||
|
let lambda_id = self.functions[k].identity.clone();
|
||||||
|
self.capture_map
|
||||||
|
.entry(info.identity.clone())
|
||||||
|
.or_default()
|
||||||
|
.insert(lambda_id);
|
||||||
addr = Address::Upvalue(self.functions[k].add_upvalue(addr));
|
addr = Address::Upvalue(self.functions[k].add_upvalue(addr));
|
||||||
}
|
}
|
||||||
return Ok(addr);
|
return Ok(addr);
|
||||||
@@ -418,7 +433,7 @@ impl Binder {
|
|||||||
|
|
||||||
// 3. Try Global
|
// 3. Try Global
|
||||||
let globals = self.globals.borrow();
|
let globals = self.globals.borrow();
|
||||||
if let Some(idx) = globals.get(sym) {
|
if let Some((idx, _)) = globals.get(sym) {
|
||||||
return Ok(Address::Global(*idx));
|
return Ok(Address::Global(*idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -428,7 +443,7 @@ impl Binder {
|
|||||||
name: sym.name.clone(),
|
name: sym.name.clone(),
|
||||||
context: None,
|
context: None,
|
||||||
};
|
};
|
||||||
if let Some(idx) = globals.get(&fallback_sym) {
|
if let Some((idx, _)) = globals.get(&fallback_sym) {
|
||||||
return Ok(Address::Global(*idx));
|
return Ok(Address::Global(*idx));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -443,12 +458,7 @@ impl Binder {
|
|||||||
) -> Result<BoundNode, String> {
|
) -> Result<BoundNode, String> {
|
||||||
match &node.kind {
|
match &node.kind {
|
||||||
UntypedKind::Parameter(sym) => {
|
UntypedKind::Parameter(sym) => {
|
||||||
let addr = self.declare_variable(sym, kind)?;
|
let addr = self.declare_variable(sym, node.identity.clone(), kind)?;
|
||||||
let captured_by = self
|
|
||||||
.capture_map
|
|
||||||
.get(&node.identity)
|
|
||||||
.cloned()
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
Ok(self.make_node(
|
Ok(self.make_node(
|
||||||
node.identity.clone(),
|
node.identity.clone(),
|
||||||
@@ -457,7 +467,7 @@ impl Binder {
|
|||||||
addr,
|
addr,
|
||||||
kind,
|
kind,
|
||||||
value: Box::new(self.make_node(node.identity.clone(), BoundKind::Nop)),
|
value: Box::new(self.make_node(node.identity.clone(), BoundKind::Nop)),
|
||||||
captured_by,
|
captured_by: Vec::new(), // Filled by post-pass
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -530,19 +540,16 @@ mod tests {
|
|||||||
let untyped = parser.parse_expression().unwrap();
|
let untyped = parser.parse_expression().unwrap();
|
||||||
|
|
||||||
let globals = Rc::new(RefCell::new(HashMap::new()));
|
let globals = Rc::new(RefCell::new(HashMap::new()));
|
||||||
let bound = Binder::bind_root(globals, &untyped).unwrap();
|
let (bound, captures) = Binder::bind_root(globals, &untyped).unwrap();
|
||||||
|
|
||||||
// Structure: Lambda -> Block -> [ Define(x), Define(f), Get(x) ]
|
// Structure: Lambda -> Block -> [ Define(x), Define(f), Get(x) ]
|
||||||
if let BoundKind::Lambda { body, .. } = &bound.kind {
|
if let BoundKind::Lambda { body, .. } = &bound.kind {
|
||||||
if let BoundKind::Block { exprs } = &body.kind {
|
if let BoundKind::Block { exprs } = &body.kind {
|
||||||
let x_decl = &exprs[0];
|
let x_decl = &exprs[0];
|
||||||
if let BoundKind::Define {
|
if let BoundKind::Define { addr, .. } = &x_decl.kind {
|
||||||
captured_by, addr, ..
|
|
||||||
} = &x_decl.kind
|
|
||||||
{
|
|
||||||
assert!(matches!(addr, Address::Local(_)));
|
assert!(matches!(addr, Address::Local(_)));
|
||||||
assert!(
|
assert!(
|
||||||
!captured_by.is_empty(),
|
captures.contains_key(&x_decl.identity),
|
||||||
"Variable 'x' should have capturers because it is used in lambda 'f'"
|
"Variable 'x' should have capturers because it is used in lambda 'f'"
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@@ -566,18 +573,15 @@ mod tests {
|
|||||||
let untyped = parser.parse_expression().unwrap();
|
let untyped = parser.parse_expression().unwrap();
|
||||||
|
|
||||||
let globals = Rc::new(RefCell::new(HashMap::new()));
|
let globals = Rc::new(RefCell::new(HashMap::new()));
|
||||||
let bound = Binder::bind_root(globals, &untyped).unwrap();
|
let (bound, captures) = Binder::bind_root(globals, &untyped).unwrap();
|
||||||
|
|
||||||
if let BoundKind::Lambda { body, .. } = &bound.kind {
|
if let BoundKind::Lambda { body, .. } = &bound.kind {
|
||||||
if let BoundKind::Block { exprs } = &body.kind {
|
if let BoundKind::Block { exprs } = &body.kind {
|
||||||
let x_decl = &exprs[0];
|
let x_decl = &exprs[0];
|
||||||
if let BoundKind::Define {
|
if let BoundKind::Define { addr, .. } = &x_decl.kind {
|
||||||
captured_by, addr, ..
|
|
||||||
} = &x_decl.kind
|
|
||||||
{
|
|
||||||
assert!(matches!(addr, Address::Local(_)));
|
assert!(matches!(addr, Address::Local(_)));
|
||||||
assert!(
|
assert!(
|
||||||
captured_by.is_empty(),
|
!captures.contains_key(&x_decl.identity),
|
||||||
"Variable 'x' should NOT have any capturers"
|
"Variable 'x' should NOT have any capturers"
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -713,7 +713,15 @@ mod tests {
|
|||||||
let expanded = expander.expand(untyped).unwrap();
|
let expanded = expander.expand(untyped).unwrap();
|
||||||
|
|
||||||
let mut global_names = HashMap::new();
|
let mut global_names = HashMap::new();
|
||||||
global_names.insert(Symbol::from("*"), 0);
|
global_names.insert(
|
||||||
|
Symbol::from("*"),
|
||||||
|
(
|
||||||
|
0,
|
||||||
|
Rc::new(crate::ast::types::NodeIdentity {
|
||||||
|
location: crate::ast::types::SourceLocation { line: 0, col: 0 },
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
let globals = Rc::new(RefCell::new(global_names));
|
let globals = Rc::new(RefCell::new(global_names));
|
||||||
|
|
||||||
let result = Binder::bind_root(globals, &expanded);
|
let result = Binder::bind_root(globals, &expanded);
|
||||||
@@ -740,8 +748,8 @@ mod tests {
|
|||||||
let expanded = expander.expand(untyped).unwrap();
|
let expanded = expander.expand(untyped).unwrap();
|
||||||
|
|
||||||
let globals = Rc::new(RefCell::new(HashMap::new()));
|
let globals = Rc::new(RefCell::new(HashMap::new()));
|
||||||
let bound = Binder::bind_root(globals, &expanded);
|
let result = Binder::bind_root(globals, &expanded);
|
||||||
assert!(bound.is_ok(), "Hygiene failed: {:?}", bound.err());
|
assert!(result.is_ok(), "Hygiene failed: {:?}", result.err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -760,11 +768,11 @@ mod tests {
|
|||||||
let expanded = expander.expand(untyped).unwrap();
|
let expanded = expander.expand(untyped).unwrap();
|
||||||
|
|
||||||
let globals = Rc::new(RefCell::new(HashMap::new()));
|
let globals = Rc::new(RefCell::new(HashMap::new()));
|
||||||
let bound = Binder::bind_root(globals, &expanded);
|
let result = Binder::bind_root(globals, &expanded);
|
||||||
assert!(
|
assert!(
|
||||||
bound.is_ok(),
|
result.is_ok(),
|
||||||
"Explicit Hygiene with Backticks failed: {:?}",
|
"Explicit Hygiene with Backticks failed: {:?}",
|
||||||
bound.err()
|
result.err()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
pub mod analyzer;
|
pub mod analyzer;
|
||||||
pub mod binder;
|
pub mod binder;
|
||||||
pub mod bound_nodes;
|
pub mod bound_nodes;
|
||||||
|
pub mod captures;
|
||||||
pub mod dumper;
|
pub mod dumper;
|
||||||
pub mod lambda_collector;
|
pub mod lambda_collector;
|
||||||
pub mod macros;
|
pub mod macros;
|
||||||
@@ -8,14 +9,13 @@ pub mod optimizer;
|
|||||||
pub mod specializer;
|
pub mod specializer;
|
||||||
pub mod tco;
|
pub mod tco;
|
||||||
pub mod type_checker;
|
pub mod type_checker;
|
||||||
pub mod upvalues;
|
|
||||||
|
|
||||||
pub use binder::*;
|
pub use binder::*;
|
||||||
pub use bound_nodes::*;
|
pub use bound_nodes::*;
|
||||||
|
pub use captures::*;
|
||||||
pub use dumper::*;
|
pub use dumper::*;
|
||||||
pub use macros::*;
|
pub use macros::*;
|
||||||
pub use optimizer::*;
|
pub use optimizer::*;
|
||||||
pub use specializer::*;
|
pub use specializer::*;
|
||||||
pub use tco::*;
|
pub use tco::*;
|
||||||
pub use type_checker::*;
|
pub use type_checker::*;
|
||||||
pub use upvalues::*;
|
|
||||||
|
|||||||
@@ -1,135 +0,0 @@
|
|||||||
use crate::ast::nodes::{Node, Symbol, UntypedKind};
|
|
||||||
use crate::ast::types::Identity;
|
|
||||||
use std::collections::{HashMap, HashSet};
|
|
||||||
|
|
||||||
/// Analyzes the AST to find which lambdas capture which variable declarations.
|
|
||||||
/// Returns a map: Declaration Identity -> List of Lambda Identities that capture it.
|
|
||||||
pub struct UpvalueAnalyzer;
|
|
||||||
|
|
||||||
impl UpvalueAnalyzer {
|
|
||||||
pub fn analyze(root: &Node<UntypedKind>) -> HashMap<Identity, Vec<Identity>> {
|
|
||||||
let mut capture_map: HashMap<Identity, HashSet<Identity>> = HashMap::new();
|
|
||||||
let mut scopes = vec![HashMap::new()]; // Root scope
|
|
||||||
|
|
||||||
Self::visit(root, &mut scopes, &mut capture_map, None);
|
|
||||||
|
|
||||||
// Convert HashSet back to sorted Vec for the final result
|
|
||||||
capture_map
|
|
||||||
.into_iter()
|
|
||||||
.map(|(decl, lambdas)| (decl, lambdas.into_iter().collect()))
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_params(node: &Node<UntypedKind>, scope: &mut HashMap<Symbol, Identity>) {
|
|
||||||
match &node.kind {
|
|
||||||
UntypedKind::Parameter(sym) => {
|
|
||||||
scope.insert(sym.clone(), node.identity.clone());
|
|
||||||
}
|
|
||||||
UntypedKind::Tuple { elements } => {
|
|
||||||
for el in elements {
|
|
||||||
Self::visit_params(el, scope);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {} // Ignore other nodes in parameter patterns for now
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit(
|
|
||||||
node: &Node<UntypedKind>,
|
|
||||||
scopes: &mut Vec<HashMap<Symbol, Identity>>,
|
|
||||||
capture_map: &mut HashMap<Identity, HashSet<Identity>>,
|
|
||||||
current_lambda: Option<Identity>,
|
|
||||||
) {
|
|
||||||
match &node.kind {
|
|
||||||
UntypedKind::Identifier(sym) => {
|
|
||||||
// Resolve name in scope stack (from inner to outer)
|
|
||||||
for (depth, scope) in scopes.iter().rev().enumerate() {
|
|
||||||
if let Some(decl_id) = scope.get(sym) {
|
|
||||||
if depth > 0 {
|
|
||||||
// Captured from an outer scope!
|
|
||||||
if let Some(lambda_id) = ¤t_lambda {
|
|
||||||
capture_map
|
|
||||||
.entry(decl_id.clone())
|
|
||||||
.or_default()
|
|
||||||
.insert(lambda_id.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
UntypedKind::Def { target, value } => {
|
|
||||||
Self::visit(value, scopes, capture_map, current_lambda.clone());
|
|
||||||
if let Some(current) = scopes.last_mut() {
|
|
||||||
Self::visit_params(target, current);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
UntypedKind::Lambda { params, body } => {
|
|
||||||
let mut new_scope = HashMap::new();
|
|
||||||
Self::visit_params(params, &mut new_scope);
|
|
||||||
|
|
||||||
scopes.push(new_scope);
|
|
||||||
// The current node is the lambda causing captures in its body
|
|
||||||
Self::visit(body, scopes, capture_map, Some(node.identity.clone()));
|
|
||||||
scopes.pop();
|
|
||||||
}
|
|
||||||
UntypedKind::MacroDecl { params, body, .. } => {
|
|
||||||
let mut new_scope = HashMap::new();
|
|
||||||
Self::visit_params(params, &mut new_scope);
|
|
||||||
scopes.push(new_scope);
|
|
||||||
Self::visit(body, scopes, capture_map, current_lambda);
|
|
||||||
scopes.pop();
|
|
||||||
}
|
|
||||||
UntypedKind::If {
|
|
||||||
cond,
|
|
||||||
then_br,
|
|
||||||
else_br,
|
|
||||||
} => {
|
|
||||||
Self::visit(cond, scopes, capture_map, current_lambda.clone());
|
|
||||||
Self::visit(then_br, scopes, capture_map, current_lambda.clone());
|
|
||||||
if let Some(e) = else_br {
|
|
||||||
Self::visit(e, scopes, capture_map, current_lambda.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
UntypedKind::Assign { target, value } => {
|
|
||||||
Self::visit(target, scopes, capture_map, current_lambda.clone());
|
|
||||||
Self::visit(value, scopes, capture_map, current_lambda.clone());
|
|
||||||
}
|
|
||||||
UntypedKind::Call { callee, args } => {
|
|
||||||
Self::visit(callee, scopes, capture_map, current_lambda.clone());
|
|
||||||
Self::visit(args, scopes, capture_map, current_lambda.clone());
|
|
||||||
}
|
|
||||||
UntypedKind::Again { args } => {
|
|
||||||
Self::visit(args, scopes, capture_map, current_lambda.clone());
|
|
||||||
}
|
|
||||||
UntypedKind::Block { exprs } => {
|
|
||||||
for expr in exprs {
|
|
||||||
Self::visit(expr, scopes, capture_map, current_lambda.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
UntypedKind::Tuple { elements } => {
|
|
||||||
for el in elements {
|
|
||||||
Self::visit(el, scopes, capture_map, current_lambda.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
UntypedKind::Record { fields } => {
|
|
||||||
for (k, v) in fields {
|
|
||||||
Self::visit(k, scopes, capture_map, current_lambda.clone());
|
|
||||||
Self::visit(v, scopes, capture_map, current_lambda.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
UntypedKind::Expansion { call: _, expanded } => {
|
|
||||||
Self::visit(expanded, scopes, capture_map, current_lambda);
|
|
||||||
}
|
|
||||||
UntypedKind::Template(body)
|
|
||||||
| UntypedKind::Placeholder(body)
|
|
||||||
| UntypedKind::Splice(body) => {
|
|
||||||
Self::visit(body, scopes, capture_map, current_lambda);
|
|
||||||
}
|
|
||||||
UntypedKind::Nop
|
|
||||||
| UntypedKind::Constant(_)
|
|
||||||
| UntypedKind::Extension(_)
|
|
||||||
| UntypedKind::Parameter(_) => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+18
-7
@@ -17,10 +17,12 @@ use crate::ast::compiler::specializer::{FunctionRegistry, MonoCache, Specializer
|
|||||||
use crate::ast::compiler::tco::{ExecNode, TCO};
|
use crate::ast::compiler::tco::{ExecNode, TCO};
|
||||||
use crate::ast::rtl;
|
use crate::ast::rtl;
|
||||||
use crate::ast::rtl::intrinsics;
|
use crate::ast::rtl::intrinsics;
|
||||||
use crate::ast::types::{Object, Purity, StaticType, Value};
|
use crate::ast::types::{
|
||||||
|
Identity, NodeIdentity, Object, Purity, SourceLocation, StaticType, Value,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct Environment {
|
pub struct Environment {
|
||||||
pub global_names: Rc<RefCell<HashMap<Symbol, u32>>>,
|
pub global_names: Rc<RefCell<HashMap<Symbol, (u32, Identity)>>>,
|
||||||
pub global_types: Rc<RefCell<HashMap<u32, StaticType>>>,
|
pub global_types: Rc<RefCell<HashMap<u32, StaticType>>>,
|
||||||
pub global_purity: Rc<RefCell<HashMap<u32, Purity>>>,
|
pub global_purity: Rc<RefCell<HashMap<u32, Purity>>>,
|
||||||
pub global_values: Rc<RefCell<Vec<Value>>>,
|
pub global_values: Rc<RefCell<Vec<Value>>>,
|
||||||
@@ -55,7 +57,7 @@ impl FunctionRegistry for EnvFunctionRegistry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct RuntimeMacroEvaluator {
|
struct RuntimeMacroEvaluator {
|
||||||
global_names: Rc<RefCell<HashMap<Symbol, u32>>>,
|
global_names: Rc<RefCell<HashMap<Symbol, (u32, Identity)>>>,
|
||||||
global_types: Rc<RefCell<HashMap<u32, StaticType>>>,
|
global_types: Rc<RefCell<HashMap<u32, StaticType>>>,
|
||||||
global_values: Rc<RefCell<Vec<Value>>>,
|
global_values: Rc<RefCell<Vec<Value>>>,
|
||||||
}
|
}
|
||||||
@@ -72,7 +74,8 @@ impl MacroEvaluator for RuntimeMacroEvaluator {
|
|||||||
return Ok(Value::Object(Rc::new(arg_node.clone()) as Rc<dyn Object>));
|
return Ok(Value::Object(Rc::new(arg_node.clone()) as Rc<dyn Object>));
|
||||||
}
|
}
|
||||||
|
|
||||||
let bound_ast = Binder::bind_root(self.global_names.clone(), node)?;
|
let (bound_ast, captures) = Binder::bind_root(self.global_names.clone(), node)?;
|
||||||
|
let bound_ast = crate::ast::compiler::captures::CapturePass::apply(bound_ast, &captures);
|
||||||
let checker = TypeChecker::new(self.global_types.clone());
|
let checker = TypeChecker::new(self.global_types.clone());
|
||||||
let typed_ast = checker.check(bound_ast, &[])?;
|
let typed_ast = checker.check(bound_ast, &[])?;
|
||||||
let exec_ast = TCO::optimize(Analyzer::analyze(&typed_ast, &HashMap::new())); // Minimal analysis for macro eval
|
let exec_ast = TCO::optimize(Analyzer::analyze(&typed_ast, &HashMap::new())); // Minimal analysis for macro eval
|
||||||
@@ -131,7 +134,10 @@ impl Environment {
|
|||||||
let mut purity = self.global_purity.borrow_mut();
|
let mut purity = self.global_purity.borrow_mut();
|
||||||
|
|
||||||
let idx = values.len() as u32;
|
let idx = values.len() as u32;
|
||||||
names.insert(Symbol::from(name), idx);
|
let identity = Rc::new(NodeIdentity {
|
||||||
|
location: SourceLocation { line: 0, col: 0 },
|
||||||
|
});
|
||||||
|
names.insert(Symbol::from(name), (idx, identity));
|
||||||
types.insert(idx, ty);
|
types.insert(idx, ty);
|
||||||
purity.insert(idx, func.purity);
|
purity.insert(idx, func.purity);
|
||||||
values.push(Value::Function(func));
|
values.push(Value::Function(func));
|
||||||
@@ -161,7 +167,10 @@ impl Environment {
|
|||||||
let mut purity = self.global_purity.borrow_mut();
|
let mut purity = self.global_purity.borrow_mut();
|
||||||
|
|
||||||
let idx = values.len() as u32;
|
let idx = values.len() as u32;
|
||||||
names.insert(Symbol::from(name), idx);
|
let identity = Rc::new(NodeIdentity {
|
||||||
|
location: SourceLocation { line: 0, col: 0 },
|
||||||
|
});
|
||||||
|
names.insert(Symbol::from(name), (idx, identity));
|
||||||
types.insert(idx, ty);
|
types.insert(idx, ty);
|
||||||
purity.insert(idx, Purity::Pure);
|
purity.insert(idx, Purity::Pure);
|
||||||
values.push(val);
|
values.push(val);
|
||||||
@@ -186,7 +195,9 @@ impl Environment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let expanded_ast = self.get_expander().expand(untyped_ast)?;
|
let expanded_ast = self.get_expander().expand(untyped_ast)?;
|
||||||
let bound_ast = Binder::bind_root(self.global_names.clone(), &expanded_ast)?;
|
let (bound_ast, captures) = Binder::bind_root(self.global_names.clone(), &expanded_ast)?;
|
||||||
|
let bound_ast = crate::ast::compiler::captures::CapturePass::apply(bound_ast, &captures);
|
||||||
|
|
||||||
LambdaCollector::collect(&bound_ast, &mut self.function_registry.borrow_mut());
|
LambdaCollector::collect(&bound_ast, &mut self.function_registry.borrow_mut());
|
||||||
|
|
||||||
let checker = TypeChecker::new(self.global_types.clone());
|
let checker = TypeChecker::new(self.global_types.clone());
|
||||||
|
|||||||
Reference in New Issue
Block a user