Refactor: Replace UntypedNode with SyntaxNode

This commit replaces the `UntypedNode` enum with the more accurately
named `SyntaxNode`. This change is primarily for clarity and better
reflects the role of these nodes as representing the structure of the
source code prior to semantic analysis.

The corresponding enum `UntypedKind` has also been renamed to
`SyntaxKind` to maintain consistency.

No functional changes are introduced by this refactoring; it is purely a
renaming and organizational update.
This commit is contained in:
Michael Schimmel
2026-03-13 14:21:28 +01:00
parent 7d72a99fa1
commit 84226f6a16
31 changed files with 8611 additions and 8611 deletions
File diff suppressed because it is too large Load Diff
+101 -101
View File
@@ -1,101 +1,101 @@
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, 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))
}
}
+210 -210
View File
@@ -1,210 +1,210 @@
use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, LocalSlot, Node, UpvalueIdx};
use crate::ast::types::Value;
use std::collections::{HashMap, HashSet};
use std::rc::Rc;
#[derive(Default)]
pub struct SubstitutionMap {
pub values: HashMap<Address, Value>,
pub ast_substitutions: HashMap<Address, Rc<AnalyzedNode>>,
pub slot_mapping: HashMap<LocalSlot, LocalSlot>,
pub assigned: HashSet<Address>,
pub next_slot: u32,
pub used: HashSet<Address>,
pub captured_slots: HashSet<LocalSlot>,
}
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, node: AnalyzedNode) {
self.ast_substitutions.insert(addr, Rc::new(node));
}
pub fn map_slot(&mut self, old_slot: LocalSlot) -> LocalSlot {
if let Some(&new_slot) = self.slot_mapping.get(&old_slot) {
return new_slot;
}
let new_slot = LocalSlot(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) -> Address {
match addr {
Address::Local(slot) => Address::Local(self.map_slot(slot)),
other => other,
}
}
pub fn add_value(&mut self, addr: Address, val: Value) {
self.values.insert(addr, val);
}
pub fn get_value(&self, addr: &Address) -> Option<&Value> {
self.values.get(addr)
}
pub fn remove_value(&mut self, addr: &Address) {
self.values.remove(addr);
}
fn reindex_addr(&self, addr: Address, mapping: &[Option<u32>]) -> Address {
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, BoundKind, LocalSlot, Node, UpvalueIdx};
use crate::ast::types::Value;
use std::collections::{HashMap, HashSet};
use std::rc::Rc;
#[derive(Default)]
pub struct SubstitutionMap {
pub values: HashMap<Address, Value>,
pub ast_substitutions: HashMap<Address, Rc<AnalyzedNode>>,
pub slot_mapping: HashMap<LocalSlot, LocalSlot>,
pub assigned: HashSet<Address>,
pub next_slot: u32,
pub used: HashSet<Address>,
pub captured_slots: HashSet<LocalSlot>,
}
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, node: AnalyzedNode) {
self.ast_substitutions.insert(addr, Rc::new(node));
}
pub fn map_slot(&mut self, old_slot: LocalSlot) -> LocalSlot {
if let Some(&new_slot) = self.slot_mapping.get(&old_slot) {
return new_slot;
}
let new_slot = LocalSlot(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) -> Address {
match addr {
Address::Local(slot) => Address::Local(self.map_slot(slot)),
other => other,
}
}
pub fn add_value(&mut self, addr: Address, val: Value) {
self.values.insert(addr, val);
}
pub fn get_value(&self, addr: &Address) -> Option<&Value> {
self.values.get(addr)
}
pub fn remove_value(&mut self, addr: &Address) {
self.values.remove(addr);
}
fn reindex_addr(&self, addr: Address, mapping: &[Option<u32>]) -> Address {
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,
})
}
}
+183 -183
View File
@@ -1,183 +1,183 @@
use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, GlobalIdx};
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>,
pub assigned: HashSet<Address>,
pub used_identities: HashSet<Identity>,
}
impl UsageInfo {
pub fn is_assigned(&self, addr: &Address) -> bool {
self.assigned.contains(addr)
}
pub fn is_used(&self, addr: &Address) -> 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 { addr, value, .. } => {
self.assigned.insert(*addr);
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, BoundKind, GlobalIdx};
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>,
pub assigned: HashSet<Address>,
pub used_identities: HashSet<Identity>,
}
impl UsageInfo {
pub fn is_assigned(&self, addr: &Address) -> bool {
self.assigned.contains(addr)
}
pub fn is_used(&self, addr: &Address) -> 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 { addr, value, .. } => {
self.assigned.insert(*addr);
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);
}
}
_ => {}
}
}
}