Refactor: Use new NodeKind and clean up Binder definitions
The Binder and related types have been refactored to use the new `NodeKind` enum instead of the previous `BoundKind`. This commit updates all references to use the new structure, ensuring consistency across the compiler's AST representation. Key changes include: - Replacing `BoundKind` with `NodeKind` in the Binder's `bind` and `visit` methods. - Updating pattern matching and field access to reflect the new enum variants (e.g., `NodeKind::Def` instead of `BoundKind::Define`). - Adjusting identifier bindings to use the new `IdentifierBinding` enum. - Reflecting changes in `DefBinding`, `AssignBinding`, and `LambdaBinding` structures. - Ensuring all newly created nodes use `NodeKind` and the appropriate metadata.
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -1,101 +1,103 @@
|
||||
use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, Node, NodeMetrics};
|
||||
use crate::ast::types::{Purity, RecordLayout, StaticType, Value};
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct Folder<'a> {
|
||||
pub globals: &'a Option<Rc<RefCell<Vec<Value>>>>,
|
||||
}
|
||||
|
||||
impl<'a> Folder<'a> {
|
||||
pub fn new(globals: &'a Option<Rc<RefCell<Vec<Value>>>>) -> Self {
|
||||
Self { globals }
|
||||
}
|
||||
|
||||
pub fn make_constant_node(&self, val: Value, template: &AnalyzedNode) -> AnalyzedNode {
|
||||
let ty = val.static_type();
|
||||
let typed_original = Rc::new(Node {
|
||||
identity: template.identity.clone(),
|
||||
kind: BoundKind::Constant(val.clone()),
|
||||
ty: ty.clone(),
|
||||
});
|
||||
Node {
|
||||
identity: template.identity.clone(),
|
||||
kind: BoundKind::Constant(val),
|
||||
ty: NodeMetrics {
|
||||
original: typed_original,
|
||||
purity: Purity::Pure,
|
||||
is_recursive: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_nop_node(&self, template: &AnalyzedNode) -> AnalyzedNode {
|
||||
let typed_original = Rc::new(Node {
|
||||
identity: template.identity.clone(),
|
||||
kind: BoundKind::Nop,
|
||||
ty: StaticType::Void,
|
||||
});
|
||||
Node {
|
||||
identity: template.identity.clone(),
|
||||
kind: BoundKind::Nop,
|
||||
ty: NodeMetrics {
|
||||
original: typed_original,
|
||||
purity: Purity::Pure,
|
||||
is_recursive: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_fold_record(
|
||||
&self,
|
||||
layout: &std::sync::Arc<RecordLayout>,
|
||||
values: &[Rc<AnalyzedNode>],
|
||||
template: &AnalyzedNode,
|
||||
) -> Option<AnalyzedNode> {
|
||||
let mut constant_values = Vec::with_capacity(values.len());
|
||||
|
||||
for v_node in values {
|
||||
if let BoundKind::Constant(val) = &v_node.kind {
|
||||
constant_values.push(val.clone());
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
let record_val = Value::Record(layout.clone(), Rc::new(constant_values));
|
||||
Some(self.make_constant_node(record_val, template))
|
||||
}
|
||||
|
||||
pub fn try_fold_pure(
|
||||
&self,
|
||||
callee: &AnalyzedNode,
|
||||
arg_nodes: &[Rc<AnalyzedNode>],
|
||||
) -> Option<AnalyzedNode> {
|
||||
if callee.ty.purity < Purity::Pure {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut arg_values = Vec::with_capacity(arg_nodes.len());
|
||||
for node in arg_nodes {
|
||||
if let BoundKind::Constant(val) = &node.kind {
|
||||
arg_values.push(val.clone());
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
let func_val = match &callee.kind {
|
||||
BoundKind::Get {
|
||||
addr: Address::Global(idx),
|
||||
..
|
||||
} => self.globals.as_ref()?.borrow().get(idx.0 as usize)?.clone(),
|
||||
BoundKind::Constant(val) => val.clone(),
|
||||
_ => return None,
|
||||
};
|
||||
let result = match func_val {
|
||||
Value::Function(f) => (f.func)(&arg_values),
|
||||
_ => return None,
|
||||
};
|
||||
Some(self.make_constant_node(result, callee))
|
||||
}
|
||||
}
|
||||
use crate::ast::compiler::bound_nodes::{
|
||||
Address, AnalyzedNode, IdentifierBinding, Node, NodeKind, NodeMetrics,
|
||||
};
|
||||
use crate::ast::types::{Purity, RecordLayout, StaticType, Value};
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct Folder<'a> {
|
||||
pub globals: &'a Option<Rc<RefCell<Vec<Value>>>>,
|
||||
}
|
||||
|
||||
impl<'a> Folder<'a> {
|
||||
pub fn new(globals: &'a Option<Rc<RefCell<Vec<Value>>>>) -> Self {
|
||||
Self { globals }
|
||||
}
|
||||
|
||||
pub fn make_constant_node(&self, val: Value, template: &AnalyzedNode) -> AnalyzedNode {
|
||||
let ty = val.static_type();
|
||||
let typed_original = Rc::new(Node {
|
||||
identity: template.identity.clone(),
|
||||
kind: NodeKind::Constant(val.clone()),
|
||||
ty: ty.clone(),
|
||||
});
|
||||
Node {
|
||||
identity: template.identity.clone(),
|
||||
kind: NodeKind::Constant(val),
|
||||
ty: NodeMetrics {
|
||||
original: typed_original,
|
||||
purity: Purity::Pure,
|
||||
is_recursive: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_nop_node(&self, template: &AnalyzedNode) -> AnalyzedNode {
|
||||
let typed_original = Rc::new(Node {
|
||||
identity: template.identity.clone(),
|
||||
kind: NodeKind::Nop,
|
||||
ty: StaticType::Void,
|
||||
});
|
||||
Node {
|
||||
identity: template.identity.clone(),
|
||||
kind: NodeKind::Nop,
|
||||
ty: NodeMetrics {
|
||||
original: typed_original,
|
||||
purity: Purity::Pure,
|
||||
is_recursive: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_fold_record(
|
||||
&self,
|
||||
layout: &std::sync::Arc<RecordLayout>,
|
||||
values: &[Rc<AnalyzedNode>],
|
||||
template: &AnalyzedNode,
|
||||
) -> Option<AnalyzedNode> {
|
||||
let mut constant_values = Vec::with_capacity(values.len());
|
||||
|
||||
for v_node in values {
|
||||
if let NodeKind::Constant(val) = &v_node.kind {
|
||||
constant_values.push(val.clone());
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
let record_val = Value::Record(layout.clone(), Rc::new(constant_values));
|
||||
Some(self.make_constant_node(record_val, template))
|
||||
}
|
||||
|
||||
pub fn try_fold_pure(
|
||||
&self,
|
||||
callee: &AnalyzedNode,
|
||||
arg_nodes: &[Rc<AnalyzedNode>],
|
||||
) -> Option<AnalyzedNode> {
|
||||
if callee.ty.purity < Purity::Pure {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut arg_values = Vec::with_capacity(arg_nodes.len());
|
||||
for node in arg_nodes {
|
||||
if let NodeKind::Constant(val) = &node.kind {
|
||||
arg_values.push(val.clone());
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
let func_val = match &callee.kind {
|
||||
NodeKind::Identifier {
|
||||
binding: IdentifierBinding::Reference(Address::Global(idx)),
|
||||
..
|
||||
} => self.globals.as_ref()?.borrow().get(idx.0 as usize)?.clone(),
|
||||
NodeKind::Constant(val) => val.clone(),
|
||||
_ => return None,
|
||||
};
|
||||
let result = match func_val {
|
||||
Value::Function(f) => (f.func)(&arg_values),
|
||||
_ => return None,
|
||||
};
|
||||
Some(self.make_constant_node(result, callee))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,165 +1,222 @@
|
||||
use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, VirtualId};
|
||||
use crate::ast::types::{Purity, Value};
|
||||
use crate::ast::vm::Closure;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashSet;
|
||||
use std::rc::Rc;
|
||||
|
||||
use super::substitution_map::SubstitutionMap;
|
||||
use super::utils::UsageInfo;
|
||||
|
||||
pub struct Inliner<'a> {
|
||||
pub globals: &'a Option<Rc<RefCell<Vec<Value>>>>,
|
||||
pub root_purity: &'a Option<Rc<RefCell<Vec<Purity>>>>,
|
||||
}
|
||||
|
||||
impl<'a> Inliner<'a> {
|
||||
pub fn new(
|
||||
globals: &'a Option<Rc<RefCell<Vec<Value>>>>,
|
||||
root_purity: &'a Option<Rc<RefCell<Vec<Purity>>>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
globals,
|
||||
root_purity,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_inlinable_value(&self, val: &Value, addr: Address<VirtualId>) -> bool {
|
||||
let type_ok = match val {
|
||||
Value::Int(_)
|
||||
| Value::Float(_)
|
||||
| Value::Bool(_)
|
||||
| Value::Text(_)
|
||||
| Value::Keyword(_)
|
||||
| Value::Record(_, _)
|
||||
| Value::DateTime(_) => true,
|
||||
Value::Object(obj) => {
|
||||
if let Some(closure) = obj.as_any().downcast_ref::<Closure>() {
|
||||
closure.upvalues.is_empty() && !closure.function_node.ty.is_recursive
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
|
||||
if !type_ok {
|
||||
return false;
|
||||
}
|
||||
|
||||
if let Address::Global(idx) = addr {
|
||||
if let Some(purity_rc) = &self.root_purity {
|
||||
let purity = purity_rc
|
||||
.borrow()
|
||||
.get(idx.0 as usize)
|
||||
.cloned()
|
||||
.unwrap_or(Purity::Impure);
|
||||
return purity >= Purity::Pure;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
pub fn prepare_beta_reduction(
|
||||
&self,
|
||||
params: &AnalyzedNode,
|
||||
arg_vals: &[Rc<AnalyzedNode>],
|
||||
body: &AnalyzedNode,
|
||||
sub: &mut SubstitutionMap,
|
||||
) -> Option<()> {
|
||||
let mut body_usage = UsageInfo::default();
|
||||
body_usage.collect(body);
|
||||
|
||||
let mut slot_index = 0;
|
||||
self.map_params_to_args(params, arg_vals, &mut slot_index, sub, &body_usage);
|
||||
|
||||
if slot_index != arg_vals.len() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut param_slots = HashSet::new();
|
||||
self.collect_parameter_slots_set(params, &mut param_slots);
|
||||
|
||||
for slot in param_slots {
|
||||
let addr = Address::Local(slot);
|
||||
if (body_usage.is_used(&addr) || body_usage.is_assigned(&addr))
|
||||
&& sub.get_value(&addr).is_none()
|
||||
&& !sub.ast_substitutions.contains_key(&addr)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
if sub.values.is_empty() && sub.ast_substitutions.is_empty() && !arg_vals.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(())
|
||||
}
|
||||
|
||||
pub fn map_params_to_args(
|
||||
&self,
|
||||
pattern: &AnalyzedNode,
|
||||
args: &[Rc<AnalyzedNode>],
|
||||
offset: &mut usize,
|
||||
sub: &mut SubstitutionMap,
|
||||
body_usage: &UsageInfo,
|
||||
) {
|
||||
match &pattern.kind {
|
||||
BoundKind::Define { addr, .. } => {
|
||||
if let Some(arg) = args.get(*offset)
|
||||
&& !body_usage.is_assigned(addr)
|
||||
{
|
||||
let mut core_arg = arg.as_ref();
|
||||
while let BoundKind::Expansion { bound_expanded, .. } = &core_arg.kind {
|
||||
core_arg = bound_expanded.as_ref();
|
||||
}
|
||||
|
||||
if let BoundKind::Constant(val) = &core_arg.kind {
|
||||
sub.add_value(*addr, val.clone());
|
||||
} else if let BoundKind::Lambda { upvalues, .. } = &core_arg.kind
|
||||
&& upvalues.is_empty()
|
||||
{
|
||||
sub.add_ast_substitution(*addr, core_arg.clone());
|
||||
} else if let BoundKind::Get {
|
||||
addr: Address::Global(_),
|
||||
..
|
||||
} = &core_arg.kind
|
||||
{
|
||||
sub.add_ast_substitution(*addr, core_arg.clone());
|
||||
}
|
||||
}
|
||||
|
||||
if let Address::Local(slot) = addr {
|
||||
sub.map_slot(*slot);
|
||||
}
|
||||
*offset += 1;
|
||||
}
|
||||
BoundKind::Tuple { elements } => {
|
||||
for el in elements {
|
||||
self.map_params_to_args(el, args, offset, sub, body_usage);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn collect_parameter_slots_set(&self, node: &AnalyzedNode, slots: &mut HashSet<VirtualId>) {
|
||||
match &node.kind {
|
||||
BoundKind::Define {
|
||||
addr: Address::Local(slot),
|
||||
..
|
||||
} => {
|
||||
slots.insert(*slot);
|
||||
}
|
||||
BoundKind::Tuple { elements } => {
|
||||
for el in elements {
|
||||
self.collect_parameter_slots_set(el, slots);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
use crate::ast::compiler::bound_nodes::{
|
||||
Address, AnalyzedNode, IdentifierBinding, NodeKind, VirtualId,
|
||||
};
|
||||
use crate::ast::types::{Purity, Value};
|
||||
use crate::ast::vm::Closure;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashSet;
|
||||
use std::rc::Rc;
|
||||
|
||||
use super::substitution_map::SubstitutionMap;
|
||||
use super::utils::UsageInfo;
|
||||
|
||||
pub struct Inliner<'a> {
|
||||
pub globals: &'a Option<Rc<RefCell<Vec<Value>>>>,
|
||||
pub root_purity: &'a Option<Rc<RefCell<Vec<Purity>>>>,
|
||||
}
|
||||
|
||||
impl<'a> Inliner<'a> {
|
||||
pub fn new(
|
||||
globals: &'a Option<Rc<RefCell<Vec<Value>>>>,
|
||||
root_purity: &'a Option<Rc<RefCell<Vec<Purity>>>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
globals,
|
||||
root_purity,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_inlinable_value(&self, val: &Value, addr: Address<VirtualId>) -> bool {
|
||||
let type_ok = match val {
|
||||
Value::Int(_)
|
||||
| Value::Float(_)
|
||||
| Value::Bool(_)
|
||||
| Value::Text(_)
|
||||
| Value::Keyword(_)
|
||||
| Value::Record(_, _)
|
||||
| Value::DateTime(_) => true,
|
||||
Value::Object(obj) => {
|
||||
if let Some(closure) = obj.as_any().downcast_ref::<Closure>() {
|
||||
closure.upvalues.is_empty() && !closure.function_node.ty.is_recursive
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
|
||||
if !type_ok {
|
||||
return false;
|
||||
}
|
||||
|
||||
if let Address::Global(idx) = addr {
|
||||
if let Some(purity_rc) = &self.root_purity {
|
||||
let purity = purity_rc
|
||||
.borrow()
|
||||
.get(idx.0 as usize)
|
||||
.cloned()
|
||||
.unwrap_or(Purity::Impure);
|
||||
return purity >= Purity::Pure;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
pub fn prepare_beta_reduction(
|
||||
&self,
|
||||
params: &AnalyzedNode,
|
||||
arg_vals: &[Rc<AnalyzedNode>],
|
||||
body: &AnalyzedNode,
|
||||
sub: &mut SubstitutionMap,
|
||||
) -> Option<()> {
|
||||
let mut body_usage = UsageInfo::default();
|
||||
body_usage.collect(body);
|
||||
|
||||
let mut slot_index = 0;
|
||||
self.map_params_to_args(params, arg_vals, &mut slot_index, sub, &body_usage);
|
||||
|
||||
if slot_index != arg_vals.len() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut param_slots = HashSet::new();
|
||||
self.collect_parameter_slots_set(params, &mut param_slots);
|
||||
|
||||
for slot in param_slots {
|
||||
let addr = Address::Local(slot);
|
||||
if (body_usage.is_used(&addr) || body_usage.is_assigned(&addr))
|
||||
&& sub.get_value(&addr).is_none()
|
||||
&& !sub.ast_substitutions.contains_key(&addr)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
if sub.values.is_empty() && sub.ast_substitutions.is_empty() && !arg_vals.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(())
|
||||
}
|
||||
|
||||
pub fn map_params_to_args(
|
||||
&self,
|
||||
pattern: &AnalyzedNode,
|
||||
args: &[Rc<AnalyzedNode>],
|
||||
offset: &mut usize,
|
||||
sub: &mut SubstitutionMap,
|
||||
body_usage: &UsageInfo,
|
||||
) {
|
||||
match &pattern.kind {
|
||||
NodeKind::Def { pattern: inner_pattern, .. } => {
|
||||
// Extract addr from the pattern's Identifier binding
|
||||
let addr = if let NodeKind::Identifier {
|
||||
binding: IdentifierBinding::Declaration { addr, .. },
|
||||
..
|
||||
} = &inner_pattern.kind
|
||||
{
|
||||
Some(*addr)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(addr) = addr {
|
||||
if let Some(arg) = args.get(*offset)
|
||||
&& !body_usage.is_assigned(&addr)
|
||||
{
|
||||
let mut core_arg = arg.as_ref();
|
||||
while let NodeKind::Expansion { expanded, .. } = &core_arg.kind {
|
||||
core_arg = expanded.as_ref();
|
||||
}
|
||||
|
||||
if let NodeKind::Constant(val) = &core_arg.kind {
|
||||
sub.add_value(addr, val.clone());
|
||||
} else if let NodeKind::Lambda { info: lambda_info, .. } = &core_arg.kind
|
||||
&& lambda_info.upvalues.is_empty()
|
||||
{
|
||||
sub.add_ast_substitution(addr, core_arg.clone());
|
||||
} else if let NodeKind::Identifier {
|
||||
binding: IdentifierBinding::Reference(Address::Global(_)),
|
||||
..
|
||||
} = &core_arg.kind
|
||||
{
|
||||
sub.add_ast_substitution(addr, core_arg.clone());
|
||||
}
|
||||
}
|
||||
|
||||
if let Address::Local(slot) = addr {
|
||||
sub.map_slot(slot);
|
||||
}
|
||||
}
|
||||
*offset += 1;
|
||||
}
|
||||
NodeKind::Identifier {
|
||||
binding: IdentifierBinding::Declaration { addr, .. },
|
||||
..
|
||||
} => {
|
||||
let addr = *addr;
|
||||
if let Some(arg) = args.get(*offset)
|
||||
&& !body_usage.is_assigned(&addr)
|
||||
{
|
||||
let mut core_arg = arg.as_ref();
|
||||
while let NodeKind::Expansion { expanded, .. } = &core_arg.kind {
|
||||
core_arg = expanded.as_ref();
|
||||
}
|
||||
|
||||
if let NodeKind::Constant(val) = &core_arg.kind {
|
||||
sub.add_value(addr, val.clone());
|
||||
} else if let NodeKind::Lambda { info: lambda_info, .. } = &core_arg.kind
|
||||
&& lambda_info.upvalues.is_empty()
|
||||
{
|
||||
sub.add_ast_substitution(addr, core_arg.clone());
|
||||
} else if let NodeKind::Identifier {
|
||||
binding: IdentifierBinding::Reference(Address::Global(_)),
|
||||
..
|
||||
} = &core_arg.kind
|
||||
{
|
||||
sub.add_ast_substitution(addr, core_arg.clone());
|
||||
}
|
||||
}
|
||||
|
||||
if let Address::Local(slot) = addr {
|
||||
sub.map_slot(slot);
|
||||
}
|
||||
*offset += 1;
|
||||
}
|
||||
NodeKind::Tuple { elements } => {
|
||||
for el in elements {
|
||||
self.map_params_to_args(el, args, offset, sub, body_usage);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn collect_parameter_slots_set(&self, node: &AnalyzedNode, slots: &mut HashSet<VirtualId>) {
|
||||
match &node.kind {
|
||||
NodeKind::Def { pattern, .. } => {
|
||||
if let NodeKind::Identifier {
|
||||
binding: IdentifierBinding::Declaration { addr: Address::Local(slot), .. },
|
||||
..
|
||||
} = &pattern.kind
|
||||
{
|
||||
slots.insert(*slot);
|
||||
}
|
||||
}
|
||||
NodeKind::Identifier {
|
||||
binding: IdentifierBinding::Declaration { addr: Address::Local(slot), .. },
|
||||
..
|
||||
} => {
|
||||
slots.insert(*slot);
|
||||
}
|
||||
NodeKind::Tuple { elements } => {
|
||||
for el in elements {
|
||||
self.collect_parameter_slots_set(el, slots);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,210 +1,237 @@
|
||||
use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, Node, UpvalueIdx, VirtualId};
|
||||
use crate::ast::types::Value;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct SubstitutionMap {
|
||||
pub values: HashMap<Address<VirtualId>, Value>,
|
||||
pub ast_substitutions: HashMap<Address<VirtualId>, Rc<AnalyzedNode>>,
|
||||
pub slot_mapping: HashMap<VirtualId, VirtualId>,
|
||||
pub assigned: HashSet<Address<VirtualId>>,
|
||||
pub next_slot: u32,
|
||||
pub used: HashSet<Address<VirtualId>>,
|
||||
pub captured_slots: HashSet<VirtualId>,
|
||||
}
|
||||
|
||||
impl SubstitutionMap {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Creates a new SubstitutionMap for an inner scope (like an inlined Lambda).
|
||||
/// Safely inherits only Global substitutions, because Local and Upvalue
|
||||
/// addresses are relative to the specific function frame and would overlap.
|
||||
pub fn new_inner(&self) -> Self {
|
||||
let mut inner = Self::new();
|
||||
for (k, v) in &self.values {
|
||||
if matches!(k, Address::Global(_)) {
|
||||
inner.values.insert(*k, v.clone());
|
||||
}
|
||||
}
|
||||
for (k, v) in &self.ast_substitutions {
|
||||
if matches!(k, Address::Global(_)) {
|
||||
inner.ast_substitutions.insert(*k, v.clone());
|
||||
}
|
||||
}
|
||||
inner
|
||||
}
|
||||
|
||||
pub fn new_for_inlining(&self) -> Self {
|
||||
let mut inner = self.new_inner();
|
||||
inner.next_slot = self.next_slot;
|
||||
inner
|
||||
}
|
||||
|
||||
pub fn add_ast_substitution(&mut self, addr: Address<VirtualId>, node: AnalyzedNode) {
|
||||
self.ast_substitutions.insert(addr, Rc::new(node));
|
||||
}
|
||||
|
||||
pub fn map_slot(&mut self, old_slot: VirtualId) -> VirtualId {
|
||||
if let Some(&new_slot) = self.slot_mapping.get(&old_slot) {
|
||||
return new_slot;
|
||||
}
|
||||
let new_slot = VirtualId(self.next_slot);
|
||||
self.slot_mapping.insert(old_slot, new_slot);
|
||||
self.next_slot += 1;
|
||||
new_slot
|
||||
}
|
||||
|
||||
pub fn map_address(&mut self, addr: Address<VirtualId>) -> Address<VirtualId> {
|
||||
match addr {
|
||||
Address::Local(slot) => Address::Local(self.map_slot(slot)),
|
||||
other => other,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_value(&mut self, addr: Address<VirtualId>, val: Value) {
|
||||
self.values.insert(addr, val);
|
||||
}
|
||||
|
||||
pub fn get_value(&self, addr: &Address<VirtualId>) -> Option<&Value> {
|
||||
self.values.get(addr)
|
||||
}
|
||||
|
||||
pub fn remove_value(&mut self, addr: &Address<VirtualId>) {
|
||||
self.values.remove(addr);
|
||||
}
|
||||
|
||||
fn reindex_addr(&self, addr: Address<VirtualId>, mapping: &[Option<u32>]) -> Address<VirtualId> {
|
||||
if let Address::Upvalue(idx) = addr
|
||||
&& let Some(res) = mapping.get(idx.0 as usize)
|
||||
&& let Some(new_idx) = res
|
||||
{
|
||||
Address::Upvalue(UpvalueIdx(*new_idx))
|
||||
} else {
|
||||
addr
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reindex_upvalues(&self, node_rc: Rc<AnalyzedNode>, mapping: &[Option<u32>]) -> Rc<AnalyzedNode> {
|
||||
let node = &*node_rc;
|
||||
let (new_kind, metrics) = match &node.kind {
|
||||
BoundKind::Get { addr, name } => (
|
||||
BoundKind::Get {
|
||||
addr: self.reindex_addr(*addr, mapping),
|
||||
name: name.clone(),
|
||||
},
|
||||
node.ty.clone(),
|
||||
),
|
||||
BoundKind::Lambda {
|
||||
params,
|
||||
upvalues,
|
||||
body,
|
||||
positional_count,
|
||||
} => {
|
||||
let mut next_upvalues = Vec::new();
|
||||
for addr in upvalues {
|
||||
next_upvalues.push(self.reindex_addr(*addr, mapping));
|
||||
}
|
||||
(
|
||||
BoundKind::Lambda {
|
||||
params: params.clone(),
|
||||
upvalues: next_upvalues,
|
||||
body: body.clone(),
|
||||
positional_count: *positional_count,
|
||||
},
|
||||
node.ty.clone(),
|
||||
)
|
||||
}
|
||||
BoundKind::If {
|
||||
cond,
|
||||
then_br,
|
||||
else_br,
|
||||
} => {
|
||||
let cond = self.reindex_upvalues(cond.clone(), mapping);
|
||||
let then_br = self.reindex_upvalues(then_br.clone(), mapping);
|
||||
let else_br = else_br.as_ref().map(|e| self.reindex_upvalues(e.clone(), mapping));
|
||||
(
|
||||
BoundKind::If {
|
||||
cond,
|
||||
then_br,
|
||||
else_br,
|
||||
},
|
||||
node.ty.clone(),
|
||||
)
|
||||
}
|
||||
BoundKind::Block { exprs } => {
|
||||
let exprs = exprs
|
||||
.iter()
|
||||
.map(|e| self.reindex_upvalues(e.clone(), mapping))
|
||||
.collect();
|
||||
(BoundKind::Block { exprs }, node.ty.clone())
|
||||
}
|
||||
BoundKind::Call { callee, args } => {
|
||||
let callee = self.reindex_upvalues(callee.clone(), mapping);
|
||||
let args = self.reindex_upvalues(args.clone(), mapping);
|
||||
(BoundKind::Call { callee, args }, node.ty.clone())
|
||||
}
|
||||
BoundKind::Define {
|
||||
name,
|
||||
addr,
|
||||
kind,
|
||||
value,
|
||||
captured_by,
|
||||
} => {
|
||||
let value = self.reindex_upvalues(value.clone(), mapping);
|
||||
(
|
||||
BoundKind::Define {
|
||||
name: name.clone(),
|
||||
addr: *addr,
|
||||
kind: *kind,
|
||||
value,
|
||||
captured_by: captured_by.clone(),
|
||||
},
|
||||
node.ty.clone(),
|
||||
)
|
||||
}
|
||||
BoundKind::Set { addr, value } => {
|
||||
let value = self.reindex_upvalues(value.clone(), mapping);
|
||||
(
|
||||
BoundKind::Set {
|
||||
addr: self.reindex_addr(*addr, mapping),
|
||||
value,
|
||||
},
|
||||
node.ty.clone(),
|
||||
)
|
||||
}
|
||||
BoundKind::Tuple { elements } => {
|
||||
let elements = elements
|
||||
.iter()
|
||||
.map(|e| self.reindex_upvalues(e.clone(), mapping))
|
||||
.collect();
|
||||
(BoundKind::Tuple { elements }, node.ty.clone())
|
||||
}
|
||||
BoundKind::Record { layout, values } => {
|
||||
let values = values
|
||||
.iter()
|
||||
.map(|v| self.reindex_upvalues(v.clone(), mapping))
|
||||
.collect();
|
||||
(BoundKind::Record { layout: layout.clone(), values }, node.ty.clone())
|
||||
}
|
||||
BoundKind::Expansion { original_call, bound_expanded } => {
|
||||
let bound_expanded = self.reindex_upvalues(bound_expanded.clone(), mapping);
|
||||
(
|
||||
BoundKind::Expansion {
|
||||
original_call: original_call.clone(),
|
||||
bound_expanded,
|
||||
},
|
||||
node.ty.clone(),
|
||||
)
|
||||
}
|
||||
k => (k.clone(), node.ty.clone()),
|
||||
};
|
||||
Rc::new(Node {
|
||||
identity: node.identity.clone(),
|
||||
kind: new_kind,
|
||||
ty: metrics,
|
||||
})
|
||||
}
|
||||
}
|
||||
use crate::ast::compiler::bound_nodes::{
|
||||
Address, AnalyzedNode, AssignBinding, IdentifierBinding,
|
||||
LambdaBinding, Node, NodeKind, UpvalueIdx, VirtualId,
|
||||
};
|
||||
use crate::ast::types::Value;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct SubstitutionMap {
|
||||
pub values: HashMap<Address<VirtualId>, Value>,
|
||||
pub ast_substitutions: HashMap<Address<VirtualId>, Rc<AnalyzedNode>>,
|
||||
pub slot_mapping: HashMap<VirtualId, VirtualId>,
|
||||
pub assigned: HashSet<Address<VirtualId>>,
|
||||
pub next_slot: u32,
|
||||
pub used: HashSet<Address<VirtualId>>,
|
||||
pub captured_slots: HashSet<VirtualId>,
|
||||
}
|
||||
|
||||
impl SubstitutionMap {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Creates a new SubstitutionMap for an inner scope (like an inlined Lambda).
|
||||
/// Safely inherits only Global substitutions, because Local and Upvalue
|
||||
/// addresses are relative to the specific function frame and would overlap.
|
||||
pub fn new_inner(&self) -> Self {
|
||||
let mut inner = Self::new();
|
||||
for (k, v) in &self.values {
|
||||
if matches!(k, Address::Global(_)) {
|
||||
inner.values.insert(*k, v.clone());
|
||||
}
|
||||
}
|
||||
for (k, v) in &self.ast_substitutions {
|
||||
if matches!(k, Address::Global(_)) {
|
||||
inner.ast_substitutions.insert(*k, v.clone());
|
||||
}
|
||||
}
|
||||
inner
|
||||
}
|
||||
|
||||
pub fn new_for_inlining(&self) -> Self {
|
||||
let mut inner = self.new_inner();
|
||||
inner.next_slot = self.next_slot;
|
||||
inner
|
||||
}
|
||||
|
||||
pub fn add_ast_substitution(&mut self, addr: Address<VirtualId>, node: AnalyzedNode) {
|
||||
self.ast_substitutions.insert(addr, Rc::new(node));
|
||||
}
|
||||
|
||||
pub fn map_slot(&mut self, old_slot: VirtualId) -> VirtualId {
|
||||
if let Some(&new_slot) = self.slot_mapping.get(&old_slot) {
|
||||
return new_slot;
|
||||
}
|
||||
let new_slot = VirtualId(self.next_slot);
|
||||
self.slot_mapping.insert(old_slot, new_slot);
|
||||
self.next_slot += 1;
|
||||
new_slot
|
||||
}
|
||||
|
||||
pub fn map_address(&mut self, addr: Address<VirtualId>) -> Address<VirtualId> {
|
||||
match addr {
|
||||
Address::Local(slot) => Address::Local(self.map_slot(slot)),
|
||||
other => other,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_value(&mut self, addr: Address<VirtualId>, val: Value) {
|
||||
self.values.insert(addr, val);
|
||||
}
|
||||
|
||||
pub fn get_value(&self, addr: &Address<VirtualId>) -> Option<&Value> {
|
||||
self.values.get(addr)
|
||||
}
|
||||
|
||||
pub fn remove_value(&mut self, addr: &Address<VirtualId>) {
|
||||
self.values.remove(addr);
|
||||
}
|
||||
|
||||
fn reindex_addr(&self, addr: Address<VirtualId>, mapping: &[Option<u32>]) -> Address<VirtualId> {
|
||||
if let Address::Upvalue(idx) = addr
|
||||
&& let Some(res) = mapping.get(idx.0 as usize)
|
||||
&& let Some(new_idx) = res
|
||||
{
|
||||
Address::Upvalue(UpvalueIdx(*new_idx))
|
||||
} else {
|
||||
addr
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reindex_upvalues(&self, node_rc: Rc<AnalyzedNode>, mapping: &[Option<u32>]) -> Rc<AnalyzedNode> {
|
||||
let node = &*node_rc;
|
||||
let (new_kind, metrics) = match &node.kind {
|
||||
NodeKind::Identifier { symbol, binding } => {
|
||||
let new_binding = match binding {
|
||||
IdentifierBinding::Reference(addr) => {
|
||||
IdentifierBinding::Reference(self.reindex_addr(*addr, mapping))
|
||||
}
|
||||
IdentifierBinding::Declaration { addr, kind } => {
|
||||
IdentifierBinding::Declaration {
|
||||
addr: self.reindex_addr(*addr, mapping),
|
||||
kind: *kind,
|
||||
}
|
||||
}
|
||||
};
|
||||
(
|
||||
NodeKind::Identifier {
|
||||
symbol: symbol.clone(),
|
||||
binding: new_binding,
|
||||
},
|
||||
node.ty.clone(),
|
||||
)
|
||||
}
|
||||
NodeKind::Lambda {
|
||||
params,
|
||||
body,
|
||||
info: lambda_info,
|
||||
} => {
|
||||
let mut next_upvalues = Vec::new();
|
||||
for addr in &lambda_info.upvalues {
|
||||
next_upvalues.push(self.reindex_addr(*addr, mapping));
|
||||
}
|
||||
(
|
||||
NodeKind::Lambda {
|
||||
params: params.clone(),
|
||||
body: body.clone(),
|
||||
info: LambdaBinding {
|
||||
upvalues: next_upvalues,
|
||||
positional_count: lambda_info.positional_count,
|
||||
},
|
||||
},
|
||||
node.ty.clone(),
|
||||
)
|
||||
}
|
||||
NodeKind::If {
|
||||
cond,
|
||||
then_br,
|
||||
else_br,
|
||||
} => {
|
||||
let cond = self.reindex_upvalues(cond.clone(), mapping);
|
||||
let then_br = self.reindex_upvalues(then_br.clone(), mapping);
|
||||
let else_br = else_br.as_ref().map(|e| self.reindex_upvalues(e.clone(), mapping));
|
||||
(
|
||||
NodeKind::If {
|
||||
cond,
|
||||
then_br,
|
||||
else_br,
|
||||
},
|
||||
node.ty.clone(),
|
||||
)
|
||||
}
|
||||
NodeKind::Block { exprs } => {
|
||||
let exprs = exprs
|
||||
.iter()
|
||||
.map(|e| self.reindex_upvalues(e.clone(), mapping))
|
||||
.collect();
|
||||
(NodeKind::Block { exprs }, node.ty.clone())
|
||||
}
|
||||
NodeKind::Call { callee, args } => {
|
||||
let callee = self.reindex_upvalues(callee.clone(), mapping);
|
||||
let args = self.reindex_upvalues(args.clone(), mapping);
|
||||
(NodeKind::Call { callee, args }, node.ty.clone())
|
||||
}
|
||||
NodeKind::Def {
|
||||
pattern,
|
||||
value,
|
||||
info,
|
||||
} => {
|
||||
let pattern = self.reindex_upvalues(pattern.clone(), mapping);
|
||||
let value = self.reindex_upvalues(value.clone(), mapping);
|
||||
(
|
||||
NodeKind::Def {
|
||||
pattern,
|
||||
value,
|
||||
info: info.clone(),
|
||||
},
|
||||
node.ty.clone(),
|
||||
)
|
||||
}
|
||||
NodeKind::Assign {
|
||||
target,
|
||||
value,
|
||||
info: assign_info,
|
||||
} => {
|
||||
let target = self.reindex_upvalues(target.clone(), mapping);
|
||||
let value = self.reindex_upvalues(value.clone(), mapping);
|
||||
let new_info = if let Some(addr) = assign_info.addr {
|
||||
AssignBinding {
|
||||
addr: Some(self.reindex_addr(addr, mapping)),
|
||||
}
|
||||
} else {
|
||||
assign_info.clone()
|
||||
};
|
||||
(
|
||||
NodeKind::Assign {
|
||||
target,
|
||||
value,
|
||||
info: new_info,
|
||||
},
|
||||
node.ty.clone(),
|
||||
)
|
||||
}
|
||||
NodeKind::Tuple { elements } => {
|
||||
let elements = elements
|
||||
.iter()
|
||||
.map(|e| self.reindex_upvalues(e.clone(), mapping))
|
||||
.collect();
|
||||
(NodeKind::Tuple { elements }, node.ty.clone())
|
||||
}
|
||||
NodeKind::Record { fields, layout } => {
|
||||
let fields = fields
|
||||
.iter()
|
||||
.map(|(k, v)| (k.clone(), self.reindex_upvalues(v.clone(), mapping)))
|
||||
.collect();
|
||||
(NodeKind::Record { fields, layout: layout.clone() }, node.ty.clone())
|
||||
}
|
||||
NodeKind::Expansion { original_call, expanded } => {
|
||||
let expanded = self.reindex_upvalues(expanded.clone(), mapping);
|
||||
(
|
||||
NodeKind::Expansion {
|
||||
original_call: original_call.clone(),
|
||||
expanded,
|
||||
},
|
||||
node.ty.clone(),
|
||||
)
|
||||
}
|
||||
k => (k.clone(), node.ty.clone()),
|
||||
};
|
||||
Rc::new(Node {
|
||||
identity: node.identity.clone(),
|
||||
kind: new_kind,
|
||||
ty: metrics,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+219
-182
@@ -1,182 +1,219 @@
|
||||
use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, GlobalIdx, VirtualId};
|
||||
use crate::ast::types::{Identity, Value};
|
||||
use crate::ast::vm::Closure;
|
||||
use std::collections::HashSet;
|
||||
|
||||
// --- PathTracker ---
|
||||
#[derive(Default)]
|
||||
pub struct PathTracker {
|
||||
pub inlining_depth: usize,
|
||||
pub inlining_stack: HashSet<GlobalIdx>,
|
||||
pub identity_stack: HashSet<Identity>,
|
||||
}
|
||||
|
||||
impl PathTracker {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn enter_lambda(&mut self, identity: &Identity) -> bool {
|
||||
if self.identity_stack.contains(identity) {
|
||||
return false;
|
||||
}
|
||||
self.identity_stack.insert(identity.clone());
|
||||
true
|
||||
}
|
||||
|
||||
pub fn exit_lambda(&mut self, identity: &Identity) {
|
||||
self.identity_stack.remove(identity);
|
||||
}
|
||||
}
|
||||
|
||||
// --- UsageInfo ---
|
||||
#[derive(Default)]
|
||||
pub struct UsageInfo {
|
||||
pub used: HashSet<Address<VirtualId>>,
|
||||
pub assigned: HashSet<Address<VirtualId>>,
|
||||
pub used_identities: HashSet<Identity>,
|
||||
}
|
||||
|
||||
impl UsageInfo {
|
||||
pub fn is_assigned(&self, addr: &Address<VirtualId>) -> bool {
|
||||
self.assigned.contains(addr)
|
||||
}
|
||||
|
||||
pub fn is_used(&self, addr: &Address<VirtualId>) -> bool {
|
||||
self.used.contains(addr)
|
||||
}
|
||||
|
||||
pub fn collect(&mut self, node: &AnalyzedNode) {
|
||||
match &node.kind {
|
||||
BoundKind::Destructure { pattern, value } => {
|
||||
self.collect_pattern(pattern);
|
||||
self.collect(value);
|
||||
}
|
||||
BoundKind::Constant(v) => {
|
||||
if let Value::Object(obj) = v
|
||||
&& let Some(closure) = obj.as_any().downcast_ref::<Closure>()
|
||||
{
|
||||
self.used_identities
|
||||
.insert(closure.function_node.identity.clone());
|
||||
self.collect(&closure.function_node);
|
||||
}
|
||||
}
|
||||
BoundKind::Get { addr, .. } => {
|
||||
self.used.insert(*addr);
|
||||
}
|
||||
BoundKind::Set { addr, value } => {
|
||||
self.assigned.insert(*addr);
|
||||
self.collect(value);
|
||||
}
|
||||
BoundKind::GetField { rec, .. } => {
|
||||
self.collect(rec);
|
||||
}
|
||||
BoundKind::Lambda {
|
||||
params,
|
||||
body,
|
||||
upvalues,
|
||||
..
|
||||
} => {
|
||||
self.used_identities.insert(node.identity.clone());
|
||||
|
||||
let mut inner_info = UsageInfo::default();
|
||||
inner_info.collect(params);
|
||||
inner_info.collect(body);
|
||||
|
||||
// Propagate globals and identities
|
||||
for addr in &inner_info.used {
|
||||
if let Address::Global(_) = addr {
|
||||
self.used.insert(*addr);
|
||||
}
|
||||
}
|
||||
for addr in &inner_info.assigned {
|
||||
if let Address::Global(_) = addr {
|
||||
self.assigned.insert(*addr);
|
||||
}
|
||||
}
|
||||
self.used_identities.extend(inner_info.used_identities);
|
||||
|
||||
// Map used upvalues to parent scope
|
||||
for addr in &inner_info.used {
|
||||
if let Address::Upvalue(idx) = addr
|
||||
&& let Some(parent_addr) = upvalues.get(idx.0 as usize)
|
||||
{
|
||||
self.used.insert(*parent_addr);
|
||||
}
|
||||
}
|
||||
// Map assigned upvalues to parent scope
|
||||
for addr in &inner_info.assigned {
|
||||
if let Address::Upvalue(idx) = addr
|
||||
&& let Some(parent_addr) = upvalues.get(idx.0 as usize)
|
||||
{
|
||||
self.assigned.insert(*parent_addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
BoundKind::Block { exprs } => {
|
||||
for e in exprs {
|
||||
self.collect(e);
|
||||
}
|
||||
}
|
||||
BoundKind::If {
|
||||
cond,
|
||||
then_br,
|
||||
else_br,
|
||||
} => {
|
||||
self.collect(cond);
|
||||
self.collect(then_br);
|
||||
if let Some(e) = else_br {
|
||||
self.collect(e);
|
||||
}
|
||||
}
|
||||
BoundKind::Call { callee, args } => {
|
||||
self.collect(callee);
|
||||
self.collect(args);
|
||||
}
|
||||
BoundKind::Tuple { elements } => {
|
||||
for e in elements {
|
||||
self.collect(e);
|
||||
}
|
||||
}
|
||||
BoundKind::Record { values, .. } => {
|
||||
for v in values {
|
||||
self.collect(v);
|
||||
}
|
||||
}
|
||||
BoundKind::Define { value, .. } => {
|
||||
self.collect(value);
|
||||
}
|
||||
BoundKind::Expansion { bound_expanded, .. } => {
|
||||
self.collect(bound_expanded);
|
||||
}
|
||||
BoundKind::Again { args } => {
|
||||
self.collect(args);
|
||||
}
|
||||
BoundKind::Pipe { inputs, lambda, .. } => {
|
||||
for input in inputs {
|
||||
self.collect(input);
|
||||
}
|
||||
self.collect(lambda);
|
||||
}
|
||||
BoundKind::Nop
|
||||
| BoundKind::FieldAccessor(_)
|
||||
| BoundKind::Extension(_)
|
||||
| BoundKind::Error => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn collect_pattern(&mut self, node: &AnalyzedNode) {
|
||||
match &node.kind {
|
||||
BoundKind::Define { .. } => {}
|
||||
BoundKind::Set { addr, .. } => {
|
||||
self.assigned.insert(*addr);
|
||||
}
|
||||
BoundKind::Tuple { elements } => {
|
||||
for el in elements {
|
||||
self.collect_pattern(el);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
use crate::ast::compiler::bound_nodes::{
|
||||
Address, AnalyzedNode, GlobalIdx, IdentifierBinding,
|
||||
NodeKind, VirtualId,
|
||||
};
|
||||
use crate::ast::types::{Identity, Value};
|
||||
use crate::ast::vm::Closure;
|
||||
use std::collections::HashSet;
|
||||
|
||||
// --- PathTracker ---
|
||||
#[derive(Default)]
|
||||
pub struct PathTracker {
|
||||
pub inlining_depth: usize,
|
||||
pub inlining_stack: HashSet<GlobalIdx>,
|
||||
pub identity_stack: HashSet<Identity>,
|
||||
}
|
||||
|
||||
impl PathTracker {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn enter_lambda(&mut self, identity: &Identity) -> bool {
|
||||
if self.identity_stack.contains(identity) {
|
||||
return false;
|
||||
}
|
||||
self.identity_stack.insert(identity.clone());
|
||||
true
|
||||
}
|
||||
|
||||
pub fn exit_lambda(&mut self, identity: &Identity) {
|
||||
self.identity_stack.remove(identity);
|
||||
}
|
||||
}
|
||||
|
||||
// --- UsageInfo ---
|
||||
#[derive(Default)]
|
||||
pub struct UsageInfo {
|
||||
pub used: HashSet<Address<VirtualId>>,
|
||||
pub assigned: HashSet<Address<VirtualId>>,
|
||||
pub used_identities: HashSet<Identity>,
|
||||
}
|
||||
|
||||
impl UsageInfo {
|
||||
pub fn is_assigned(&self, addr: &Address<VirtualId>) -> bool {
|
||||
self.assigned.contains(addr)
|
||||
}
|
||||
|
||||
pub fn is_used(&self, addr: &Address<VirtualId>) -> bool {
|
||||
self.used.contains(addr)
|
||||
}
|
||||
|
||||
pub fn collect(&mut self, node: &AnalyzedNode) {
|
||||
match &node.kind {
|
||||
NodeKind::Def { pattern, value, .. } => {
|
||||
self.collect_pattern(pattern);
|
||||
self.collect(value);
|
||||
}
|
||||
NodeKind::Constant(v) => {
|
||||
if let Value::Object(obj) = v
|
||||
&& let Some(closure) = obj.as_any().downcast_ref::<Closure>()
|
||||
{
|
||||
self.used_identities
|
||||
.insert(closure.function_node.identity.clone());
|
||||
self.collect(&closure.function_node);
|
||||
}
|
||||
}
|
||||
NodeKind::Identifier { binding, .. } => {
|
||||
let addr = match binding {
|
||||
IdentifierBinding::Reference(addr) => *addr,
|
||||
IdentifierBinding::Declaration { addr, .. } => *addr,
|
||||
};
|
||||
self.used.insert(addr);
|
||||
}
|
||||
NodeKind::Assign { target, value, info, .. } => {
|
||||
if let Some(addr) = info.addr {
|
||||
self.assigned.insert(addr);
|
||||
} else {
|
||||
// Destructuring assign: collect assigned addresses from target pattern
|
||||
self.collect_assigned_from_target(target);
|
||||
}
|
||||
self.collect(value);
|
||||
}
|
||||
NodeKind::GetField { rec, .. } => {
|
||||
self.collect(rec);
|
||||
}
|
||||
NodeKind::Lambda {
|
||||
params,
|
||||
body,
|
||||
info: lambda_info,
|
||||
} => {
|
||||
self.used_identities.insert(node.identity.clone());
|
||||
|
||||
let mut inner_info = UsageInfo::default();
|
||||
inner_info.collect(params);
|
||||
inner_info.collect(body);
|
||||
|
||||
// Propagate globals and identities
|
||||
for addr in &inner_info.used {
|
||||
if let Address::Global(_) = addr {
|
||||
self.used.insert(*addr);
|
||||
}
|
||||
}
|
||||
for addr in &inner_info.assigned {
|
||||
if let Address::Global(_) = addr {
|
||||
self.assigned.insert(*addr);
|
||||
}
|
||||
}
|
||||
self.used_identities.extend(inner_info.used_identities);
|
||||
|
||||
// Map used upvalues to parent scope
|
||||
for addr in &inner_info.used {
|
||||
if let Address::Upvalue(idx) = addr
|
||||
&& let Some(parent_addr) = lambda_info.upvalues.get(idx.0 as usize)
|
||||
{
|
||||
self.used.insert(*parent_addr);
|
||||
}
|
||||
}
|
||||
// Map assigned upvalues to parent scope
|
||||
for addr in &inner_info.assigned {
|
||||
if let Address::Upvalue(idx) = addr
|
||||
&& let Some(parent_addr) = lambda_info.upvalues.get(idx.0 as usize)
|
||||
{
|
||||
self.assigned.insert(*parent_addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
NodeKind::Block { exprs } => {
|
||||
for e in exprs {
|
||||
self.collect(e);
|
||||
}
|
||||
}
|
||||
NodeKind::If {
|
||||
cond,
|
||||
then_br,
|
||||
else_br,
|
||||
} => {
|
||||
self.collect(cond);
|
||||
self.collect(then_br);
|
||||
if let Some(e) = else_br {
|
||||
self.collect(e);
|
||||
}
|
||||
}
|
||||
NodeKind::Call { callee, args } => {
|
||||
self.collect(callee);
|
||||
self.collect(args);
|
||||
}
|
||||
NodeKind::Tuple { elements } => {
|
||||
for e in elements {
|
||||
self.collect(e);
|
||||
}
|
||||
}
|
||||
NodeKind::Record { fields, .. } => {
|
||||
for (_, v) in fields {
|
||||
self.collect(v);
|
||||
}
|
||||
}
|
||||
NodeKind::Expansion { expanded, .. } => {
|
||||
self.collect(expanded);
|
||||
}
|
||||
NodeKind::Again { args } => {
|
||||
self.collect(args);
|
||||
}
|
||||
NodeKind::Pipe { inputs, lambda, .. } => {
|
||||
for input in inputs {
|
||||
self.collect(input);
|
||||
}
|
||||
self.collect(lambda);
|
||||
}
|
||||
NodeKind::Nop
|
||||
| NodeKind::FieldAccessor(_)
|
||||
| NodeKind::Extension(_)
|
||||
| NodeKind::Error => {}
|
||||
// Syntax-only variants that should not appear in AnalyzedPhase
|
||||
NodeKind::MacroDecl { .. }
|
||||
| NodeKind::Template(_)
|
||||
| NodeKind::Placeholder(_)
|
||||
| NodeKind::Splice(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn collect_pattern(&mut self, node: &AnalyzedNode) {
|
||||
match &node.kind {
|
||||
NodeKind::Identifier {
|
||||
binding: IdentifierBinding::Declaration { .. },
|
||||
..
|
||||
} => {}
|
||||
NodeKind::Def { .. } => {}
|
||||
NodeKind::Assign { info, .. } => {
|
||||
if let Some(addr) = info.addr {
|
||||
self.assigned.insert(addr);
|
||||
}
|
||||
}
|
||||
NodeKind::Tuple { elements } => {
|
||||
for el in elements {
|
||||
self.collect_pattern(el);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_assigned_from_target(&mut self, node: &AnalyzedNode) {
|
||||
match &node.kind {
|
||||
NodeKind::Identifier { binding, .. } => {
|
||||
let addr = match binding {
|
||||
IdentifierBinding::Reference(addr)
|
||||
| IdentifierBinding::Declaration { addr, .. } => *addr,
|
||||
};
|
||||
self.assigned.insert(addr);
|
||||
}
|
||||
NodeKind::Tuple { elements } => {
|
||||
for el in elements {
|
||||
self.collect_assigned_from_target(el);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user