refactor: separate parser and compiler AST node structures
Simplified the AST architecture by removing the overly complex Node<K,
T> structure and replacing it with specialized types for
each phase:
- Introduced UntypedNode in nodes.rs for the Parser and Macro system,
eliminating redundant ty: () initializations.
- Defined a new generic Node<T> in bound_nodes.rs for all compiler
stages, where T represents the phase-specific metadata.
- Updated the Binder to act as the bridge between UntypedNode and
Node<()>.
- Simplified type aliases: TypedNode, AnalyzedNode, and ExecNode now
leverage the streamlined Node<T> structure.
- Updated Parser, Macros, Type-Checker, Optimizer, and VM to reflect
the architectural changes.
- Fixed import chains and resolved needless borrows via clippy.
This change improves type safety, reduces boilerplate code in the
parser, and makes the compiler stages more idiomatic and
readable.
This commit is contained in:
+386
-386
@@ -1,386 +1,386 @@
|
||||
use crate::ast::compiler::bound_nodes::{
|
||||
Address, AnalyzedNode, BoundKind, GlobalIdx, NodeMetrics, TypedNode,
|
||||
};
|
||||
use crate::ast::types::Purity;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct Analyzer<'a> {
|
||||
root_purity: &'a [Purity],
|
||||
/// Stack of currently visiting lambdas to detect direct recursion.
|
||||
lambda_stack: Vec<crate::ast::types::Identity>,
|
||||
/// Map of global index to its Lambda identity if known.
|
||||
globals_to_lambdas: HashMap<GlobalIdx, crate::ast::types::Identity>,
|
||||
/// Set of identities that were found to be recursive.
|
||||
recursive_identities: HashSet<crate::ast::types::Identity>,
|
||||
}
|
||||
|
||||
impl<'a> Analyzer<'a> {
|
||||
pub fn analyze(
|
||||
node: &TypedNode,
|
||||
root_purity: &'a [Purity],
|
||||
) -> AnalyzedNode {
|
||||
let mut analyzer = Self {
|
||||
root_purity,
|
||||
lambda_stack: Vec::new(),
|
||||
globals_to_lambdas: HashMap::new(),
|
||||
recursive_identities: HashSet::new(),
|
||||
};
|
||||
|
||||
// First pass: map globals to their lambda identities
|
||||
analyzer.collect_globals(node);
|
||||
|
||||
// Second pass: full analysis (decorating TypedNode into AnalyzedNode)
|
||||
analyzer.visit(Rc::new(node.clone()))
|
||||
}
|
||||
|
||||
fn collect_globals(&mut self, node: &TypedNode) {
|
||||
match &node.kind {
|
||||
BoundKind::Define {
|
||||
addr: Address::Global(global_index),
|
||||
value,
|
||||
..
|
||||
} => {
|
||||
if let BoundKind::Lambda { .. } = &value.kind {
|
||||
self.globals_to_lambdas
|
||||
.insert(*global_index, value.identity.clone());
|
||||
}
|
||||
self.collect_globals(value);
|
||||
}
|
||||
BoundKind::Block { exprs } => {
|
||||
for e in exprs {
|
||||
self.collect_globals(e);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
node.kind
|
||||
.for_each_child(|child| self.collect_globals(child));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit(&mut self, node_rc: Rc<TypedNode>) -> AnalyzedNode {
|
||||
let node = &*node_rc;
|
||||
let mut is_recursive = false;
|
||||
|
||||
let (new_kind, purity) = match &node.kind {
|
||||
BoundKind::Constant(v) => (BoundKind::Constant(v.clone()), Purity::Pure),
|
||||
BoundKind::Nop => (BoundKind::Nop, Purity::Pure),
|
||||
|
||||
BoundKind::Get { addr, name } => {
|
||||
let p = match addr {
|
||||
Address::Global(idx) => {
|
||||
self.root_purity.get(idx.0 as usize).cloned().unwrap_or(Purity::Pure)
|
||||
}
|
||||
_ => Purity::Pure,
|
||||
};
|
||||
(
|
||||
BoundKind::Get {
|
||||
addr: *addr,
|
||||
name: name.clone(),
|
||||
},
|
||||
p,
|
||||
)
|
||||
}
|
||||
|
||||
BoundKind::FieldAccessor(k) => (BoundKind::FieldAccessor(*k), Purity::Pure),
|
||||
|
||||
BoundKind::GetField { rec, field } => {
|
||||
let rec_m = self.visit(rec.clone());
|
||||
let p = rec_m.ty.purity;
|
||||
(
|
||||
BoundKind::GetField {
|
||||
rec: Rc::new(rec_m),
|
||||
field: *field,
|
||||
},
|
||||
p,
|
||||
)
|
||||
}
|
||||
|
||||
BoundKind::Set { addr, value } => {
|
||||
let val_m = self.visit(value.clone());
|
||||
(
|
||||
BoundKind::Set {
|
||||
addr: *addr,
|
||||
value: Rc::new(val_m),
|
||||
},
|
||||
Purity::Impure,
|
||||
)
|
||||
}
|
||||
|
||||
BoundKind::Define {
|
||||
name,
|
||||
addr,
|
||||
kind,
|
||||
value,
|
||||
captured_by,
|
||||
} => {
|
||||
let val_m = self.visit(value.clone());
|
||||
(
|
||||
BoundKind::Define {
|
||||
name: name.clone(),
|
||||
addr: *addr,
|
||||
kind: *kind,
|
||||
value: Rc::new(val_m),
|
||||
captured_by: captured_by.clone(),
|
||||
},
|
||||
Purity::Impure,
|
||||
)
|
||||
}
|
||||
|
||||
BoundKind::If {
|
||||
cond,
|
||||
then_br,
|
||||
else_br,
|
||||
} => {
|
||||
let cond_m = self.visit(cond.clone());
|
||||
let then_m = self.visit(then_br.clone());
|
||||
let else_m = else_br.as_ref().map(|e| self.visit(e.clone()));
|
||||
|
||||
let mut p = cond_m.ty.purity.min(then_m.ty.purity);
|
||||
if let Some(ref em) = else_m {
|
||||
p = p.min(em.ty.purity);
|
||||
}
|
||||
(
|
||||
BoundKind::If {
|
||||
cond: Rc::new(cond_m),
|
||||
then_br: Rc::new(then_m),
|
||||
else_br: else_m.map(Rc::new),
|
||||
},
|
||||
p,
|
||||
)
|
||||
}
|
||||
|
||||
BoundKind::Lambda {
|
||||
params,
|
||||
upvalues,
|
||||
body,
|
||||
positional_count,
|
||||
} => {
|
||||
self.lambda_stack.push(node.identity.clone());
|
||||
let params_m = self.visit(params.clone());
|
||||
let body_m = self.visit(body.clone());
|
||||
self.lambda_stack.pop();
|
||||
|
||||
is_recursive = self.recursive_identities.contains(&node.identity);
|
||||
(
|
||||
BoundKind::Lambda {
|
||||
params: Rc::new(params_m),
|
||||
upvalues: upvalues.clone(),
|
||||
body: Rc::new(body_m),
|
||||
positional_count: *positional_count,
|
||||
},
|
||||
Purity::Pure,
|
||||
)
|
||||
}
|
||||
|
||||
BoundKind::Destructure { pattern, value } => {
|
||||
let pat_m = self.visit(pattern.clone());
|
||||
let val_m = self.visit(value.clone());
|
||||
(
|
||||
BoundKind::Destructure {
|
||||
pattern: Rc::new(pat_m),
|
||||
value: Rc::new(val_m),
|
||||
},
|
||||
Purity::Impure,
|
||||
)
|
||||
}
|
||||
|
||||
BoundKind::Call { callee, args } => {
|
||||
let callee_m = self.visit(callee.clone());
|
||||
let args_m = self.visit(args.clone());
|
||||
|
||||
if let BoundKind::Get {
|
||||
addr: Address::Global(idx),
|
||||
..
|
||||
} = &callee.kind
|
||||
&& let Some(lambda_id) = self.globals_to_lambdas.get(idx)
|
||||
&& self.lambda_stack.contains(lambda_id)
|
||||
{
|
||||
self.recursive_identities.insert(lambda_id.clone());
|
||||
is_recursive = true;
|
||||
}
|
||||
|
||||
let p_func = if let BoundKind::Get {
|
||||
addr: Address::Global(idx),
|
||||
..
|
||||
} = &callee.kind
|
||||
{
|
||||
self.root_purity
|
||||
.get(idx.0 as usize)
|
||||
.cloned()
|
||||
.unwrap_or(Purity::Impure)
|
||||
} else {
|
||||
Purity::Impure
|
||||
};
|
||||
let p = callee_m.ty.purity.min(args_m.ty.purity).min(p_func);
|
||||
(
|
||||
BoundKind::Call {
|
||||
callee: Rc::new(callee_m),
|
||||
args: Rc::new(args_m),
|
||||
},
|
||||
p,
|
||||
)
|
||||
}
|
||||
|
||||
BoundKind::Again { args } => {
|
||||
let args_m = self.visit(args.clone());
|
||||
if let Some(lambda_id) = self.lambda_stack.last() {
|
||||
self.recursive_identities.insert(lambda_id.clone());
|
||||
is_recursive = true;
|
||||
}
|
||||
(
|
||||
BoundKind::Again {
|
||||
args: Rc::new(args_m),
|
||||
},
|
||||
Purity::Impure,
|
||||
)
|
||||
}
|
||||
BoundKind::Pipe {
|
||||
inputs,
|
||||
lambda,
|
||||
out_type,
|
||||
} => {
|
||||
let mut analyzed_inputs = Vec::with_capacity(inputs.len());
|
||||
for input in inputs {
|
||||
analyzed_inputs.push(Rc::new(self.visit(input.clone())));
|
||||
}
|
||||
let a_lambda = Rc::new(self.visit(lambda.clone()));
|
||||
(
|
||||
BoundKind::Pipe {
|
||||
inputs: analyzed_inputs,
|
||||
lambda: a_lambda,
|
||||
out_type: out_type.clone(),
|
||||
},
|
||||
Purity::Impure,
|
||||
)
|
||||
}
|
||||
|
||||
BoundKind::Block { exprs } => {
|
||||
let mut new_exprs = Vec::with_capacity(exprs.len());
|
||||
let mut p = Purity::Pure;
|
||||
for e in exprs {
|
||||
let em = self.visit(e.clone());
|
||||
p = p.min(em.ty.purity);
|
||||
new_exprs.push(Rc::new(em));
|
||||
}
|
||||
(BoundKind::Block { exprs: new_exprs }, p)
|
||||
}
|
||||
|
||||
BoundKind::Tuple { elements } => {
|
||||
let mut new_elements = Vec::with_capacity(elements.len());
|
||||
let mut p = Purity::Pure;
|
||||
for e in elements {
|
||||
let em = self.visit(e.clone());
|
||||
p = p.min(em.ty.purity);
|
||||
new_elements.push(Rc::new(em));
|
||||
}
|
||||
(
|
||||
BoundKind::Tuple {
|
||||
elements: new_elements,
|
||||
},
|
||||
p,
|
||||
)
|
||||
}
|
||||
|
||||
BoundKind::Record { layout, values } => {
|
||||
let mut new_values = Vec::with_capacity(values.len());
|
||||
let mut p = Purity::Pure;
|
||||
for v in values {
|
||||
let vm = self.visit(v.clone());
|
||||
p = p.min(vm.ty.purity);
|
||||
new_values.push(Rc::new(vm));
|
||||
}
|
||||
(
|
||||
BoundKind::Record {
|
||||
layout: layout.clone(),
|
||||
values: new_values,
|
||||
},
|
||||
p,
|
||||
)
|
||||
}
|
||||
|
||||
BoundKind::Expansion {
|
||||
original_call,
|
||||
bound_expanded,
|
||||
} => {
|
||||
let expanded_m = self.visit(bound_expanded.clone());
|
||||
(
|
||||
BoundKind::Expansion {
|
||||
original_call: original_call.clone(),
|
||||
bound_expanded: Rc::new(expanded_m.clone()),
|
||||
},
|
||||
expanded_m.ty.purity,
|
||||
)
|
||||
}
|
||||
|
||||
BoundKind::Extension(_) => (BoundKind::Nop, Purity::Impure),
|
||||
BoundKind::Error => (BoundKind::Error, Purity::Impure),
|
||||
};
|
||||
|
||||
crate::ast::nodes::Node {
|
||||
identity: node.identity.clone(),
|
||||
kind: new_kind,
|
||||
ty: NodeMetrics {
|
||||
original: node_rc,
|
||||
purity,
|
||||
is_recursive,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait NodeExt {
|
||||
fn for_each_child<F: FnMut(&TypedNode)>(&self, f: F);
|
||||
}
|
||||
|
||||
impl NodeExt for BoundKind<crate::ast::types::StaticType> {
|
||||
fn for_each_child<F: FnMut(&TypedNode)>(&self, mut f: F) {
|
||||
match self {
|
||||
BoundKind::If {
|
||||
cond,
|
||||
then_br,
|
||||
else_br,
|
||||
} => {
|
||||
f(cond);
|
||||
f(then_br);
|
||||
if let Some(e) = else_br {
|
||||
f(e);
|
||||
}
|
||||
}
|
||||
BoundKind::Define { value, .. } | BoundKind::Set { value, .. } => {
|
||||
f(value);
|
||||
}
|
||||
BoundKind::GetField { rec, .. } => {
|
||||
f(rec);
|
||||
}
|
||||
BoundKind::Lambda { params, body, .. } => {
|
||||
f(params);
|
||||
f(body);
|
||||
}
|
||||
BoundKind::Call { callee, args } => {
|
||||
f(callee);
|
||||
f(args);
|
||||
}
|
||||
BoundKind::Block { exprs } => {
|
||||
for e in exprs {
|
||||
f(e);
|
||||
}
|
||||
}
|
||||
BoundKind::Tuple { elements } => {
|
||||
for e in elements {
|
||||
f(e);
|
||||
}
|
||||
}
|
||||
BoundKind::Record { values, .. } => {
|
||||
for v in values {
|
||||
f(v);
|
||||
}
|
||||
}
|
||||
BoundKind::Expansion { bound_expanded, .. } => {
|
||||
f(bound_expanded);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
use crate::ast::compiler::bound_nodes::{
|
||||
Address, AnalyzedNode, BoundKind, GlobalIdx, NodeMetrics, TypedNode,
|
||||
};
|
||||
use crate::ast::types::Purity;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct Analyzer<'a> {
|
||||
root_purity: &'a [Purity],
|
||||
/// Stack of currently visiting lambdas to detect direct recursion.
|
||||
lambda_stack: Vec<crate::ast::types::Identity>,
|
||||
/// Map of global index to its Lambda identity if known.
|
||||
globals_to_lambdas: HashMap<GlobalIdx, crate::ast::types::Identity>,
|
||||
/// Set of identities that were found to be recursive.
|
||||
recursive_identities: HashSet<crate::ast::types::Identity>,
|
||||
}
|
||||
|
||||
impl<'a> Analyzer<'a> {
|
||||
pub fn analyze(
|
||||
node: &TypedNode,
|
||||
root_purity: &'a [Purity],
|
||||
) -> AnalyzedNode {
|
||||
let mut analyzer = Self {
|
||||
root_purity,
|
||||
lambda_stack: Vec::new(),
|
||||
globals_to_lambdas: HashMap::new(),
|
||||
recursive_identities: HashSet::new(),
|
||||
};
|
||||
|
||||
// First pass: map globals to their lambda identities
|
||||
analyzer.collect_globals(node);
|
||||
|
||||
// Second pass: full analysis (decorating TypedNode into AnalyzedNode)
|
||||
analyzer.visit(Rc::new(node.clone()))
|
||||
}
|
||||
|
||||
fn collect_globals(&mut self, node: &TypedNode) {
|
||||
match &node.kind {
|
||||
BoundKind::Define {
|
||||
addr: Address::Global(global_index),
|
||||
value,
|
||||
..
|
||||
} => {
|
||||
if let BoundKind::Lambda { .. } = &value.kind {
|
||||
self.globals_to_lambdas
|
||||
.insert(*global_index, value.identity.clone());
|
||||
}
|
||||
self.collect_globals(value);
|
||||
}
|
||||
BoundKind::Block { exprs } => {
|
||||
for e in exprs {
|
||||
self.collect_globals(e);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
node.kind
|
||||
.for_each_child(|child| self.collect_globals(child));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit(&mut self, node_rc: Rc<TypedNode>) -> AnalyzedNode {
|
||||
let node = &*node_rc;
|
||||
let mut is_recursive = false;
|
||||
|
||||
let (new_kind, purity) = match &node.kind {
|
||||
BoundKind::Constant(v) => (BoundKind::Constant(v.clone()), Purity::Pure),
|
||||
BoundKind::Nop => (BoundKind::Nop, Purity::Pure),
|
||||
|
||||
BoundKind::Get { addr, name } => {
|
||||
let p = match addr {
|
||||
Address::Global(idx) => {
|
||||
self.root_purity.get(idx.0 as usize).cloned().unwrap_or(Purity::Pure)
|
||||
}
|
||||
_ => Purity::Pure,
|
||||
};
|
||||
(
|
||||
BoundKind::Get {
|
||||
addr: *addr,
|
||||
name: name.clone(),
|
||||
},
|
||||
p,
|
||||
)
|
||||
}
|
||||
|
||||
BoundKind::FieldAccessor(k) => (BoundKind::FieldAccessor(*k), Purity::Pure),
|
||||
|
||||
BoundKind::GetField { rec, field } => {
|
||||
let rec_m = self.visit(rec.clone());
|
||||
let p = rec_m.ty.purity;
|
||||
(
|
||||
BoundKind::GetField {
|
||||
rec: Rc::new(rec_m),
|
||||
field: *field,
|
||||
},
|
||||
p,
|
||||
)
|
||||
}
|
||||
|
||||
BoundKind::Set { addr, value } => {
|
||||
let val_m = self.visit(value.clone());
|
||||
(
|
||||
BoundKind::Set {
|
||||
addr: *addr,
|
||||
value: Rc::new(val_m),
|
||||
},
|
||||
Purity::Impure,
|
||||
)
|
||||
}
|
||||
|
||||
BoundKind::Define {
|
||||
name,
|
||||
addr,
|
||||
kind,
|
||||
value,
|
||||
captured_by,
|
||||
} => {
|
||||
let val_m = self.visit(value.clone());
|
||||
(
|
||||
BoundKind::Define {
|
||||
name: name.clone(),
|
||||
addr: *addr,
|
||||
kind: *kind,
|
||||
value: Rc::new(val_m),
|
||||
captured_by: captured_by.clone(),
|
||||
},
|
||||
Purity::Impure,
|
||||
)
|
||||
}
|
||||
|
||||
BoundKind::If {
|
||||
cond,
|
||||
then_br,
|
||||
else_br,
|
||||
} => {
|
||||
let cond_m = self.visit(cond.clone());
|
||||
let then_m = self.visit(then_br.clone());
|
||||
let else_m = else_br.as_ref().map(|e| self.visit(e.clone()));
|
||||
|
||||
let mut p = cond_m.ty.purity.min(then_m.ty.purity);
|
||||
if let Some(ref em) = else_m {
|
||||
p = p.min(em.ty.purity);
|
||||
}
|
||||
(
|
||||
BoundKind::If {
|
||||
cond: Rc::new(cond_m),
|
||||
then_br: Rc::new(then_m),
|
||||
else_br: else_m.map(Rc::new),
|
||||
},
|
||||
p,
|
||||
)
|
||||
}
|
||||
|
||||
BoundKind::Lambda {
|
||||
params,
|
||||
upvalues,
|
||||
body,
|
||||
positional_count,
|
||||
} => {
|
||||
self.lambda_stack.push(node.identity.clone());
|
||||
let params_m = self.visit(params.clone());
|
||||
let body_m = self.visit(body.clone());
|
||||
self.lambda_stack.pop();
|
||||
|
||||
is_recursive = self.recursive_identities.contains(&node.identity);
|
||||
(
|
||||
BoundKind::Lambda {
|
||||
params: Rc::new(params_m),
|
||||
upvalues: upvalues.clone(),
|
||||
body: Rc::new(body_m),
|
||||
positional_count: *positional_count,
|
||||
},
|
||||
Purity::Pure,
|
||||
)
|
||||
}
|
||||
|
||||
BoundKind::Destructure { pattern, value } => {
|
||||
let pat_m = self.visit(pattern.clone());
|
||||
let val_m = self.visit(value.clone());
|
||||
(
|
||||
BoundKind::Destructure {
|
||||
pattern: Rc::new(pat_m),
|
||||
value: Rc::new(val_m),
|
||||
},
|
||||
Purity::Impure,
|
||||
)
|
||||
}
|
||||
|
||||
BoundKind::Call { callee, args } => {
|
||||
let callee_m = self.visit(callee.clone());
|
||||
let args_m = self.visit(args.clone());
|
||||
|
||||
if let BoundKind::Get {
|
||||
addr: Address::Global(idx),
|
||||
..
|
||||
} = &callee.kind
|
||||
&& let Some(lambda_id) = self.globals_to_lambdas.get(idx)
|
||||
&& self.lambda_stack.contains(lambda_id)
|
||||
{
|
||||
self.recursive_identities.insert(lambda_id.clone());
|
||||
is_recursive = true;
|
||||
}
|
||||
|
||||
let p_func = if let BoundKind::Get {
|
||||
addr: Address::Global(idx),
|
||||
..
|
||||
} = &callee.kind
|
||||
{
|
||||
self.root_purity
|
||||
.get(idx.0 as usize)
|
||||
.cloned()
|
||||
.unwrap_or(Purity::Impure)
|
||||
} else {
|
||||
Purity::Impure
|
||||
};
|
||||
let p = callee_m.ty.purity.min(args_m.ty.purity).min(p_func);
|
||||
(
|
||||
BoundKind::Call {
|
||||
callee: Rc::new(callee_m),
|
||||
args: Rc::new(args_m),
|
||||
},
|
||||
p,
|
||||
)
|
||||
}
|
||||
|
||||
BoundKind::Again { args } => {
|
||||
let args_m = self.visit(args.clone());
|
||||
if let Some(lambda_id) = self.lambda_stack.last() {
|
||||
self.recursive_identities.insert(lambda_id.clone());
|
||||
is_recursive = true;
|
||||
}
|
||||
(
|
||||
BoundKind::Again {
|
||||
args: Rc::new(args_m),
|
||||
},
|
||||
Purity::Impure,
|
||||
)
|
||||
}
|
||||
BoundKind::Pipe {
|
||||
inputs,
|
||||
lambda,
|
||||
out_type,
|
||||
} => {
|
||||
let mut analyzed_inputs = Vec::with_capacity(inputs.len());
|
||||
for input in inputs {
|
||||
analyzed_inputs.push(Rc::new(self.visit(input.clone())));
|
||||
}
|
||||
let a_lambda = Rc::new(self.visit(lambda.clone()));
|
||||
(
|
||||
BoundKind::Pipe {
|
||||
inputs: analyzed_inputs,
|
||||
lambda: a_lambda,
|
||||
out_type: out_type.clone(),
|
||||
},
|
||||
Purity::Impure,
|
||||
)
|
||||
}
|
||||
|
||||
BoundKind::Block { exprs } => {
|
||||
let mut new_exprs = Vec::with_capacity(exprs.len());
|
||||
let mut p = Purity::Pure;
|
||||
for e in exprs {
|
||||
let em = self.visit(e.clone());
|
||||
p = p.min(em.ty.purity);
|
||||
new_exprs.push(Rc::new(em));
|
||||
}
|
||||
(BoundKind::Block { exprs: new_exprs }, p)
|
||||
}
|
||||
|
||||
BoundKind::Tuple { elements } => {
|
||||
let mut new_elements = Vec::with_capacity(elements.len());
|
||||
let mut p = Purity::Pure;
|
||||
for e in elements {
|
||||
let em = self.visit(e.clone());
|
||||
p = p.min(em.ty.purity);
|
||||
new_elements.push(Rc::new(em));
|
||||
}
|
||||
(
|
||||
BoundKind::Tuple {
|
||||
elements: new_elements,
|
||||
},
|
||||
p,
|
||||
)
|
||||
}
|
||||
|
||||
BoundKind::Record { layout, values } => {
|
||||
let mut new_values = Vec::with_capacity(values.len());
|
||||
let mut p = Purity::Pure;
|
||||
for v in values {
|
||||
let vm = self.visit(v.clone());
|
||||
p = p.min(vm.ty.purity);
|
||||
new_values.push(Rc::new(vm));
|
||||
}
|
||||
(
|
||||
BoundKind::Record {
|
||||
layout: layout.clone(),
|
||||
values: new_values,
|
||||
},
|
||||
p,
|
||||
)
|
||||
}
|
||||
|
||||
BoundKind::Expansion {
|
||||
original_call,
|
||||
bound_expanded,
|
||||
} => {
|
||||
let expanded_m = self.visit(bound_expanded.clone());
|
||||
(
|
||||
BoundKind::Expansion {
|
||||
original_call: original_call.clone(),
|
||||
bound_expanded: Rc::new(expanded_m.clone()),
|
||||
},
|
||||
expanded_m.ty.purity,
|
||||
)
|
||||
}
|
||||
|
||||
BoundKind::Extension(_) => (BoundKind::Nop, Purity::Impure),
|
||||
BoundKind::Error => (BoundKind::Error, Purity::Impure),
|
||||
};
|
||||
|
||||
crate::ast::compiler::bound_nodes::Node {
|
||||
identity: node.identity.clone(),
|
||||
kind: new_kind,
|
||||
ty: NodeMetrics {
|
||||
original: node_rc,
|
||||
purity,
|
||||
is_recursive,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait NodeExt {
|
||||
fn for_each_child<F: FnMut(&TypedNode)>(&self, f: F);
|
||||
}
|
||||
|
||||
impl NodeExt for BoundKind<crate::ast::types::StaticType> {
|
||||
fn for_each_child<F: FnMut(&TypedNode)>(&self, mut f: F) {
|
||||
match self {
|
||||
BoundKind::If {
|
||||
cond,
|
||||
then_br,
|
||||
else_br,
|
||||
} => {
|
||||
f(cond);
|
||||
f(then_br);
|
||||
if let Some(e) = else_br {
|
||||
f(e);
|
||||
}
|
||||
}
|
||||
BoundKind::Define { value, .. } | BoundKind::Set { value, .. } => {
|
||||
f(value);
|
||||
}
|
||||
BoundKind::GetField { rec, .. } => {
|
||||
f(rec);
|
||||
}
|
||||
BoundKind::Lambda { params, body, .. } => {
|
||||
f(params);
|
||||
f(body);
|
||||
}
|
||||
BoundKind::Call { callee, args } => {
|
||||
f(callee);
|
||||
f(args);
|
||||
}
|
||||
BoundKind::Block { exprs } => {
|
||||
for e in exprs {
|
||||
f(e);
|
||||
}
|
||||
}
|
||||
BoundKind::Tuple { elements } => {
|
||||
for e in elements {
|
||||
f(e);
|
||||
}
|
||||
}
|
||||
BoundKind::Record { values, .. } => {
|
||||
for v in values {
|
||||
f(v);
|
||||
}
|
||||
}
|
||||
BoundKind::Expansion { bound_expanded, .. } => {
|
||||
f(bound_expanded);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user