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:
@@ -1,5 +1,3 @@
|
||||
;; Benchmark: 19.9us
|
||||
;; Benchmark-Repeat: 110
|
||||
(do
|
||||
(repeat n 10 (print n)
|
||||
)
|
||||
|
||||
+387
-386
@@ -1,386 +1,387 @@
|
||||
use crate::ast::compiler::bound_nodes::{
|
||||
Address, AnalyzedNode, BoundKind, GlobalIdx, Node, NodeMetrics, TypedNode, TypedPhase,
|
||||
};
|
||||
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),
|
||||
};
|
||||
|
||||
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<TypedPhase> {
|
||||
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, GlobalIdx, IdentifierBinding, Node,
|
||||
NodeKind, NodeMetrics, TypedNode, TypedPhase,
|
||||
};
|
||||
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 {
|
||||
NodeKind::Def {
|
||||
pattern,
|
||||
value,
|
||||
..
|
||||
} => {
|
||||
if let NodeKind::Identifier {
|
||||
binding: IdentifierBinding::Declaration { addr: Address::Global(global_index), .. },
|
||||
..
|
||||
} = &pattern.kind
|
||||
&& let NodeKind::Lambda { .. } = &value.kind
|
||||
{
|
||||
self.globals_to_lambdas
|
||||
.insert(*global_index, value.identity.clone());
|
||||
}
|
||||
self.collect_globals(value);
|
||||
}
|
||||
NodeKind::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 {
|
||||
NodeKind::Constant(v) => (NodeKind::Constant(v.clone()), Purity::Pure),
|
||||
NodeKind::Nop => (NodeKind::Nop, Purity::Pure),
|
||||
|
||||
NodeKind::Identifier { symbol, binding } => {
|
||||
let p = if let IdentifierBinding::Reference(Address::Global(idx)) = binding {
|
||||
self.root_purity.get(idx.0 as usize).cloned().unwrap_or(Purity::Pure)
|
||||
} else {
|
||||
Purity::Pure
|
||||
};
|
||||
(
|
||||
NodeKind::Identifier {
|
||||
symbol: symbol.clone(),
|
||||
binding: binding.clone(),
|
||||
},
|
||||
p,
|
||||
)
|
||||
}
|
||||
|
||||
NodeKind::FieldAccessor(k) => (NodeKind::FieldAccessor(*k), Purity::Pure),
|
||||
|
||||
NodeKind::GetField { rec, field } => {
|
||||
let rec_m = self.visit(rec.clone());
|
||||
let p = rec_m.ty.purity;
|
||||
(
|
||||
NodeKind::GetField {
|
||||
rec: Rc::new(rec_m),
|
||||
field: *field,
|
||||
},
|
||||
p,
|
||||
)
|
||||
}
|
||||
|
||||
NodeKind::Assign { target, value, info } => {
|
||||
let target_m = self.visit(target.clone());
|
||||
let val_m = self.visit(value.clone());
|
||||
(
|
||||
NodeKind::Assign {
|
||||
target: Rc::new(target_m),
|
||||
value: Rc::new(val_m),
|
||||
info: info.clone(),
|
||||
},
|
||||
Purity::Impure,
|
||||
)
|
||||
}
|
||||
|
||||
NodeKind::Def {
|
||||
pattern,
|
||||
value,
|
||||
info,
|
||||
} => {
|
||||
let pat_m = self.visit(pattern.clone());
|
||||
let val_m = self.visit(value.clone());
|
||||
(
|
||||
NodeKind::Def {
|
||||
pattern: Rc::new(pat_m),
|
||||
value: Rc::new(val_m),
|
||||
info: info.clone(),
|
||||
},
|
||||
Purity::Impure,
|
||||
)
|
||||
}
|
||||
|
||||
NodeKind::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);
|
||||
}
|
||||
(
|
||||
NodeKind::If {
|
||||
cond: Rc::new(cond_m),
|
||||
then_br: Rc::new(then_m),
|
||||
else_br: else_m.map(Rc::new),
|
||||
},
|
||||
p,
|
||||
)
|
||||
}
|
||||
|
||||
NodeKind::Lambda {
|
||||
params,
|
||||
body,
|
||||
info,
|
||||
} => {
|
||||
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);
|
||||
(
|
||||
NodeKind::Lambda {
|
||||
params: Rc::new(params_m),
|
||||
body: Rc::new(body_m),
|
||||
info: info.clone(),
|
||||
},
|
||||
Purity::Pure,
|
||||
)
|
||||
}
|
||||
|
||||
NodeKind::Call { callee, args } => {
|
||||
let callee_m = self.visit(callee.clone());
|
||||
let args_m = self.visit(args.clone());
|
||||
|
||||
if let NodeKind::Identifier {
|
||||
binding: IdentifierBinding::Reference(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 NodeKind::Identifier {
|
||||
binding: IdentifierBinding::Reference(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);
|
||||
(
|
||||
NodeKind::Call {
|
||||
callee: Rc::new(callee_m),
|
||||
args: Rc::new(args_m),
|
||||
},
|
||||
p,
|
||||
)
|
||||
}
|
||||
|
||||
NodeKind::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;
|
||||
}
|
||||
(
|
||||
NodeKind::Again {
|
||||
args: Rc::new(args_m),
|
||||
},
|
||||
Purity::Impure,
|
||||
)
|
||||
}
|
||||
NodeKind::Pipe {
|
||||
inputs,
|
||||
lambda,
|
||||
} => {
|
||||
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()));
|
||||
(
|
||||
NodeKind::Pipe {
|
||||
inputs: analyzed_inputs,
|
||||
lambda: a_lambda,
|
||||
},
|
||||
Purity::Impure,
|
||||
)
|
||||
}
|
||||
|
||||
NodeKind::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));
|
||||
}
|
||||
(NodeKind::Block { exprs: new_exprs }, p)
|
||||
}
|
||||
|
||||
NodeKind::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));
|
||||
}
|
||||
(
|
||||
NodeKind::Tuple {
|
||||
elements: new_elements,
|
||||
},
|
||||
p,
|
||||
)
|
||||
}
|
||||
|
||||
NodeKind::Record { fields, layout } => {
|
||||
let mut new_fields = Vec::with_capacity(fields.len());
|
||||
let mut p = Purity::Pure;
|
||||
for (key_node, val_node) in fields {
|
||||
let km = self.visit(key_node.clone());
|
||||
let vm = self.visit(val_node.clone());
|
||||
p = p.min(vm.ty.purity);
|
||||
new_fields.push((Rc::new(km), Rc::new(vm)));
|
||||
}
|
||||
(
|
||||
NodeKind::Record {
|
||||
fields: new_fields,
|
||||
layout: layout.clone(),
|
||||
},
|
||||
p,
|
||||
)
|
||||
}
|
||||
|
||||
NodeKind::Expansion {
|
||||
original_call,
|
||||
expanded,
|
||||
} => {
|
||||
let expanded_m = self.visit(expanded.clone());
|
||||
(
|
||||
NodeKind::Expansion {
|
||||
original_call: original_call.clone(),
|
||||
expanded: Rc::new(expanded_m.clone()),
|
||||
},
|
||||
expanded_m.ty.purity,
|
||||
)
|
||||
}
|
||||
|
||||
NodeKind::Extension(_) => (NodeKind::Nop, Purity::Impure),
|
||||
NodeKind::Error => (NodeKind::Error, Purity::Impure),
|
||||
|
||||
// Syntax-only variants should not appear in typed phases
|
||||
NodeKind::MacroDecl { .. }
|
||||
| NodeKind::Template(_)
|
||||
| NodeKind::Placeholder(_)
|
||||
| NodeKind::Splice(_) => (NodeKind::Error, Purity::Impure),
|
||||
};
|
||||
|
||||
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 NodeKind<TypedPhase> {
|
||||
fn for_each_child<F: FnMut(&TypedNode)>(&self, mut f: F) {
|
||||
match self {
|
||||
NodeKind::If {
|
||||
cond,
|
||||
then_br,
|
||||
else_br,
|
||||
} => {
|
||||
f(cond);
|
||||
f(then_br);
|
||||
if let Some(e) = else_br {
|
||||
f(e);
|
||||
}
|
||||
}
|
||||
NodeKind::Def { pattern, value, .. } => {
|
||||
f(pattern);
|
||||
f(value);
|
||||
}
|
||||
NodeKind::Assign { target, value, .. } => {
|
||||
f(target);
|
||||
f(value);
|
||||
}
|
||||
NodeKind::GetField { rec, .. } => {
|
||||
f(rec);
|
||||
}
|
||||
NodeKind::Lambda { params, body, .. } => {
|
||||
f(params);
|
||||
f(body);
|
||||
}
|
||||
NodeKind::Call { callee, args } => {
|
||||
f(callee);
|
||||
f(args);
|
||||
}
|
||||
NodeKind::Block { exprs } => {
|
||||
for e in exprs {
|
||||
f(e);
|
||||
}
|
||||
}
|
||||
NodeKind::Tuple { elements } => {
|
||||
for e in elements {
|
||||
f(e);
|
||||
}
|
||||
}
|
||||
NodeKind::Record { fields, .. } => {
|
||||
for (key, val) in fields {
|
||||
f(key);
|
||||
f(val);
|
||||
}
|
||||
}
|
||||
NodeKind::Expansion { expanded, .. } => {
|
||||
f(expanded);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+828
-789
File diff suppressed because it is too large
Load Diff
@@ -181,11 +181,39 @@ impl CompilerPhase for RuntimePhase {
|
||||
type RecordLayout = Arc<crate::ast::types::RecordLayout>;
|
||||
}
|
||||
|
||||
/// A bound AST node, decorated with phase-specific information P.
|
||||
/// Convenience alias for all post-binding phases that share the same
|
||||
/// concrete binding, def, assign, lambda, and record-layout types.
|
||||
/// Covers `BoundPhase`, `TypedPhase`, `AnalyzedPhase` (but not `SyntaxPhase` or `RuntimePhase`).
|
||||
pub trait BoundLike:
|
||||
CompilerPhase<
|
||||
LocalAddress = VirtualId,
|
||||
Binding = IdentifierBinding<VirtualId>,
|
||||
DefInfo = DefBinding,
|
||||
AssignInfo = AssignBinding<VirtualId>,
|
||||
LambdaInfo = LambdaBinding<VirtualId>,
|
||||
RecordLayout = Arc<crate::ast::types::RecordLayout>,
|
||||
>
|
||||
{
|
||||
}
|
||||
|
||||
impl<P> BoundLike for P where
|
||||
P: CompilerPhase<
|
||||
LocalAddress = VirtualId,
|
||||
Binding = IdentifierBinding<VirtualId>,
|
||||
DefInfo = DefBinding,
|
||||
AssignInfo = AssignBinding<VirtualId>,
|
||||
LambdaInfo = LambdaBinding<VirtualId>,
|
||||
RecordLayout = Arc<crate::ast::types::RecordLayout>,
|
||||
>
|
||||
{
|
||||
}
|
||||
|
||||
/// A unified AST node, decorated with phase-specific information P.
|
||||
/// Replaces both `SyntaxNode` (parser output) and the old bound AST node.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct Node<P: CompilerPhase = BoundPhase> {
|
||||
pub identity: Identity,
|
||||
pub kind: BoundKind<P>,
|
||||
pub kind: NodeKind<P>,
|
||||
pub ty: P::Metadata,
|
||||
}
|
||||
|
||||
@@ -418,7 +446,7 @@ impl<P: CompilerPhase> BoundKind<P> {
|
||||
BoundKind::Destructure { .. } => "DESTRUCTURE".to_string(),
|
||||
BoundKind::Lambda { params, upvalues, .. } => {
|
||||
let p_str = match ¶ms.kind {
|
||||
BoundKind::Tuple { elements } => format!("p:{}", elements.len()),
|
||||
NodeKind::Tuple { elements } => format!("p:{}", elements.len()),
|
||||
_ => "p:1".to_string(),
|
||||
};
|
||||
format!("LAMBDA({}, Captures:{})", p_str, upvalues.len())
|
||||
|
||||
+122
-129
@@ -1,129 +1,122 @@
|
||||
use crate::ast::compiler::bound_nodes::{BoundKind, CompilerPhase, Node};
|
||||
use crate::ast::types::Identity;
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct CapturePass;
|
||||
|
||||
impl CapturePass {
|
||||
pub fn apply<P: CompilerPhase>(node: Node<P>, capture_map: &HashMap<Identity, Vec<Identity>>) -> Node<P> {
|
||||
Self::transform(node, capture_map)
|
||||
}
|
||||
|
||||
fn transform<P: CompilerPhase>(mut node: Node<P>, capture_map: &HashMap<Identity, Vec<Identity>>) -> Node<P> {
|
||||
node.kind = match node.kind {
|
||||
BoundKind::Define {
|
||||
name,
|
||||
addr,
|
||||
kind,
|
||||
value,
|
||||
mut captured_by,
|
||||
} => {
|
||||
if let Some(capturers) = capture_map.get(&node.identity) {
|
||||
captured_by.extend(capturers.iter().cloned());
|
||||
}
|
||||
BoundKind::Define {
|
||||
name,
|
||||
addr,
|
||||
kind,
|
||||
value: Rc::new(Self::transform(value.as_ref().clone(), capture_map)),
|
||||
captured_by,
|
||||
}
|
||||
}
|
||||
|
||||
BoundKind::Lambda {
|
||||
params,
|
||||
upvalues,
|
||||
body,
|
||||
positional_count,
|
||||
} => BoundKind::Lambda {
|
||||
params: Rc::new(Self::transform(params.as_ref().clone(), capture_map)),
|
||||
upvalues,
|
||||
body: Rc::new(Self::transform(body.as_ref().clone(), capture_map)),
|
||||
positional_count,
|
||||
},
|
||||
|
||||
BoundKind::Call { callee, args } => BoundKind::Call {
|
||||
callee: Rc::new(Self::transform(callee.as_ref().clone(), capture_map)),
|
||||
args: Rc::new(Self::transform(args.as_ref().clone(), capture_map)),
|
||||
},
|
||||
|
||||
BoundKind::Again { args } => BoundKind::Again {
|
||||
args: Rc::new(Self::transform(args.as_ref().clone(), capture_map)),
|
||||
},
|
||||
|
||||
BoundKind::If {
|
||||
cond,
|
||||
then_br,
|
||||
else_br,
|
||||
} => BoundKind::If {
|
||||
cond: Rc::new(Self::transform(cond.as_ref().clone(), capture_map)),
|
||||
then_br: Rc::new(Self::transform(then_br.as_ref().clone(), capture_map)),
|
||||
else_br: else_br
|
||||
.map(|e| Rc::new(Self::transform(e.as_ref().clone(), capture_map))),
|
||||
},
|
||||
|
||||
BoundKind::Destructure { pattern, value } => BoundKind::Destructure {
|
||||
pattern: Rc::new(Self::transform(pattern.as_ref().clone(), capture_map)),
|
||||
value: Rc::new(Self::transform(value.as_ref().clone(), capture_map)),
|
||||
},
|
||||
|
||||
BoundKind::Pipe {
|
||||
inputs,
|
||||
lambda,
|
||||
out_type,
|
||||
} => {
|
||||
let mut t_inputs = Vec::with_capacity(inputs.len());
|
||||
for input in inputs {
|
||||
t_inputs.push(Rc::new(Self::transform(input.as_ref().clone(), capture_map)));
|
||||
}
|
||||
BoundKind::Pipe {
|
||||
inputs: t_inputs,
|
||||
lambda: Rc::new(Self::transform(lambda.as_ref().clone(), capture_map)),
|
||||
out_type,
|
||||
}
|
||||
}
|
||||
|
||||
BoundKind::Block { exprs } => BoundKind::Block {
|
||||
exprs: exprs
|
||||
.into_iter()
|
||||
.map(|e| Rc::new(Self::transform(e.as_ref().clone(), capture_map)))
|
||||
.collect(),
|
||||
},
|
||||
|
||||
BoundKind::Tuple { elements } => BoundKind::Tuple {
|
||||
elements: elements
|
||||
.into_iter()
|
||||
.map(|e| Rc::new(Self::transform(e.as_ref().clone(), capture_map)))
|
||||
.collect(),
|
||||
},
|
||||
|
||||
BoundKind::Record { layout, values } => BoundKind::Record {
|
||||
layout,
|
||||
values: values
|
||||
.into_iter()
|
||||
.map(|v| Rc::new(Self::transform(v.as_ref().clone(), capture_map)))
|
||||
.collect(),
|
||||
},
|
||||
|
||||
BoundKind::Expansion {
|
||||
original_call,
|
||||
bound_expanded,
|
||||
} => BoundKind::Expansion {
|
||||
original_call,
|
||||
bound_expanded: Rc::new(Self::transform(
|
||||
bound_expanded.as_ref().clone(),
|
||||
capture_map,
|
||||
)),
|
||||
},
|
||||
|
||||
BoundKind::GetField { rec, field } => BoundKind::GetField {
|
||||
rec: Rc::new(Self::transform(rec.as_ref().clone(), capture_map)),
|
||||
field,
|
||||
},
|
||||
|
||||
other => other,
|
||||
};
|
||||
node
|
||||
}
|
||||
}
|
||||
use crate::ast::compiler::bound_nodes::{CompilerPhase, DefBinding, Node, NodeKind};
|
||||
use crate::ast::types::Identity;
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct CapturePass;
|
||||
|
||||
impl CapturePass {
|
||||
pub fn apply<P: CompilerPhase<DefInfo = DefBinding>>(node: Node<P>, capture_map: &HashMap<Identity, Vec<Identity>>) -> Node<P> {
|
||||
Self::transform(node, capture_map)
|
||||
}
|
||||
|
||||
fn transform<P: CompilerPhase<DefInfo = DefBinding>>(mut node: Node<P>, capture_map: &HashMap<Identity, Vec<Identity>>) -> Node<P> {
|
||||
node.kind = match node.kind {
|
||||
NodeKind::Def {
|
||||
pattern,
|
||||
value,
|
||||
mut info,
|
||||
} => {
|
||||
if let Some(capturers) = capture_map.get(&node.identity) {
|
||||
info.captured_by.extend(capturers.iter().cloned());
|
||||
}
|
||||
NodeKind::Def {
|
||||
pattern: Rc::new(Self::transform(pattern.as_ref().clone(), capture_map)),
|
||||
value: Rc::new(Self::transform(value.as_ref().clone(), capture_map)),
|
||||
info,
|
||||
}
|
||||
}
|
||||
|
||||
NodeKind::Lambda {
|
||||
params,
|
||||
body,
|
||||
info,
|
||||
} => NodeKind::Lambda {
|
||||
params: Rc::new(Self::transform(params.as_ref().clone(), capture_map)),
|
||||
body: Rc::new(Self::transform(body.as_ref().clone(), capture_map)),
|
||||
info,
|
||||
},
|
||||
|
||||
NodeKind::Call { callee, args } => NodeKind::Call {
|
||||
callee: Rc::new(Self::transform(callee.as_ref().clone(), capture_map)),
|
||||
args: Rc::new(Self::transform(args.as_ref().clone(), capture_map)),
|
||||
},
|
||||
|
||||
NodeKind::Again { args } => NodeKind::Again {
|
||||
args: Rc::new(Self::transform(args.as_ref().clone(), capture_map)),
|
||||
},
|
||||
|
||||
NodeKind::If {
|
||||
cond,
|
||||
then_br,
|
||||
else_br,
|
||||
} => NodeKind::If {
|
||||
cond: Rc::new(Self::transform(cond.as_ref().clone(), capture_map)),
|
||||
then_br: Rc::new(Self::transform(then_br.as_ref().clone(), capture_map)),
|
||||
else_br: else_br
|
||||
.map(|e| Rc::new(Self::transform(e.as_ref().clone(), capture_map))),
|
||||
},
|
||||
|
||||
NodeKind::Assign { target, value, info } => NodeKind::Assign {
|
||||
target: Rc::new(Self::transform(target.as_ref().clone(), capture_map)),
|
||||
value: Rc::new(Self::transform(value.as_ref().clone(), capture_map)),
|
||||
info,
|
||||
},
|
||||
|
||||
NodeKind::Pipe {
|
||||
inputs,
|
||||
lambda,
|
||||
} => {
|
||||
let mut t_inputs = Vec::with_capacity(inputs.len());
|
||||
for input in inputs {
|
||||
t_inputs.push(Rc::new(Self::transform(input.as_ref().clone(), capture_map)));
|
||||
}
|
||||
NodeKind::Pipe {
|
||||
inputs: t_inputs,
|
||||
lambda: Rc::new(Self::transform(lambda.as_ref().clone(), capture_map)),
|
||||
}
|
||||
}
|
||||
|
||||
NodeKind::Block { exprs } => NodeKind::Block {
|
||||
exprs: exprs
|
||||
.into_iter()
|
||||
.map(|e| Rc::new(Self::transform(e.as_ref().clone(), capture_map)))
|
||||
.collect(),
|
||||
},
|
||||
|
||||
NodeKind::Tuple { elements } => NodeKind::Tuple {
|
||||
elements: elements
|
||||
.into_iter()
|
||||
.map(|e| Rc::new(Self::transform(e.as_ref().clone(), capture_map)))
|
||||
.collect(),
|
||||
},
|
||||
|
||||
NodeKind::Record { fields, layout } => NodeKind::Record {
|
||||
fields: fields
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k, Rc::new(Self::transform(v.as_ref().clone(), capture_map))))
|
||||
.collect(),
|
||||
layout,
|
||||
},
|
||||
|
||||
NodeKind::Expansion {
|
||||
original_call,
|
||||
expanded,
|
||||
} => NodeKind::Expansion {
|
||||
original_call,
|
||||
expanded: Rc::new(Self::transform(
|
||||
expanded.as_ref().clone(),
|
||||
capture_map,
|
||||
)),
|
||||
},
|
||||
|
||||
NodeKind::GetField { rec, field } => NodeKind::GetField {
|
||||
rec: Rc::new(Self::transform(rec.as_ref().clone(), capture_map)),
|
||||
field,
|
||||
},
|
||||
|
||||
other => other,
|
||||
};
|
||||
node
|
||||
}
|
||||
}
|
||||
|
||||
+240
-239
@@ -1,239 +1,240 @@
|
||||
use crate::ast::compiler::bound_nodes::{BoundKind, CompilerPhase, Node};
|
||||
|
||||
/// Human-readable AST dumper for the bound AST.
|
||||
pub struct Dumper {
|
||||
output: String,
|
||||
indent: usize,
|
||||
}
|
||||
|
||||
impl Dumper {
|
||||
/// Produces a formatted string representation of the given bound AST node and its children.
|
||||
pub fn dump<P: CompilerPhase>(node: &Node<P>) -> String {
|
||||
let mut dumper = Self {
|
||||
output: String::new(),
|
||||
indent: 0,
|
||||
};
|
||||
dumper.visit(node);
|
||||
dumper.output
|
||||
}
|
||||
|
||||
fn write_indent(&mut self) {
|
||||
for _ in 0..self.indent {
|
||||
self.output.push_str(" ");
|
||||
}
|
||||
}
|
||||
|
||||
fn log<P: CompilerPhase>(&mut self, label: &str, node: &Node<P>) {
|
||||
self.write_indent();
|
||||
self.output.push_str(label);
|
||||
self.output
|
||||
.push_str(&format!(" <Metadata: {:?}>\n", node.ty));
|
||||
}
|
||||
|
||||
fn visit<P: CompilerPhase>(&mut self, node: &Node<P>) {
|
||||
match &node.kind {
|
||||
BoundKind::Nop => self.log("Nop", node),
|
||||
BoundKind::Constant(v) => {
|
||||
self.log(&format!("Constant: {}", v), node);
|
||||
}
|
||||
BoundKind::Get { addr, name } => {
|
||||
self.log(&format!("Get: {} ({:?})", name.name, addr), node)
|
||||
}
|
||||
|
||||
BoundKind::FieldAccessor(k) => self.log(&format!("FieldAccessor: .{}", k.name()), node),
|
||||
|
||||
BoundKind::GetField { rec, field } => {
|
||||
self.log(&format!("GetField: .{}", field.name()), node);
|
||||
self.indent += 1;
|
||||
self.visit(rec);
|
||||
self.indent -= 1;
|
||||
}
|
||||
|
||||
BoundKind::Set { addr, value } => {
|
||||
self.log(&format!("Set: {:?}", addr), node);
|
||||
self.indent += 1;
|
||||
self.visit(value);
|
||||
self.indent -= 1;
|
||||
}
|
||||
|
||||
BoundKind::Define {
|
||||
name,
|
||||
addr,
|
||||
kind,
|
||||
value,
|
||||
captured_by,
|
||||
} => {
|
||||
let k_str = match kind {
|
||||
crate::ast::compiler::bound_nodes::DeclarationKind::Variable => "Variable",
|
||||
crate::ast::compiler::bound_nodes::DeclarationKind::Parameter => "Parameter",
|
||||
};
|
||||
let capture_info = if captured_by.is_empty() {
|
||||
String::from("not captured")
|
||||
} else {
|
||||
format!("captured by {} lambdas", captured_by.len())
|
||||
};
|
||||
self.log(
|
||||
&format!(
|
||||
"Define {} (Name: '{}', Address: {:?}, {})",
|
||||
k_str, name.name, addr, capture_info
|
||||
),
|
||||
node,
|
||||
);
|
||||
|
||||
self.indent += 1;
|
||||
if !captured_by.is_empty() {
|
||||
for capturer in captured_by {
|
||||
self.write_indent();
|
||||
let loc = capturer
|
||||
.location
|
||||
.unwrap_or(crate::ast::types::SourceLocation { line: 0, col: 0 });
|
||||
self.output.push_str(&format!(
|
||||
"- Capturer: Lambda at line {}, col {}\n",
|
||||
loc.line, loc.col
|
||||
));
|
||||
}
|
||||
}
|
||||
self.visit(value);
|
||||
self.indent -= 1;
|
||||
}
|
||||
|
||||
BoundKind::Destructure { pattern, value } => {
|
||||
self.log("Destructure", node);
|
||||
self.indent += 1;
|
||||
self.write_indent();
|
||||
self.output.push_str("Pattern:\n");
|
||||
self.visit(pattern);
|
||||
self.write_indent();
|
||||
self.output.push_str("Value:\n");
|
||||
self.visit(value);
|
||||
self.indent -= 1;
|
||||
}
|
||||
|
||||
BoundKind::If {
|
||||
cond,
|
||||
then_br,
|
||||
else_br,
|
||||
} => {
|
||||
self.log("If", node);
|
||||
self.indent += 1;
|
||||
|
||||
self.write_indent();
|
||||
self.output.push_str("Condition:\n");
|
||||
self.visit(cond);
|
||||
|
||||
self.write_indent();
|
||||
self.output.push_str("Then:\n");
|
||||
self.visit(then_br);
|
||||
|
||||
if let Some(e) = else_br {
|
||||
self.write_indent();
|
||||
self.output.push_str("Else:\n");
|
||||
self.visit(e);
|
||||
}
|
||||
self.indent -= 1;
|
||||
}
|
||||
|
||||
BoundKind::Lambda {
|
||||
params,
|
||||
upvalues,
|
||||
body,
|
||||
..
|
||||
} => {
|
||||
self.log(&format!("Lambda (Upvalues: {})", upvalues.len()), node);
|
||||
self.indent += 1;
|
||||
|
||||
self.write_indent();
|
||||
self.output.push_str("Parameters:\n");
|
||||
self.visit(params);
|
||||
|
||||
if !upvalues.is_empty() {
|
||||
self.write_indent();
|
||||
self.output.push_str(&format!("Upvalues: {:?}\n", upvalues));
|
||||
}
|
||||
self.visit(body);
|
||||
self.indent -= 1;
|
||||
}
|
||||
|
||||
BoundKind::Call { callee, args } => {
|
||||
self.log("Call", node);
|
||||
self.indent += 1;
|
||||
|
||||
self.write_indent();
|
||||
self.output.push_str("Callee:\n");
|
||||
self.visit(callee);
|
||||
|
||||
self.write_indent();
|
||||
self.output.push_str("Arguments:\n");
|
||||
self.visit(args);
|
||||
|
||||
self.indent -= 1;
|
||||
}
|
||||
|
||||
BoundKind::Again { args } => {
|
||||
self.log("Again", node);
|
||||
self.indent += 1;
|
||||
self.visit(args);
|
||||
self.indent -= 1;
|
||||
}
|
||||
BoundKind::Pipe { inputs, lambda, .. } => {
|
||||
self.log("Pipe", node);
|
||||
self.indent += 1;
|
||||
for input in inputs {
|
||||
self.visit(input);
|
||||
}
|
||||
self.visit(lambda);
|
||||
self.indent -= 1;
|
||||
}
|
||||
|
||||
BoundKind::Block { exprs } => {
|
||||
self.log("Block", node);
|
||||
self.indent += 1;
|
||||
for expr in exprs {
|
||||
self.visit(expr);
|
||||
}
|
||||
self.indent -= 1;
|
||||
}
|
||||
|
||||
BoundKind::Tuple { elements } => {
|
||||
self.log("Tuple", node);
|
||||
self.indent += 1;
|
||||
for el in elements {
|
||||
self.visit(el);
|
||||
}
|
||||
self.indent -= 1;
|
||||
}
|
||||
|
||||
BoundKind::Record { layout, values } => {
|
||||
self.log(
|
||||
&format!("Record (Layout: {} fields)", layout.fields.len()),
|
||||
node,
|
||||
);
|
||||
self.indent += 1;
|
||||
for v in values {
|
||||
self.visit(v);
|
||||
}
|
||||
self.indent -= 1;
|
||||
}
|
||||
|
||||
BoundKind::Expansion {
|
||||
original_call,
|
||||
bound_expanded,
|
||||
} => {
|
||||
self.log(
|
||||
&format!("Expansion (Original: {:?})", original_call.kind),
|
||||
node,
|
||||
);
|
||||
self.indent += 1;
|
||||
self.visit(bound_expanded);
|
||||
self.indent -= 1;
|
||||
}
|
||||
|
||||
BoundKind::Extension(ext) => {
|
||||
self.log(&ext.display_name(), node);
|
||||
}
|
||||
BoundKind::Error => {
|
||||
self.log("ERROR_NODE", node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
use crate::ast::compiler::bound_nodes::{CompilerPhase, Node, NodeKind};
|
||||
|
||||
/// Human-readable AST dumper for the bound AST.
|
||||
pub struct Dumper {
|
||||
output: String,
|
||||
indent: usize,
|
||||
}
|
||||
|
||||
impl Dumper {
|
||||
/// Produces a formatted string representation of the given bound AST node and its children.
|
||||
pub fn dump<P: CompilerPhase>(node: &Node<P>) -> String {
|
||||
let mut dumper = Self {
|
||||
output: String::new(),
|
||||
indent: 0,
|
||||
};
|
||||
dumper.visit(node);
|
||||
dumper.output
|
||||
}
|
||||
|
||||
fn write_indent(&mut self) {
|
||||
for _ in 0..self.indent {
|
||||
self.output.push_str(" ");
|
||||
}
|
||||
}
|
||||
|
||||
fn log<P: CompilerPhase>(&mut self, label: &str, node: &Node<P>) {
|
||||
self.write_indent();
|
||||
self.output.push_str(label);
|
||||
self.output
|
||||
.push_str(&format!(" <Metadata: {:?}>\n", node.ty));
|
||||
}
|
||||
|
||||
fn visit<P: CompilerPhase>(&mut self, node: &Node<P>) {
|
||||
match &node.kind {
|
||||
NodeKind::Nop => self.log("Nop", node),
|
||||
NodeKind::Constant(v) => {
|
||||
self.log(&format!("Constant: {}", v), node);
|
||||
}
|
||||
NodeKind::Identifier { symbol, binding } => {
|
||||
self.log(&format!("Identifier: {} ({:?})", symbol.name, binding), node)
|
||||
}
|
||||
|
||||
NodeKind::FieldAccessor(k) => self.log(&format!("FieldAccessor: .{}", k.name()), node),
|
||||
|
||||
NodeKind::GetField { rec, field } => {
|
||||
self.log(&format!("GetField: .{}", field.name()), node);
|
||||
self.indent += 1;
|
||||
self.visit(rec);
|
||||
self.indent -= 1;
|
||||
}
|
||||
|
||||
NodeKind::Assign { target, value, info } => {
|
||||
self.log(&format!("Assign: {:?}", info), node);
|
||||
self.indent += 1;
|
||||
self.write_indent();
|
||||
self.output.push_str("Target:\n");
|
||||
self.visit(target);
|
||||
self.write_indent();
|
||||
self.output.push_str("Value:\n");
|
||||
self.visit(value);
|
||||
self.indent -= 1;
|
||||
}
|
||||
|
||||
NodeKind::Def {
|
||||
pattern,
|
||||
value,
|
||||
info,
|
||||
} => {
|
||||
self.log(&format!("Def ({:?})", info), node);
|
||||
self.indent += 1;
|
||||
self.write_indent();
|
||||
self.output.push_str("Pattern:\n");
|
||||
self.visit(pattern);
|
||||
self.write_indent();
|
||||
self.output.push_str("Value:\n");
|
||||
self.visit(value);
|
||||
self.indent -= 1;
|
||||
}
|
||||
|
||||
NodeKind::If {
|
||||
cond,
|
||||
then_br,
|
||||
else_br,
|
||||
} => {
|
||||
self.log("If", node);
|
||||
self.indent += 1;
|
||||
|
||||
self.write_indent();
|
||||
self.output.push_str("Condition:\n");
|
||||
self.visit(cond);
|
||||
|
||||
self.write_indent();
|
||||
self.output.push_str("Then:\n");
|
||||
self.visit(then_br);
|
||||
|
||||
if let Some(e) = else_br {
|
||||
self.write_indent();
|
||||
self.output.push_str("Else:\n");
|
||||
self.visit(e);
|
||||
}
|
||||
self.indent -= 1;
|
||||
}
|
||||
|
||||
NodeKind::Lambda {
|
||||
params,
|
||||
body,
|
||||
info,
|
||||
} => {
|
||||
self.log(&format!("Lambda ({:?})", info), node);
|
||||
self.indent += 1;
|
||||
|
||||
self.write_indent();
|
||||
self.output.push_str("Parameters:\n");
|
||||
self.visit(params);
|
||||
|
||||
self.visit(body);
|
||||
self.indent -= 1;
|
||||
}
|
||||
|
||||
NodeKind::Call { callee, args } => {
|
||||
self.log("Call", node);
|
||||
self.indent += 1;
|
||||
|
||||
self.write_indent();
|
||||
self.output.push_str("Callee:\n");
|
||||
self.visit(callee);
|
||||
|
||||
self.write_indent();
|
||||
self.output.push_str("Arguments:\n");
|
||||
self.visit(args);
|
||||
|
||||
self.indent -= 1;
|
||||
}
|
||||
|
||||
NodeKind::Again { args } => {
|
||||
self.log("Again", node);
|
||||
self.indent += 1;
|
||||
self.visit(args);
|
||||
self.indent -= 1;
|
||||
}
|
||||
NodeKind::Pipe { inputs, lambda } => {
|
||||
self.log("Pipe", node);
|
||||
self.indent += 1;
|
||||
for input in inputs {
|
||||
self.visit(input);
|
||||
}
|
||||
self.visit(lambda);
|
||||
self.indent -= 1;
|
||||
}
|
||||
|
||||
NodeKind::Block { exprs } => {
|
||||
self.log("Block", node);
|
||||
self.indent += 1;
|
||||
for expr in exprs {
|
||||
self.visit(expr);
|
||||
}
|
||||
self.indent -= 1;
|
||||
}
|
||||
|
||||
NodeKind::Tuple { elements } => {
|
||||
self.log("Tuple", node);
|
||||
self.indent += 1;
|
||||
for el in elements {
|
||||
self.visit(el);
|
||||
}
|
||||
self.indent -= 1;
|
||||
}
|
||||
|
||||
NodeKind::Record { fields, layout } => {
|
||||
self.log(
|
||||
&format!("Record (Layout: {:?})", layout),
|
||||
node,
|
||||
);
|
||||
self.indent += 1;
|
||||
for (key, val) in fields {
|
||||
self.write_indent();
|
||||
self.output.push_str("Key:\n");
|
||||
self.indent += 1;
|
||||
self.visit(key);
|
||||
self.indent -= 1;
|
||||
self.write_indent();
|
||||
self.output.push_str("Value:\n");
|
||||
self.indent += 1;
|
||||
self.visit(val);
|
||||
self.indent -= 1;
|
||||
}
|
||||
self.indent -= 1;
|
||||
}
|
||||
|
||||
NodeKind::Expansion {
|
||||
original_call,
|
||||
expanded,
|
||||
} => {
|
||||
self.log(
|
||||
&format!("Expansion (Original: {:?})", original_call.kind),
|
||||
node,
|
||||
);
|
||||
self.indent += 1;
|
||||
self.visit(expanded);
|
||||
self.indent -= 1;
|
||||
}
|
||||
|
||||
NodeKind::MacroDecl { name, params, body } => {
|
||||
self.log(&format!("MacroDecl: {}", name.name), node);
|
||||
self.indent += 1;
|
||||
self.visit(params);
|
||||
self.visit(body);
|
||||
self.indent -= 1;
|
||||
}
|
||||
|
||||
NodeKind::Template(inner) => {
|
||||
self.log("Template", node);
|
||||
self.indent += 1;
|
||||
self.visit(inner);
|
||||
self.indent -= 1;
|
||||
}
|
||||
|
||||
NodeKind::Placeholder(inner) => {
|
||||
self.log("Placeholder", node);
|
||||
self.indent += 1;
|
||||
self.visit(inner);
|
||||
self.indent -= 1;
|
||||
}
|
||||
|
||||
NodeKind::Splice(inner) => {
|
||||
self.log("Splice", node);
|
||||
self.indent += 1;
|
||||
self.visit(inner);
|
||||
self.indent -= 1;
|
||||
}
|
||||
|
||||
NodeKind::Extension(ext) => {
|
||||
self.log(&ext.display_name(), node);
|
||||
}
|
||||
NodeKind::Error => {
|
||||
self.log("ERROR_NODE", node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,99 +1,111 @@
|
||||
use crate::ast::compiler::bound_nodes::{Address, BoundKind, CompilerPhase, GlobalIdx, Node};
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
/// A pass that collects all global function definitions (lambdas) into a registry.
|
||||
/// This allows the Specializer to retrieve the original AST of a function for monomorphization.
|
||||
pub struct LambdaCollector<'a, P: CompilerPhase> {
|
||||
registry: &'a mut HashMap<GlobalIdx, Rc<Node<P>>>,
|
||||
}
|
||||
|
||||
impl<'a, P: CompilerPhase> LambdaCollector<'a, P> {
|
||||
/// Performs a full traversal of the AST and populates the provided registry.
|
||||
pub fn collect(node: &Node<P>, registry: &'a mut HashMap<GlobalIdx, Rc<Node<P>>>) {
|
||||
let mut collector = Self { registry };
|
||||
collector.visit(node);
|
||||
}
|
||||
|
||||
fn visit(&mut self, node: &Node<P>) {
|
||||
match &node.kind {
|
||||
BoundKind::Block { exprs } => {
|
||||
for expr in exprs {
|
||||
self.visit(expr);
|
||||
}
|
||||
}
|
||||
|
||||
BoundKind::Define { addr, value, .. } => {
|
||||
// Register global function definitions (lambdas)
|
||||
if let Address::Global(global_index) = addr {
|
||||
let mut current = value;
|
||||
while let BoundKind::Expansion { bound_expanded, .. } = ¤t.kind {
|
||||
current = bound_expanded;
|
||||
}
|
||||
|
||||
if let BoundKind::Lambda { .. } = ¤t.kind {
|
||||
self.registry
|
||||
.insert(*global_index, (*current).clone());
|
||||
}
|
||||
}
|
||||
self.visit(value);
|
||||
}
|
||||
|
||||
BoundKind::Set { addr, value } => {
|
||||
// Also track assignments to globals if they hold lambdas.
|
||||
if let Address::Global(global_index) = addr {
|
||||
let mut current = value;
|
||||
while let BoundKind::Expansion { bound_expanded, .. } = ¤t.kind {
|
||||
current = bound_expanded;
|
||||
}
|
||||
|
||||
if let BoundKind::Lambda { .. } = ¤t.kind {
|
||||
self.registry
|
||||
.insert(*global_index, (*current).clone());
|
||||
}
|
||||
}
|
||||
self.visit(value);
|
||||
}
|
||||
|
||||
BoundKind::If {
|
||||
cond,
|
||||
then_br,
|
||||
else_br,
|
||||
} => {
|
||||
self.visit(cond);
|
||||
self.visit(then_br);
|
||||
if let Some(e) = else_br {
|
||||
self.visit(e);
|
||||
}
|
||||
}
|
||||
|
||||
BoundKind::Lambda { params, body, .. } => {
|
||||
self.visit(params);
|
||||
self.visit(body);
|
||||
}
|
||||
|
||||
BoundKind::Call { callee, args } => {
|
||||
self.visit(callee);
|
||||
self.visit(args);
|
||||
}
|
||||
|
||||
BoundKind::Tuple { elements } => {
|
||||
for el in elements {
|
||||
self.visit(el);
|
||||
}
|
||||
}
|
||||
|
||||
BoundKind::Record { values, .. } => {
|
||||
for v in values {
|
||||
self.visit(v);
|
||||
}
|
||||
}
|
||||
|
||||
BoundKind::Expansion { bound_expanded, .. } => {
|
||||
self.visit(bound_expanded);
|
||||
}
|
||||
|
||||
_ => {} // Leaf nodes
|
||||
}
|
||||
}
|
||||
}
|
||||
use crate::ast::compiler::bound_nodes::{
|
||||
Address, BoundLike, GlobalIdx, IdentifierBinding, Node, NodeKind,
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
/// A pass that collects all global function definitions (lambdas) into a registry.
|
||||
/// This allows the Specializer to retrieve the original AST of a function for monomorphization.
|
||||
pub struct LambdaCollector<'a, P: BoundLike> {
|
||||
registry: &'a mut HashMap<GlobalIdx, Rc<Node<P>>>,
|
||||
}
|
||||
|
||||
impl<'a, P> LambdaCollector<'a, P>
|
||||
where
|
||||
P: BoundLike,
|
||||
{
|
||||
/// Performs a full traversal of the AST and populates the provided registry.
|
||||
pub fn collect(node: &Node<P>, registry: &'a mut HashMap<GlobalIdx, Rc<Node<P>>>) {
|
||||
let mut collector = Self { registry };
|
||||
collector.visit(node);
|
||||
}
|
||||
|
||||
fn visit(&mut self, node: &Node<P>) {
|
||||
match &node.kind {
|
||||
NodeKind::Block { exprs } => {
|
||||
for expr in exprs {
|
||||
self.visit(expr);
|
||||
}
|
||||
}
|
||||
|
||||
NodeKind::Def { pattern, value, .. } => {
|
||||
// Register global function definitions (lambdas)
|
||||
if let NodeKind::Identifier {
|
||||
binding: IdentifierBinding::Declaration {
|
||||
addr: Address::Global(global_index),
|
||||
..
|
||||
},
|
||||
..
|
||||
} = &pattern.kind
|
||||
{
|
||||
let mut current = value;
|
||||
while let NodeKind::Expansion { expanded, .. } = ¤t.kind {
|
||||
current = expanded;
|
||||
}
|
||||
|
||||
if let NodeKind::Lambda { .. } = ¤t.kind {
|
||||
self.registry
|
||||
.insert(*global_index, (*current).clone());
|
||||
}
|
||||
}
|
||||
self.visit(value);
|
||||
}
|
||||
|
||||
NodeKind::Assign { value, info, .. } => {
|
||||
// Also track assignments to globals if they hold lambdas.
|
||||
if let Some(Address::Global(global_index)) = &info.addr {
|
||||
let mut current = value;
|
||||
while let NodeKind::Expansion { expanded, .. } = ¤t.kind {
|
||||
current = expanded;
|
||||
}
|
||||
|
||||
if let NodeKind::Lambda { .. } = ¤t.kind {
|
||||
self.registry
|
||||
.insert(*global_index, (*current).clone());
|
||||
}
|
||||
}
|
||||
self.visit(value);
|
||||
}
|
||||
|
||||
NodeKind::If {
|
||||
cond,
|
||||
then_br,
|
||||
else_br,
|
||||
} => {
|
||||
self.visit(cond);
|
||||
self.visit(then_br);
|
||||
if let Some(e) = else_br {
|
||||
self.visit(e);
|
||||
}
|
||||
}
|
||||
|
||||
NodeKind::Lambda { params, body, .. } => {
|
||||
self.visit(params);
|
||||
self.visit(body);
|
||||
}
|
||||
|
||||
NodeKind::Call { callee, args } => {
|
||||
self.visit(callee);
|
||||
self.visit(args);
|
||||
}
|
||||
|
||||
NodeKind::Tuple { elements } => {
|
||||
for el in elements {
|
||||
self.visit(el);
|
||||
}
|
||||
}
|
||||
|
||||
NodeKind::Record { fields, .. } => {
|
||||
for (_, v) in fields {
|
||||
self.visit(v);
|
||||
}
|
||||
}
|
||||
|
||||
NodeKind::Expansion { expanded, .. } => {
|
||||
self.visit(expanded);
|
||||
}
|
||||
|
||||
_ => {} // Leaf nodes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+283
-231
@@ -1,231 +1,283 @@
|
||||
use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, ExecNode, Node, RuntimeMetadata, StackOffset, VirtualId};
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
struct StackAllocator {
|
||||
mapping: HashMap<u32, u32>,
|
||||
next_slot: u32,
|
||||
}
|
||||
|
||||
impl StackAllocator {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
mapping: HashMap::new(),
|
||||
next_slot: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn map_slot(&mut self, slot: VirtualId) -> StackOffset {
|
||||
let entry = self.mapping.entry(slot.0).or_insert_with(|| {
|
||||
let s = self.next_slot;
|
||||
self.next_slot += 1;
|
||||
s
|
||||
});
|
||||
StackOffset(*entry)
|
||||
}
|
||||
|
||||
fn map_address(&mut self, addr: Address<VirtualId>) -> Address<StackOffset> {
|
||||
match addr {
|
||||
Address::Local(slot) => Address::Local(self.map_slot(slot)),
|
||||
Address::Upvalue(idx) => Address::Upvalue(idx),
|
||||
Address::Global(idx) => Address::Global(idx),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Lowering;
|
||||
|
||||
impl Lowering {
|
||||
/// Lowers an AnalyzedNode to an ExecNode, marking tail positions and calculating stack sizes.
|
||||
pub fn lower(node: AnalyzedNode) -> ExecNode {
|
||||
let mut allocator = StackAllocator::new();
|
||||
let mut exec_node = Self::transform(Rc::new(node), true, &mut allocator);
|
||||
|
||||
// If the top-level node is a Lambda, it already has its internal stack_size
|
||||
// calculated during transform(). For non-lambdas (like raw expressions),
|
||||
// we use the allocator's next_slot to determine the required root stack size.
|
||||
if !matches!(exec_node.kind, BoundKind::Lambda { .. }) {
|
||||
exec_node.ty.stack_size = allocator.next_slot;
|
||||
}
|
||||
exec_node
|
||||
}
|
||||
|
||||
fn transform(
|
||||
node_rc: Rc<AnalyzedNode>,
|
||||
is_tail_position: bool,
|
||||
allocator: &mut StackAllocator,
|
||||
) -> ExecNode {
|
||||
let node = &*node_rc;
|
||||
let mut lambda_stack_size = 0;
|
||||
|
||||
let new_kind = match &node.kind {
|
||||
BoundKind::Call { callee, args } => BoundKind::Call {
|
||||
callee: Rc::new(Self::transform(callee.clone(), false, allocator)),
|
||||
args: Rc::new(Self::transform(args.clone(), false, allocator)),
|
||||
},
|
||||
|
||||
BoundKind::Again { args } => {
|
||||
if !is_tail_position {
|
||||
panic!("'again' is only allowed in tail position to avoid dead code.");
|
||||
}
|
||||
BoundKind::Again {
|
||||
args: Rc::new(Self::transform(args.clone(), false, allocator)),
|
||||
}
|
||||
}
|
||||
BoundKind::Pipe {
|
||||
inputs,
|
||||
lambda,
|
||||
out_type,
|
||||
} => {
|
||||
let mut t_inputs = Vec::with_capacity(inputs.len());
|
||||
for input in inputs {
|
||||
t_inputs.push(Rc::new(Self::transform(input.clone(), false, allocator)));
|
||||
}
|
||||
BoundKind::Pipe {
|
||||
inputs: t_inputs,
|
||||
lambda: Rc::new(Self::transform(lambda.clone(), false, allocator)),
|
||||
out_type: out_type.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
BoundKind::If {
|
||||
cond,
|
||||
then_br,
|
||||
else_br,
|
||||
} => BoundKind::If {
|
||||
cond: Rc::new(Self::transform(cond.clone(), false, allocator)),
|
||||
then_br: Rc::new(Self::transform(then_br.clone(), is_tail_position, allocator)),
|
||||
else_br: else_br
|
||||
.as_ref()
|
||||
.map(|e| Rc::new(Self::transform(e.clone(), is_tail_position, allocator))),
|
||||
},
|
||||
|
||||
BoundKind::Block { exprs } => {
|
||||
if exprs.is_empty() {
|
||||
BoundKind::Block { exprs: vec![] }
|
||||
} else {
|
||||
let last_idx = exprs.len() - 1;
|
||||
let mut new_exprs = Vec::with_capacity(exprs.len());
|
||||
|
||||
for (i, expr) in exprs.iter().enumerate() {
|
||||
let is_last = i == last_idx;
|
||||
new_exprs.push(Rc::new(Self::transform(
|
||||
expr.clone(),
|
||||
is_tail_position && is_last,
|
||||
allocator,
|
||||
)));
|
||||
}
|
||||
BoundKind::Block { exprs: new_exprs }
|
||||
}
|
||||
}
|
||||
|
||||
BoundKind::Lambda {
|
||||
params,
|
||||
upvalues,
|
||||
body,
|
||||
positional_count,
|
||||
} => {
|
||||
// Upvalues refer to the PARENT scope's addresses.
|
||||
let mapped_upvalues = upvalues
|
||||
.iter()
|
||||
.map(|a| allocator.map_address(*a))
|
||||
.collect();
|
||||
|
||||
// New allocator for the lambda's own stack frame
|
||||
let mut lambda_allocator = StackAllocator::new();
|
||||
let t_params = Rc::new(Self::transform(params.clone(), false, &mut lambda_allocator));
|
||||
let t_body = Rc::new(Self::transform(body.clone(), true, &mut lambda_allocator));
|
||||
lambda_stack_size = lambda_allocator.next_slot;
|
||||
|
||||
BoundKind::Lambda {
|
||||
params: t_params,
|
||||
upvalues: mapped_upvalues,
|
||||
body: t_body,
|
||||
positional_count: *positional_count,
|
||||
}
|
||||
}
|
||||
|
||||
BoundKind::Set { addr, value } => BoundKind::Set {
|
||||
addr: allocator.map_address(*addr),
|
||||
value: Rc::new(Self::transform(value.clone(), false, allocator)),
|
||||
},
|
||||
BoundKind::Define {
|
||||
name,
|
||||
addr,
|
||||
kind,
|
||||
value,
|
||||
captured_by,
|
||||
} => BoundKind::Define {
|
||||
name: name.clone(),
|
||||
addr: allocator.map_address(*addr),
|
||||
kind: *kind,
|
||||
value: Rc::new(Self::transform(value.clone(), false, allocator)),
|
||||
captured_by: captured_by.clone(),
|
||||
},
|
||||
BoundKind::Destructure { pattern, value } => BoundKind::Destructure {
|
||||
pattern: Rc::new(Self::transform(pattern.clone(), false, allocator)),
|
||||
value: Rc::new(Self::transform(value.clone(), false, allocator)),
|
||||
},
|
||||
BoundKind::Record { layout, values } => {
|
||||
let new_values = values
|
||||
.iter()
|
||||
.map(|v| Rc::new(Self::transform(v.clone(), false, allocator)))
|
||||
.collect();
|
||||
BoundKind::Record {
|
||||
layout: layout.clone(),
|
||||
values: new_values,
|
||||
}
|
||||
}
|
||||
BoundKind::Tuple { elements } => {
|
||||
let new_elements = elements
|
||||
.iter()
|
||||
.map(|e| Rc::new(Self::transform(e.clone(), false, allocator)))
|
||||
.collect();
|
||||
BoundKind::Tuple {
|
||||
elements: new_elements,
|
||||
}
|
||||
}
|
||||
BoundKind::Constant(v) => BoundKind::Constant(v.clone()),
|
||||
BoundKind::Get { addr, name } => BoundKind::Get {
|
||||
addr: allocator.map_address(*addr),
|
||||
name: name.clone(),
|
||||
},
|
||||
BoundKind::FieldAccessor(k) => BoundKind::FieldAccessor(*k),
|
||||
BoundKind::GetField { rec, field } => BoundKind::GetField {
|
||||
rec: Rc::new(Self::transform(rec.clone(), false, allocator)),
|
||||
field: *field,
|
||||
},
|
||||
BoundKind::Nop => BoundKind::Nop,
|
||||
BoundKind::Expansion {
|
||||
original_call,
|
||||
bound_expanded,
|
||||
} => BoundKind::Expansion {
|
||||
original_call: original_call.clone(),
|
||||
bound_expanded: Rc::new(Self::transform(
|
||||
bound_expanded.clone(),
|
||||
is_tail_position,
|
||||
allocator,
|
||||
)),
|
||||
},
|
||||
BoundKind::Extension(_) => BoundKind::Nop,
|
||||
BoundKind::Error => BoundKind::Error,
|
||||
};
|
||||
|
||||
let stack_size = if let BoundKind::Lambda { .. } = &new_kind {
|
||||
lambda_stack_size
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
Node {
|
||||
identity: node.identity.clone(),
|
||||
kind: new_kind,
|
||||
ty: RuntimeMetadata {
|
||||
ty: node.ty.original.ty.clone(),
|
||||
is_tail: is_tail_position,
|
||||
original: node_rc,
|
||||
stack_size,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
use crate::ast::compiler::bound_nodes::{
|
||||
Address, AnalyzedNode, AssignBinding, ExecNode,
|
||||
IdentifierBinding, LambdaBinding, Node, NodeKind, RuntimeMetadata,
|
||||
StackOffset, VirtualId,
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
struct StackAllocator {
|
||||
mapping: HashMap<u32, u32>,
|
||||
next_slot: u32,
|
||||
}
|
||||
|
||||
impl StackAllocator {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
mapping: HashMap::new(),
|
||||
next_slot: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn map_slot(&mut self, slot: VirtualId) -> StackOffset {
|
||||
let entry = self.mapping.entry(slot.0).or_insert_with(|| {
|
||||
let s = self.next_slot;
|
||||
self.next_slot += 1;
|
||||
s
|
||||
});
|
||||
StackOffset(*entry)
|
||||
}
|
||||
|
||||
fn map_address(&mut self, addr: Address<VirtualId>) -> Address<StackOffset> {
|
||||
match addr {
|
||||
Address::Local(slot) => Address::Local(self.map_slot(slot)),
|
||||
Address::Upvalue(idx) => Address::Upvalue(idx),
|
||||
Address::Global(idx) => Address::Global(idx),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Lowering;
|
||||
|
||||
impl Lowering {
|
||||
/// Lowers an AnalyzedNode to an ExecNode, marking tail positions and calculating stack sizes.
|
||||
pub fn lower(node: AnalyzedNode) -> ExecNode {
|
||||
let mut allocator = StackAllocator::new();
|
||||
let mut exec_node = Self::transform(Rc::new(node), true, &mut allocator);
|
||||
|
||||
// If the top-level node is a Lambda, it already has its internal stack_size
|
||||
// calculated during transform(). For non-lambdas (like raw expressions),
|
||||
// we use the allocator's next_slot to determine the required root stack size.
|
||||
if !matches!(exec_node.kind, NodeKind::Lambda { .. }) {
|
||||
exec_node.ty.stack_size = allocator.next_slot;
|
||||
}
|
||||
exec_node
|
||||
}
|
||||
|
||||
fn transform(
|
||||
node_rc: Rc<AnalyzedNode>,
|
||||
is_tail_position: bool,
|
||||
allocator: &mut StackAllocator,
|
||||
) -> ExecNode {
|
||||
let node = &*node_rc;
|
||||
let mut lambda_stack_size = 0;
|
||||
|
||||
let new_kind = match &node.kind {
|
||||
NodeKind::Call { callee, args } => {
|
||||
// Optimized Field Access: Call { callee: FieldAccessor, args: Tuple[1] } → GetField
|
||||
if let NodeKind::FieldAccessor(k) = &callee.kind
|
||||
&& let NodeKind::Tuple { elements } = &args.kind
|
||||
&& elements.len() == 1
|
||||
{
|
||||
let rec = Rc::new(Self::transform(elements[0].clone(), false, allocator));
|
||||
return Node {
|
||||
identity: node.identity.clone(),
|
||||
kind: NodeKind::GetField {
|
||||
rec,
|
||||
field: *k,
|
||||
},
|
||||
ty: RuntimeMetadata {
|
||||
ty: node.ty.original.ty.clone(),
|
||||
is_tail: is_tail_position,
|
||||
original: node_rc,
|
||||
stack_size: 0,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
NodeKind::Call {
|
||||
callee: Rc::new(Self::transform(callee.clone(), false, allocator)),
|
||||
args: Rc::new(Self::transform(args.clone(), false, allocator)),
|
||||
}
|
||||
}
|
||||
|
||||
NodeKind::Again { args } => {
|
||||
if !is_tail_position {
|
||||
panic!("'again' is only allowed in tail position to avoid dead code.");
|
||||
}
|
||||
NodeKind::Again {
|
||||
args: Rc::new(Self::transform(args.clone(), false, allocator)),
|
||||
}
|
||||
}
|
||||
NodeKind::Pipe {
|
||||
inputs,
|
||||
lambda,
|
||||
} => {
|
||||
let mut t_inputs = Vec::with_capacity(inputs.len());
|
||||
for input in inputs {
|
||||
t_inputs.push(Rc::new(Self::transform(input.clone(), false, allocator)));
|
||||
}
|
||||
NodeKind::Pipe {
|
||||
inputs: t_inputs,
|
||||
lambda: Rc::new(Self::transform(lambda.clone(), false, allocator)),
|
||||
}
|
||||
}
|
||||
|
||||
NodeKind::If {
|
||||
cond,
|
||||
then_br,
|
||||
else_br,
|
||||
} => NodeKind::If {
|
||||
cond: Rc::new(Self::transform(cond.clone(), false, allocator)),
|
||||
then_br: Rc::new(Self::transform(then_br.clone(), is_tail_position, allocator)),
|
||||
else_br: else_br
|
||||
.as_ref()
|
||||
.map(|e| Rc::new(Self::transform(e.clone(), is_tail_position, allocator))),
|
||||
},
|
||||
|
||||
NodeKind::Block { exprs } => {
|
||||
if exprs.is_empty() {
|
||||
NodeKind::Block { exprs: vec![] }
|
||||
} else {
|
||||
let last_idx = exprs.len() - 1;
|
||||
let mut new_exprs = Vec::with_capacity(exprs.len());
|
||||
|
||||
for (i, expr) in exprs.iter().enumerate() {
|
||||
let is_last = i == last_idx;
|
||||
new_exprs.push(Rc::new(Self::transform(
|
||||
expr.clone(),
|
||||
is_tail_position && is_last,
|
||||
allocator,
|
||||
)));
|
||||
}
|
||||
NodeKind::Block { exprs: new_exprs }
|
||||
}
|
||||
}
|
||||
|
||||
NodeKind::Lambda {
|
||||
params,
|
||||
body,
|
||||
info: lambda_info,
|
||||
} => {
|
||||
// Upvalues refer to the PARENT scope's addresses.
|
||||
let mapped_upvalues = lambda_info
|
||||
.upvalues
|
||||
.iter()
|
||||
.map(|a| allocator.map_address(*a))
|
||||
.collect();
|
||||
|
||||
// New allocator for the lambda's own stack frame
|
||||
let mut lambda_allocator = StackAllocator::new();
|
||||
let t_params = Rc::new(Self::transform(params.clone(), false, &mut lambda_allocator));
|
||||
let t_body = Rc::new(Self::transform(body.clone(), true, &mut lambda_allocator));
|
||||
lambda_stack_size = lambda_allocator.next_slot;
|
||||
|
||||
NodeKind::Lambda {
|
||||
params: t_params,
|
||||
body: t_body,
|
||||
info: LambdaBinding {
|
||||
upvalues: mapped_upvalues,
|
||||
positional_count: lambda_info.positional_count,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
NodeKind::Assign { target, value, info } => {
|
||||
let new_info = if let Some(addr) = info.addr {
|
||||
AssignBinding {
|
||||
addr: Some(allocator.map_address(addr)),
|
||||
}
|
||||
} else {
|
||||
AssignBinding { addr: None }
|
||||
};
|
||||
NodeKind::Assign {
|
||||
target: Rc::new(Self::transform(target.clone(), false, allocator)),
|
||||
value: Rc::new(Self::transform(value.clone(), false, allocator)),
|
||||
info: new_info,
|
||||
}
|
||||
}
|
||||
NodeKind::Def {
|
||||
pattern,
|
||||
value,
|
||||
info,
|
||||
} => NodeKind::Def {
|
||||
pattern: Rc::new(Self::transform(pattern.clone(), false, allocator)),
|
||||
value: Rc::new(Self::transform(value.clone(), false, allocator)),
|
||||
info: info.clone(),
|
||||
},
|
||||
NodeKind::Identifier { symbol, binding } => {
|
||||
let new_binding = match binding {
|
||||
IdentifierBinding::Reference(addr) => {
|
||||
IdentifierBinding::Reference(allocator.map_address(*addr))
|
||||
}
|
||||
IdentifierBinding::Declaration { addr, kind } => {
|
||||
IdentifierBinding::Declaration {
|
||||
addr: allocator.map_address(*addr),
|
||||
kind: *kind,
|
||||
}
|
||||
}
|
||||
};
|
||||
NodeKind::Identifier {
|
||||
symbol: symbol.clone(),
|
||||
binding: new_binding,
|
||||
}
|
||||
}
|
||||
NodeKind::Record { fields, layout } => {
|
||||
let new_fields = fields
|
||||
.iter()
|
||||
.map(|(k, v)| {
|
||||
(
|
||||
Rc::new(Self::transform(k.clone(), false, allocator)),
|
||||
Rc::new(Self::transform(v.clone(), false, allocator)),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
NodeKind::Record {
|
||||
fields: new_fields,
|
||||
layout: layout.clone(),
|
||||
}
|
||||
}
|
||||
NodeKind::Tuple { elements } => {
|
||||
let new_elements = elements
|
||||
.iter()
|
||||
.map(|e| Rc::new(Self::transform(e.clone(), false, allocator)))
|
||||
.collect();
|
||||
NodeKind::Tuple {
|
||||
elements: new_elements,
|
||||
}
|
||||
}
|
||||
NodeKind::Constant(v) => NodeKind::Constant(v.clone()),
|
||||
NodeKind::FieldAccessor(k) => NodeKind::FieldAccessor(*k),
|
||||
NodeKind::GetField { rec, field } => NodeKind::GetField {
|
||||
rec: Rc::new(Self::transform(rec.clone(), false, allocator)),
|
||||
field: *field,
|
||||
},
|
||||
NodeKind::Nop => NodeKind::Nop,
|
||||
NodeKind::Expansion {
|
||||
original_call,
|
||||
expanded,
|
||||
} => NodeKind::Expansion {
|
||||
original_call: original_call.clone(),
|
||||
expanded: Rc::new(Self::transform(
|
||||
expanded.clone(),
|
||||
is_tail_position,
|
||||
allocator,
|
||||
)),
|
||||
},
|
||||
NodeKind::Extension(_) => NodeKind::Nop,
|
||||
NodeKind::Error => NodeKind::Error,
|
||||
// Syntax-only variants should not appear in AnalyzedPhase
|
||||
NodeKind::MacroDecl { .. }
|
||||
| NodeKind::Template(_)
|
||||
| NodeKind::Placeholder(_)
|
||||
| NodeKind::Splice(_) => NodeKind::Nop,
|
||||
};
|
||||
|
||||
let stack_size = if let NodeKind::Lambda { .. } = &new_kind {
|
||||
lambda_stack_size
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
Node {
|
||||
identity: node.identity.clone(),
|
||||
kind: new_kind,
|
||||
ty: RuntimeMetadata {
|
||||
ty: node.ty.original.ty.clone(),
|
||||
is_tail: is_tail_position,
|
||||
original: node_rc,
|
||||
stack_size,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+855
-836
File diff suppressed because it is too large
Load Diff
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);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+276
-276
@@ -1,276 +1,276 @@
|
||||
use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, AnalyzedPhase, BoundKind, Node, NodeMetrics, VirtualId};
|
||||
use crate::ast::types::{Purity, Signature, StaticType, Value};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct MonoCacheKey {
|
||||
pub address: Address<VirtualId>,
|
||||
pub arg_types: Vec<StaticType>,
|
||||
}
|
||||
|
||||
pub type CompileFunc = Rc<dyn Fn(Rc<Node<AnalyzedPhase>>, &[StaticType]) -> Result<(Value, StaticType), String>>;
|
||||
pub type RtlLookupFunc = Rc<dyn Fn(&str, &[StaticType]) -> Option<(Value, StaticType)>>;
|
||||
|
||||
pub trait FunctionRegistry {
|
||||
fn resolve(&self, addr: Address<VirtualId>) -> Option<Rc<Node<AnalyzedPhase>>>;
|
||||
fn resolve_analyzed(&self, _addr: Address<VirtualId>) -> Option<Rc<AnalyzedNode>> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub type MonoCache = HashMap<MonoCacheKey, (Value, StaticType)>;
|
||||
|
||||
pub struct Specializer {
|
||||
pub cache: Rc<RefCell<MonoCache>>,
|
||||
registry: Option<Rc<dyn FunctionRegistry>>,
|
||||
compiler: Option<CompileFunc>,
|
||||
rtl_lookup: Option<RtlLookupFunc>,
|
||||
}
|
||||
|
||||
impl Specializer {
|
||||
pub fn new(
|
||||
registry: Option<Rc<dyn FunctionRegistry>>,
|
||||
compiler: Option<CompileFunc>,
|
||||
rtl_lookup: Option<RtlLookupFunc>,
|
||||
cache: Option<Rc<RefCell<MonoCache>>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
cache: cache.unwrap_or_else(|| Rc::new(RefCell::new(HashMap::new()))),
|
||||
registry,
|
||||
compiler,
|
||||
rtl_lookup,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn specialize(&self, node: AnalyzedNode) -> AnalyzedNode {
|
||||
self.visit_node(node)
|
||||
}
|
||||
|
||||
fn visit_node(&self, node: AnalyzedNode) -> AnalyzedNode {
|
||||
let (new_kind, metrics) = match node.kind {
|
||||
BoundKind::Call { callee, args } => {
|
||||
let (new_callee, new_args, _ret_ty) =
|
||||
self.specialize_call_logic(callee, args, node.ty.original.ty.clone());
|
||||
|
||||
let new_metrics = node.ty.clone();
|
||||
(
|
||||
BoundKind::Call {
|
||||
callee: Rc::new(new_callee),
|
||||
args: Rc::new(new_args),
|
||||
},
|
||||
new_metrics,
|
||||
)
|
||||
}
|
||||
|
||||
BoundKind::If {
|
||||
cond,
|
||||
then_br,
|
||||
else_br,
|
||||
} => {
|
||||
let cond = Rc::new(self.visit_node(cond.as_ref().clone()));
|
||||
let then_br = Rc::new(self.visit_node(then_br.as_ref().clone()));
|
||||
let else_br = else_br.map(|e| Rc::new(self.visit_node(e.as_ref().clone())));
|
||||
(
|
||||
BoundKind::If {
|
||||
cond,
|
||||
then_br,
|
||||
else_br,
|
||||
},
|
||||
node.ty.clone(),
|
||||
)
|
||||
}
|
||||
BoundKind::Block { exprs } => {
|
||||
let exprs = exprs.into_iter().map(|e| Rc::new(self.visit_node(e.as_ref().clone()))).collect();
|
||||
(BoundKind::Block { exprs }, node.ty.clone())
|
||||
}
|
||||
BoundKind::Lambda {
|
||||
params,
|
||||
upvalues,
|
||||
body,
|
||||
positional_count,
|
||||
} => {
|
||||
let params = Rc::new(self.visit_node(params.as_ref().clone()));
|
||||
let body = Rc::new(self.visit_node(body.as_ref().clone()));
|
||||
(
|
||||
BoundKind::Lambda {
|
||||
params,
|
||||
upvalues,
|
||||
body,
|
||||
positional_count,
|
||||
},
|
||||
node.ty.clone(),
|
||||
)
|
||||
}
|
||||
BoundKind::Define {
|
||||
name,
|
||||
addr,
|
||||
kind,
|
||||
value,
|
||||
captured_by,
|
||||
} => {
|
||||
let value = Rc::new(self.visit_node(value.as_ref().clone()));
|
||||
(
|
||||
BoundKind::Define {
|
||||
name: name.clone(),
|
||||
addr,
|
||||
kind,
|
||||
value,
|
||||
captured_by: captured_by.clone(),
|
||||
},
|
||||
node.ty.clone(),
|
||||
)
|
||||
}
|
||||
BoundKind::Set { addr, value } => {
|
||||
let value = Rc::new(self.visit_node(value.as_ref().clone()));
|
||||
(BoundKind::Set { addr, value }, node.ty.clone())
|
||||
}
|
||||
BoundKind::Tuple { elements } => {
|
||||
let elements = elements.into_iter().map(|e| Rc::new(self.visit_node(e.as_ref().clone()))).collect();
|
||||
(BoundKind::Tuple { elements }, node.ty.clone())
|
||||
}
|
||||
BoundKind::Record { layout, values } => {
|
||||
let values = values.into_iter().map(|v| Rc::new(self.visit_node(v.as_ref().clone()))).collect();
|
||||
(BoundKind::Record { layout, values }, node.ty.clone())
|
||||
}
|
||||
BoundKind::Expansion {
|
||||
original_call,
|
||||
bound_expanded,
|
||||
} => {
|
||||
let bound_expanded = Rc::new(self.visit_node(bound_expanded.as_ref().clone()));
|
||||
(
|
||||
BoundKind::Expansion {
|
||||
original_call,
|
||||
bound_expanded,
|
||||
},
|
||||
node.ty.clone(),
|
||||
)
|
||||
}
|
||||
k => (k, node.ty.clone()),
|
||||
};
|
||||
|
||||
Node {
|
||||
identity: node.identity,
|
||||
kind: new_kind,
|
||||
ty: metrics,
|
||||
}
|
||||
}
|
||||
|
||||
fn specialize_call_logic(
|
||||
&self,
|
||||
callee: Rc<AnalyzedNode>,
|
||||
args: Rc<AnalyzedNode>,
|
||||
original_ty: StaticType,
|
||||
) -> (AnalyzedNode, AnalyzedNode, StaticType) {
|
||||
let new_callee = self.visit_node(callee.as_ref().clone());
|
||||
let new_args = self.visit_node(args.as_ref().clone());
|
||||
|
||||
let address = if let BoundKind::Get { addr, .. } = &new_callee.kind {
|
||||
*addr
|
||||
} else {
|
||||
return (new_callee, new_args, original_ty);
|
||||
};
|
||||
|
||||
let arg_types: Vec<StaticType> =
|
||||
if let StaticType::Tuple(elements) = &new_args.ty.original.ty {
|
||||
elements.clone()
|
||||
} else {
|
||||
vec![new_args.ty.original.ty.clone()]
|
||||
};
|
||||
|
||||
if arg_types.iter().any(|t| matches!(t, StaticType::Any)) {
|
||||
return (new_callee, new_args, original_ty);
|
||||
}
|
||||
|
||||
let key = MonoCacheKey {
|
||||
address,
|
||||
arg_types: arg_types.clone(),
|
||||
};
|
||||
|
||||
if let Some((val, ret_ty)) = self.cache.borrow().get(&key) {
|
||||
let specialized_callee = self.make_constant_node(
|
||||
val.clone(),
|
||||
StaticType::Function(Box::new(Signature {
|
||||
params: StaticType::Tuple(arg_types),
|
||||
ret: ret_ty.clone(),
|
||||
})),
|
||||
&new_callee,
|
||||
);
|
||||
return (specialized_callee, new_args, ret_ty.clone());
|
||||
}
|
||||
|
||||
if let Some(rtl_lookup) = &self.rtl_lookup
|
||||
&& let BoundKind::Get { name, .. } = &new_callee.kind
|
||||
&& let Some((val, ret_ty)) = rtl_lookup(&name.name, &arg_types)
|
||||
{
|
||||
self.cache
|
||||
.borrow_mut()
|
||||
.insert(key.clone(), (val.clone(), ret_ty.clone()));
|
||||
let specialized_callee = self.make_constant_node(
|
||||
val.clone(),
|
||||
StaticType::Function(Box::new(Signature {
|
||||
params: StaticType::Tuple(arg_types),
|
||||
ret: ret_ty.clone(),
|
||||
})),
|
||||
&new_callee,
|
||||
);
|
||||
return (specialized_callee, new_args, ret_ty);
|
||||
}
|
||||
|
||||
if let Some(registry) = &self.registry
|
||||
&& let Some(func_node) = registry.resolve_analyzed(address)
|
||||
&& func_node.ty.is_recursive
|
||||
{
|
||||
return (new_callee, new_args, original_ty);
|
||||
}
|
||||
|
||||
if let Some(compiler) = &self.compiler
|
||||
&& let Some(func_node) = self.registry.as_ref().and_then(|r| r.resolve(address))
|
||||
&& let Ok((compiled_val, ret_ty)) = compiler(func_node, &arg_types)
|
||||
{
|
||||
self.cache
|
||||
.borrow_mut()
|
||||
.insert(key, (compiled_val.clone(), ret_ty.clone()));
|
||||
|
||||
// Only replace the callee if the compiled value is actually a function/object.
|
||||
// If it's a scalar (like 30 from folding), we DON'T fold here.
|
||||
// We keep the Call but update the callee to the specialized version if it's an object.
|
||||
if let Value::Object(_) | Value::Function(_) = &compiled_val {
|
||||
let specialized_callee = self.make_constant_node(
|
||||
compiled_val,
|
||||
StaticType::Function(Box::new(Signature {
|
||||
params: StaticType::Tuple(arg_types),
|
||||
ret: ret_ty.clone(),
|
||||
})),
|
||||
&new_callee,
|
||||
);
|
||||
return (specialized_callee, new_args, ret_ty);
|
||||
}
|
||||
}
|
||||
|
||||
(new_callee, new_args, original_ty)
|
||||
}
|
||||
|
||||
fn make_constant_node(
|
||||
&self,
|
||||
val: Value,
|
||||
ty: StaticType,
|
||||
template: &AnalyzedNode,
|
||||
) -> AnalyzedNode {
|
||||
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,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
use crate::ast::compiler::bound_nodes::{
|
||||
Address, AnalyzedNode, AnalyzedPhase, IdentifierBinding, Node, NodeKind, NodeMetrics, VirtualId,
|
||||
};
|
||||
use crate::ast::types::{Purity, Signature, StaticType, Value};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct MonoCacheKey {
|
||||
pub address: Address<VirtualId>,
|
||||
pub arg_types: Vec<StaticType>,
|
||||
}
|
||||
|
||||
pub type CompileFunc = Rc<dyn Fn(Rc<Node<AnalyzedPhase>>, &[StaticType]) -> Result<(Value, StaticType), String>>;
|
||||
pub type RtlLookupFunc = Rc<dyn Fn(&str, &[StaticType]) -> Option<(Value, StaticType)>>;
|
||||
|
||||
pub trait FunctionRegistry {
|
||||
fn resolve(&self, addr: Address<VirtualId>) -> Option<Rc<Node<AnalyzedPhase>>>;
|
||||
fn resolve_analyzed(&self, _addr: Address<VirtualId>) -> Option<Rc<AnalyzedNode>> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub type MonoCache = HashMap<MonoCacheKey, (Value, StaticType)>;
|
||||
|
||||
pub struct Specializer {
|
||||
pub cache: Rc<RefCell<MonoCache>>,
|
||||
registry: Option<Rc<dyn FunctionRegistry>>,
|
||||
compiler: Option<CompileFunc>,
|
||||
rtl_lookup: Option<RtlLookupFunc>,
|
||||
}
|
||||
|
||||
impl Specializer {
|
||||
pub fn new(
|
||||
registry: Option<Rc<dyn FunctionRegistry>>,
|
||||
compiler: Option<CompileFunc>,
|
||||
rtl_lookup: Option<RtlLookupFunc>,
|
||||
cache: Option<Rc<RefCell<MonoCache>>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
cache: cache.unwrap_or_else(|| Rc::new(RefCell::new(HashMap::new()))),
|
||||
registry,
|
||||
compiler,
|
||||
rtl_lookup,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn specialize(&self, node: AnalyzedNode) -> AnalyzedNode {
|
||||
self.visit_node(node)
|
||||
}
|
||||
|
||||
fn visit_node(&self, node: AnalyzedNode) -> AnalyzedNode {
|
||||
let (new_kind, metrics) = match node.kind {
|
||||
NodeKind::Call { callee, args } => {
|
||||
let (new_callee, new_args, _ret_ty) =
|
||||
self.specialize_call_logic(callee, args, node.ty.original.ty.clone());
|
||||
|
||||
let new_metrics = node.ty.clone();
|
||||
(
|
||||
NodeKind::Call {
|
||||
callee: Rc::new(new_callee),
|
||||
args: Rc::new(new_args),
|
||||
},
|
||||
new_metrics,
|
||||
)
|
||||
}
|
||||
|
||||
NodeKind::If {
|
||||
cond,
|
||||
then_br,
|
||||
else_br,
|
||||
} => {
|
||||
let cond = Rc::new(self.visit_node(cond.as_ref().clone()));
|
||||
let then_br = Rc::new(self.visit_node(then_br.as_ref().clone()));
|
||||
let else_br = else_br.map(|e| Rc::new(self.visit_node(e.as_ref().clone())));
|
||||
(
|
||||
NodeKind::If {
|
||||
cond,
|
||||
then_br,
|
||||
else_br,
|
||||
},
|
||||
node.ty.clone(),
|
||||
)
|
||||
}
|
||||
NodeKind::Block { exprs } => {
|
||||
let exprs = exprs.into_iter().map(|e| Rc::new(self.visit_node(e.as_ref().clone()))).collect();
|
||||
(NodeKind::Block { exprs }, node.ty.clone())
|
||||
}
|
||||
NodeKind::Lambda {
|
||||
params,
|
||||
body,
|
||||
info,
|
||||
} => {
|
||||
let params = Rc::new(self.visit_node(params.as_ref().clone()));
|
||||
let body = Rc::new(self.visit_node(body.as_ref().clone()));
|
||||
(
|
||||
NodeKind::Lambda {
|
||||
params,
|
||||
body,
|
||||
info,
|
||||
},
|
||||
node.ty.clone(),
|
||||
)
|
||||
}
|
||||
NodeKind::Def {
|
||||
pattern,
|
||||
value,
|
||||
info,
|
||||
} => {
|
||||
let value = Rc::new(self.visit_node(value.as_ref().clone()));
|
||||
(
|
||||
NodeKind::Def {
|
||||
pattern,
|
||||
value,
|
||||
info,
|
||||
},
|
||||
node.ty.clone(),
|
||||
)
|
||||
}
|
||||
NodeKind::Assign { target, value, info } => {
|
||||
let value = Rc::new(self.visit_node(value.as_ref().clone()));
|
||||
(NodeKind::Assign { target, value, info }, node.ty.clone())
|
||||
}
|
||||
NodeKind::Tuple { elements } => {
|
||||
let elements = elements.into_iter().map(|e| Rc::new(self.visit_node(e.as_ref().clone()))).collect();
|
||||
(NodeKind::Tuple { elements }, node.ty.clone())
|
||||
}
|
||||
NodeKind::Record { fields, layout } => {
|
||||
let fields = fields.into_iter().map(|(k, v)| (k, Rc::new(self.visit_node(v.as_ref().clone())))).collect();
|
||||
(NodeKind::Record { fields, layout }, node.ty.clone())
|
||||
}
|
||||
NodeKind::Expansion {
|
||||
original_call,
|
||||
expanded,
|
||||
} => {
|
||||
let expanded = Rc::new(self.visit_node(expanded.as_ref().clone()));
|
||||
(
|
||||
NodeKind::Expansion {
|
||||
original_call,
|
||||
expanded,
|
||||
},
|
||||
node.ty.clone(),
|
||||
)
|
||||
}
|
||||
k => (k, node.ty.clone()),
|
||||
};
|
||||
|
||||
Node {
|
||||
identity: node.identity,
|
||||
kind: new_kind,
|
||||
ty: metrics,
|
||||
}
|
||||
}
|
||||
|
||||
fn specialize_call_logic(
|
||||
&self,
|
||||
callee: Rc<AnalyzedNode>,
|
||||
args: Rc<AnalyzedNode>,
|
||||
original_ty: StaticType,
|
||||
) -> (AnalyzedNode, AnalyzedNode, StaticType) {
|
||||
let new_callee = self.visit_node(callee.as_ref().clone());
|
||||
let new_args = self.visit_node(args.as_ref().clone());
|
||||
|
||||
let address = if let NodeKind::Identifier {
|
||||
binding: IdentifierBinding::Reference(addr),
|
||||
..
|
||||
} = &new_callee.kind
|
||||
{
|
||||
*addr
|
||||
} else {
|
||||
return (new_callee, new_args, original_ty);
|
||||
};
|
||||
|
||||
let arg_types: Vec<StaticType> =
|
||||
if let StaticType::Tuple(elements) = &new_args.ty.original.ty {
|
||||
elements.clone()
|
||||
} else {
|
||||
vec![new_args.ty.original.ty.clone()]
|
||||
};
|
||||
|
||||
if arg_types.iter().any(|t| matches!(t, StaticType::Any)) {
|
||||
return (new_callee, new_args, original_ty);
|
||||
}
|
||||
|
||||
let key = MonoCacheKey {
|
||||
address,
|
||||
arg_types: arg_types.clone(),
|
||||
};
|
||||
|
||||
if let Some((val, ret_ty)) = self.cache.borrow().get(&key) {
|
||||
let specialized_callee = self.make_constant_node(
|
||||
val.clone(),
|
||||
StaticType::Function(Box::new(Signature {
|
||||
params: StaticType::Tuple(arg_types),
|
||||
ret: ret_ty.clone(),
|
||||
})),
|
||||
&new_callee,
|
||||
);
|
||||
return (specialized_callee, new_args, ret_ty.clone());
|
||||
}
|
||||
|
||||
if let Some(rtl_lookup) = &self.rtl_lookup
|
||||
&& let NodeKind::Identifier { symbol, .. } = &new_callee.kind
|
||||
&& let Some((val, ret_ty)) = rtl_lookup(&symbol.name, &arg_types)
|
||||
{
|
||||
self.cache
|
||||
.borrow_mut()
|
||||
.insert(key.clone(), (val.clone(), ret_ty.clone()));
|
||||
let specialized_callee = self.make_constant_node(
|
||||
val.clone(),
|
||||
StaticType::Function(Box::new(Signature {
|
||||
params: StaticType::Tuple(arg_types),
|
||||
ret: ret_ty.clone(),
|
||||
})),
|
||||
&new_callee,
|
||||
);
|
||||
return (specialized_callee, new_args, ret_ty);
|
||||
}
|
||||
|
||||
if let Some(registry) = &self.registry
|
||||
&& let Some(func_node) = registry.resolve_analyzed(address)
|
||||
&& func_node.ty.is_recursive
|
||||
{
|
||||
return (new_callee, new_args, original_ty);
|
||||
}
|
||||
|
||||
if let Some(compiler) = &self.compiler
|
||||
&& let Some(func_node) = self.registry.as_ref().and_then(|r| r.resolve(address))
|
||||
&& let Ok((compiled_val, ret_ty)) = compiler(func_node, &arg_types)
|
||||
{
|
||||
self.cache
|
||||
.borrow_mut()
|
||||
.insert(key, (compiled_val.clone(), ret_ty.clone()));
|
||||
|
||||
// Only replace the callee if the compiled value is actually a function/object.
|
||||
// If it's a scalar (like 30 from folding), we DON'T fold here.
|
||||
// We keep the Call but update the callee to the specialized version if it's an object.
|
||||
if let Value::Object(_) | Value::Function(_) = &compiled_val {
|
||||
let specialized_callee = self.make_constant_node(
|
||||
compiled_val,
|
||||
StaticType::Function(Box::new(Signature {
|
||||
params: StaticType::Tuple(arg_types),
|
||||
ret: ret_ty.clone(),
|
||||
})),
|
||||
&new_callee,
|
||||
);
|
||||
return (specialized_callee, new_args, ret_ty);
|
||||
}
|
||||
}
|
||||
|
||||
(new_callee, new_args, original_ty)
|
||||
}
|
||||
|
||||
fn make_constant_node(
|
||||
&self,
|
||||
val: Value,
|
||||
ty: StaticType,
|
||||
template: &AnalyzedNode,
|
||||
) -> AnalyzedNode {
|
||||
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,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1068
-918
File diff suppressed because it is too large
Load Diff
+19
-18
@@ -10,8 +10,8 @@ use std::path::{Path, PathBuf};
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::ast::compiler::bound_nodes::{
|
||||
Address, AnalyzedNode, BoundKind, ExecNode, GlobalAnalyzedRegistry, GlobalFunctionRegistry, GlobalIdx,
|
||||
Node, VirtualId,
|
||||
Address, AnalyzedNode, ExecNode, GlobalAnalyzedRegistry, GlobalFunctionRegistry, GlobalIdx,
|
||||
LambdaBinding, Node, NodeKind, VirtualId,
|
||||
};
|
||||
use crate::ast::compiler::dumper::Dumper;
|
||||
use crate::ast::compiler::lambda_collector::LambdaCollector;
|
||||
@@ -117,7 +117,7 @@ impl MacroEvaluator for RuntimeMacroEvaluator {
|
||||
node: &SyntaxNode,
|
||||
bindings: &HashMap<Rc<str>, SyntaxNode>,
|
||||
) -> Result<Value, String> {
|
||||
if let SyntaxKind::Identifier(sym) = &node.kind
|
||||
if let SyntaxKind::Identifier { symbol: sym, .. } = &node.kind
|
||||
&& let Some(arg_node) = bindings.get(&sym.name)
|
||||
{
|
||||
return Ok(Value::Object(Rc::new(arg_node.clone()) as Rc<dyn Object>));
|
||||
@@ -390,8 +390,8 @@ impl Environment {
|
||||
|
||||
fn discover_globals(&self, node: &SyntaxNode) {
|
||||
match &node.kind {
|
||||
SyntaxKind::Def { target, .. } => {
|
||||
if let SyntaxKind::Identifier(sym) = &target.kind {
|
||||
SyntaxKind::Def { pattern, .. } => {
|
||||
if let SyntaxKind::Identifier { symbol: sym, .. } = &pattern.kind {
|
||||
let mut root_scopes = self.root_scopes.borrow_mut();
|
||||
let last_idx = root_scopes.len() - 1;
|
||||
let current_scope = &mut root_scopes[last_idx];
|
||||
@@ -417,9 +417,9 @@ impl Environment {
|
||||
|
||||
fn extract_names(node: &SyntaxNode) -> Vec<Rc<str>> {
|
||||
match &node.kind {
|
||||
SyntaxKind::Identifier(sym) => vec![sym.name.clone()],
|
||||
SyntaxKind::Identifier { symbol: sym, .. } => vec![sym.name.clone()],
|
||||
SyntaxKind::Tuple { elements } => {
|
||||
elements.iter().flat_map(extract_names).collect()
|
||||
elements.iter().flat_map(|e| extract_names(e)).collect()
|
||||
}
|
||||
_ => vec![],
|
||||
}
|
||||
@@ -476,20 +476,22 @@ impl Environment {
|
||||
LambdaCollector::collect(&bound_ast, &mut self.function_registry.borrow_mut());
|
||||
|
||||
let checker = TypeChecker::new(self.root_types.clone());
|
||||
let wrapped_ast = if let BoundKind::Lambda { .. } = bound_ast.kind {
|
||||
let wrapped_ast = if let NodeKind::Lambda { .. } = bound_ast.kind {
|
||||
bound_ast
|
||||
} else {
|
||||
Node {
|
||||
identity: bound_ast.identity.clone(),
|
||||
kind: BoundKind::Lambda {
|
||||
kind: NodeKind::Lambda {
|
||||
params: std::rc::Rc::new(Node {
|
||||
identity: bound_ast.identity.clone(),
|
||||
kind: BoundKind::Tuple { elements: vec![] },
|
||||
kind: NodeKind::Tuple { elements: vec![] },
|
||||
ty: (),
|
||||
}),
|
||||
upvalues: vec![],
|
||||
body: std::rc::Rc::new(bound_ast),
|
||||
positional_count: Some(0),
|
||||
info: LambdaBinding {
|
||||
upvalues: vec![],
|
||||
positional_count: Some(0),
|
||||
},
|
||||
},
|
||||
ty: (),
|
||||
}
|
||||
@@ -650,20 +652,19 @@ impl Environment {
|
||||
|
||||
pub fn instantiate(&self, node: ExecNode) -> Rc<crate::ast::types::NativeFunction> {
|
||||
let root_values = self.root_values.clone();
|
||||
if let BoundKind::Lambda {
|
||||
if let NodeKind::Lambda {
|
||||
params,
|
||||
upvalues,
|
||||
body,
|
||||
positional_count,
|
||||
info,
|
||||
} = &node.kind
|
||||
&& upvalues.is_empty()
|
||||
&& info.upvalues.is_empty()
|
||||
{
|
||||
let closure = Rc::new(crate::ast::vm::Closure::new(
|
||||
params.clone(),
|
||||
body.ty.original.clone(),
|
||||
body.ty.original.clone(),
|
||||
body.clone(),
|
||||
Vec::new(),
|
||||
*positional_count,
|
||||
info.positional_count,
|
||||
node.ty.stack_size,
|
||||
));
|
||||
let closure_obj: Rc<dyn crate::ast::types::Object> = closure;
|
||||
|
||||
+65
-134
@@ -1,134 +1,65 @@
|
||||
use crate::ast::types::{Identity, Object, Value};
|
||||
use std::any::Any;
|
||||
use std::fmt::Debug;
|
||||
use std::rc::Rc;
|
||||
|
||||
/// A name with an optional context for macro hygiene.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Symbol {
|
||||
pub name: Rc<str>,
|
||||
/// Points to the identity of the Expansion node if this symbol
|
||||
/// was created/referenced inside a macro expansion.
|
||||
pub context: Option<Identity>,
|
||||
}
|
||||
|
||||
impl From<Rc<str>> for Symbol {
|
||||
fn from(name: Rc<str>) -> Self {
|
||||
Self {
|
||||
name,
|
||||
context: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Symbol {
|
||||
fn from(name: &str) -> Self {
|
||||
Self {
|
||||
name: Rc::from(name),
|
||||
context: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A parser AST Node wrapper to preserve identity and metadata
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct SyntaxNode {
|
||||
pub identity: Identity,
|
||||
pub kind: SyntaxKind,
|
||||
}
|
||||
|
||||
impl Object for SyntaxNode {
|
||||
fn type_name(&self) -> &'static str {
|
||||
"ast-node"
|
||||
}
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// The base for custom node types (extensions)
|
||||
pub trait CustomNode: Debug {
|
||||
fn display_name(&self) -> &'static str;
|
||||
fn clone_box(&self) -> Box<dyn CustomNode>;
|
||||
}
|
||||
|
||||
impl Clone for Box<dyn CustomNode> {
|
||||
fn clone(&self) -> Self {
|
||||
self.clone_box()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum SyntaxKind {
|
||||
Nop,
|
||||
Constant(Value),
|
||||
/// A general identifier (used for both references and declarations in the syntax AST).
|
||||
Identifier(Symbol),
|
||||
/// A first-class field accessor (e.g. .name)
|
||||
FieldAccessor(crate::ast::types::Keyword),
|
||||
If {
|
||||
cond: Box<SyntaxNode>,
|
||||
then_br: Box<SyntaxNode>,
|
||||
else_br: Option<Box<SyntaxNode>>,
|
||||
},
|
||||
Def {
|
||||
target: Box<SyntaxNode>,
|
||||
value: Box<SyntaxNode>,
|
||||
},
|
||||
Assign {
|
||||
target: Box<SyntaxNode>,
|
||||
value: Box<SyntaxNode>,
|
||||
},
|
||||
Lambda {
|
||||
params: Box<SyntaxNode>,
|
||||
body: Rc<SyntaxNode>,
|
||||
},
|
||||
Call {
|
||||
callee: Box<SyntaxNode>,
|
||||
args: Box<SyntaxNode>,
|
||||
},
|
||||
Again {
|
||||
args: Box<SyntaxNode>,
|
||||
},
|
||||
Pipe {
|
||||
inputs: Vec<SyntaxNode>,
|
||||
lambda: Box<SyntaxNode>,
|
||||
},
|
||||
Block {
|
||||
exprs: Vec<SyntaxNode>,
|
||||
},
|
||||
Tuple {
|
||||
elements: Vec<SyntaxNode>,
|
||||
},
|
||||
Record {
|
||||
fields: Vec<(SyntaxNode, SyntaxNode)>,
|
||||
},
|
||||
/// A macro declaration that can be expanded at compile time.
|
||||
MacroDecl {
|
||||
name: Symbol,
|
||||
params: Box<SyntaxNode>,
|
||||
body: Box<SyntaxNode>,
|
||||
},
|
||||
/// A template for AST nodes, allowing for substitutions. (Quasiquote)
|
||||
Template(Box<SyntaxNode>),
|
||||
/// A placeholder inside a template to be replaced by a node or value. (Unquote)
|
||||
Placeholder(Box<SyntaxNode>),
|
||||
/// A placeholder that flattens a sequence or merges a record into its surroundings. (Splice)
|
||||
Splice(Box<SyntaxNode>),
|
||||
/// Represents an expanded macro call, preserving the original call for debugging.
|
||||
Expansion {
|
||||
/// The original call from the source AST.
|
||||
call: Box<SyntaxNode>,
|
||||
/// The resulting AST after macro expansion.
|
||||
expanded: Box<SyntaxNode>,
|
||||
},
|
||||
/// A diagnostic poison node, allowing compilation to continue after an error.
|
||||
Error,
|
||||
Extension(Box<dyn CustomNode>),
|
||||
}
|
||||
|
||||
impl PartialEq for Box<dyn CustomNode> {
|
||||
fn eq(&self, _other: &Self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
use crate::ast::compiler::bound_nodes::{Node, NodeKind, SyntaxPhase};
|
||||
use crate::ast::types::{Identity, Object};
|
||||
use std::any::Any;
|
||||
use std::fmt::Debug;
|
||||
use std::rc::Rc;
|
||||
|
||||
/// A name with an optional context for macro hygiene.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Symbol {
|
||||
pub name: Rc<str>,
|
||||
/// Points to the identity of the Expansion node if this symbol
|
||||
/// was created/referenced inside a macro expansion.
|
||||
pub context: Option<Identity>,
|
||||
}
|
||||
|
||||
impl From<Rc<str>> for Symbol {
|
||||
fn from(name: Rc<str>) -> Self {
|
||||
Self {
|
||||
name,
|
||||
context: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Symbol {
|
||||
fn from(name: &str) -> Self {
|
||||
Self {
|
||||
name: Rc::from(name),
|
||||
context: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Type alias: the parser AST is now `Node<SyntaxPhase>`.
|
||||
pub type SyntaxNode = Node<SyntaxPhase>;
|
||||
|
||||
/// Type alias: the parser AST kind is now `NodeKind<SyntaxPhase>`.
|
||||
pub type SyntaxKind = NodeKind<SyntaxPhase>;
|
||||
|
||||
impl Object for Node<SyntaxPhase> {
|
||||
fn type_name(&self) -> &'static str {
|
||||
"ast-node"
|
||||
}
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// The base for custom node types (extensions)
|
||||
pub trait CustomNode: Debug {
|
||||
fn display_name(&self) -> &'static str;
|
||||
fn clone_box(&self) -> Box<dyn CustomNode>;
|
||||
}
|
||||
|
||||
impl Clone for Box<dyn CustomNode> {
|
||||
fn clone(&self) -> Self {
|
||||
self.clone_box()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Box<dyn CustomNode> {
|
||||
fn eq(&self, _other: &Self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
+544
-522
File diff suppressed because it is too large
Load Diff
+101
-88
@@ -1,4 +1,4 @@
|
||||
use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, ExecNode, StackOffset};
|
||||
use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, ExecNode, IdentifierBinding, NodeKind, StackOffset};
|
||||
use crate::ast::types::{Object, Value};
|
||||
use std::any::Any;
|
||||
use std::cell::RefCell;
|
||||
@@ -105,7 +105,7 @@ impl VMObserver for TracingObserver {
|
||||
self.indent = self.indent.saturating_sub(1);
|
||||
let pad = self.pad();
|
||||
match &node.kind {
|
||||
BoundKind::Define { .. } | BoundKind::Set { .. } => {
|
||||
NodeKind::Def { .. } | NodeKind::Assign { .. } => {
|
||||
let s_pad = format!("{}| ", pad);
|
||||
self.logs.push(format!("{}--- Scope Status ---", s_pad));
|
||||
self.logs.push(format!(
|
||||
@@ -303,58 +303,76 @@ impl VM {
|
||||
#[inline(always)]
|
||||
fn eval_core<O: VMObserver>(&mut self, obs: &mut O, node: &ExecNode) -> Result<Value, String> {
|
||||
match &node.kind {
|
||||
BoundKind::Nop => Ok(Value::Void),
|
||||
BoundKind::Constant(v) => Ok(v.clone()),
|
||||
BoundKind::Define {
|
||||
addr,
|
||||
value,
|
||||
captured_by,
|
||||
..
|
||||
} => {
|
||||
NodeKind::Nop => Ok(Value::Void),
|
||||
NodeKind::Constant(v) => Ok(v.clone()),
|
||||
NodeKind::Def { pattern, value, info } => {
|
||||
let val = self.eval_internal(obs, value)?;
|
||||
|
||||
let mut needs_cell_wrap = false;
|
||||
if !captured_by.is_empty()
|
||||
&& let Address::Local(slot) = addr
|
||||
{
|
||||
let frame = self.frames.last().unwrap();
|
||||
let abs_index = frame.stack_base + (slot.0 as usize);
|
||||
|
||||
// Robustness Fix: If the slot is already a Cell (due to forward capture
|
||||
// in a recursive scenario or complex pre-allocation), don't wrap it again.
|
||||
// This prevents Cell(Cell(Value)) nesting which causes type errors.
|
||||
if abs_index >= self.stack.len() || !matches!(self.stack[abs_index], Value::Cell(_)) {
|
||||
needs_cell_wrap = true;
|
||||
match &pattern.kind {
|
||||
NodeKind::Identifier { binding, .. } => {
|
||||
let addr = match binding {
|
||||
IdentifierBinding::Declaration { addr, .. } | IdentifierBinding::Reference(addr) => *addr,
|
||||
};
|
||||
|
||||
let mut needs_cell_wrap = false;
|
||||
if !info.captured_by.is_empty()
|
||||
&& let Address::Local(slot) = addr
|
||||
{
|
||||
let frame = self.frames.last().unwrap();
|
||||
let abs_index = frame.stack_base + (slot.0 as usize);
|
||||
|
||||
// Robustness Fix: If the slot is already a Cell (due to forward capture
|
||||
// in a recursive scenario or complex pre-allocation), don't wrap it again.
|
||||
// This prevents Cell(Cell(Value)) nesting which causes type errors.
|
||||
if abs_index >= self.stack.len() || !matches!(self.stack[abs_index], Value::Cell(_)) {
|
||||
needs_cell_wrap = true;
|
||||
}
|
||||
}
|
||||
|
||||
let store_val = if needs_cell_wrap {
|
||||
Value::Cell(Rc::new(RefCell::new(val.clone())))
|
||||
} else {
|
||||
val.clone()
|
||||
};
|
||||
|
||||
self.set_value(addr, store_val)?;
|
||||
}
|
||||
_ => {
|
||||
// Destructuring (was Destructure variant)
|
||||
let mut offset = 0;
|
||||
if let Some(vals) = val.as_slice() {
|
||||
self.unpack(pattern.as_ref(), vals, &mut offset)?;
|
||||
} else {
|
||||
self.unpack(pattern.as_ref(), std::slice::from_ref(&val), &mut offset)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let store_val = if needs_cell_wrap {
|
||||
Value::Cell(Rc::new(RefCell::new(val.clone())))
|
||||
} else {
|
||||
val.clone()
|
||||
};
|
||||
|
||||
self.set_value(*addr, store_val)?;
|
||||
// Define always evaluates to the unwrapped value for immediate use.
|
||||
// Def always evaluates to the unwrapped value for immediate use.
|
||||
Ok(val)
|
||||
}
|
||||
BoundKind::Destructure { pattern, value } => {
|
||||
NodeKind::Assign { target, value, info } => {
|
||||
let val = self.eval_internal(obs, value)?;
|
||||
let mut offset = 0;
|
||||
|
||||
// Destructuring works on tuples/vectors, or single values wrapped in a slice
|
||||
if let Some(vals) = val.as_slice() {
|
||||
self.unpack(pattern.as_ref(), vals, &mut offset)?;
|
||||
if let Some(addr) = info.addr {
|
||||
self.set_value(addr, val.clone())?;
|
||||
} else {
|
||||
self.unpack(pattern.as_ref(), std::slice::from_ref(&val), &mut offset)?;
|
||||
// Destructuring assign
|
||||
let mut offset = 0;
|
||||
if let Some(vals) = val.as_slice() {
|
||||
self.unpack(target.as_ref(), vals, &mut offset)?;
|
||||
} else {
|
||||
self.unpack(target.as_ref(), std::slice::from_ref(&val), &mut offset)?;
|
||||
}
|
||||
}
|
||||
Ok(val)
|
||||
}
|
||||
BoundKind::Get { addr, .. } => self.get_value(*addr),
|
||||
NodeKind::Identifier { binding, .. } => {
|
||||
match binding {
|
||||
IdentifierBinding::Reference(addr) | IdentifierBinding::Declaration { addr, .. } => self.get_value(*addr),
|
||||
}
|
||||
}
|
||||
|
||||
BoundKind::FieldAccessor(k) => Ok(Value::FieldAccessor(*k)),
|
||||
NodeKind::FieldAccessor(k) => Ok(Value::FieldAccessor(*k)),
|
||||
|
||||
BoundKind::GetField { rec, field } => {
|
||||
NodeKind::GetField { rec, field } => {
|
||||
let rec_val = self.eval_internal(obs, rec)?;
|
||||
|
||||
match rec_val {
|
||||
@@ -395,12 +413,7 @@ impl VM {
|
||||
}
|
||||
}
|
||||
|
||||
BoundKind::Set { addr, value } => {
|
||||
let val = self.eval_internal(obs, value)?;
|
||||
self.set_value(*addr, val.clone())?;
|
||||
Ok(val)
|
||||
}
|
||||
BoundKind::If {
|
||||
NodeKind::If {
|
||||
cond,
|
||||
then_br,
|
||||
else_br,
|
||||
@@ -414,10 +427,9 @@ impl VM {
|
||||
Ok(Value::Void)
|
||||
}
|
||||
}
|
||||
BoundKind::Pipe {
|
||||
NodeKind::Pipe {
|
||||
inputs,
|
||||
lambda,
|
||||
out_type,
|
||||
} => {
|
||||
use crate::ast::rtl::streams::StreamNode;
|
||||
|
||||
@@ -464,25 +476,24 @@ impl VM {
|
||||
|
||||
// Delegate to the RTL Factory for specialized buffer instantiation
|
||||
let node =
|
||||
crate::ast::rtl::streams::build_pipeline_node(obs_streams, executor, out_type);
|
||||
crate::ast::rtl::streams::build_pipeline_node(obs_streams, executor, &node.ty.ty);
|
||||
|
||||
Ok(Value::Object(node))
|
||||
}
|
||||
BoundKind::Block { exprs } => {
|
||||
NodeKind::Block { exprs } => {
|
||||
let mut last = Value::Void;
|
||||
for e in exprs {
|
||||
last = self.eval_internal(obs, e)?;
|
||||
}
|
||||
Ok(last)
|
||||
}
|
||||
BoundKind::Lambda {
|
||||
NodeKind::Lambda {
|
||||
params,
|
||||
upvalues,
|
||||
body,
|
||||
positional_count,
|
||||
info,
|
||||
} => {
|
||||
let mut captured = Vec::with_capacity(upvalues.len());
|
||||
for addr in upvalues {
|
||||
let mut captured = Vec::with_capacity(info.upvalues.len());
|
||||
for addr in &info.upvalues {
|
||||
captured.push(self.capture_upvalue(*addr)?);
|
||||
}
|
||||
let stack_size = node.ty.stack_size;
|
||||
@@ -491,12 +502,12 @@ impl VM {
|
||||
body.ty.original.clone(),
|
||||
body.clone(),
|
||||
captured,
|
||||
*positional_count,
|
||||
info.positional_count,
|
||||
stack_size,
|
||||
);
|
||||
Ok(Value::Object(Rc::new(closure)))
|
||||
}
|
||||
BoundKind::Call { callee, args } => {
|
||||
NodeKind::Call { callee, args } => {
|
||||
let func_val = self.eval_internal(obs, callee)?;
|
||||
|
||||
let base = self.stack.len();
|
||||
@@ -654,7 +665,7 @@ impl VM {
|
||||
} else {
|
||||
let args_for_unpack = self.stack[base..].to_vec();
|
||||
self.stack.truncate(base);
|
||||
if let BoundKind::Tuple { elements } =
|
||||
if let NodeKind::Tuple { elements } =
|
||||
&closure.parameter_node.kind
|
||||
{
|
||||
let mut offset = 0;
|
||||
@@ -755,7 +766,7 @@ impl VM {
|
||||
}
|
||||
}
|
||||
}
|
||||
BoundKind::Again { args } => {
|
||||
NodeKind::Again { args } => {
|
||||
let base = self.stack.len();
|
||||
if let Err(e) = self.eval_args_to_stack(obs, args) {
|
||||
self.stack.truncate(base);
|
||||
@@ -774,16 +785,16 @@ impl VM {
|
||||
Err("'again' called outside of a closure".to_string())
|
||||
}
|
||||
}
|
||||
BoundKind::Tuple { elements } => {
|
||||
NodeKind::Tuple { elements } => {
|
||||
let mut vals = Vec::with_capacity(elements.len());
|
||||
for e in elements {
|
||||
vals.push(self.eval_internal(obs, e)?);
|
||||
}
|
||||
Ok(Value::make_tuple(vals))
|
||||
}
|
||||
BoundKind::Record { layout, values } => {
|
||||
let mut evaluated_values = Vec::with_capacity(values.len());
|
||||
for v in values {
|
||||
NodeKind::Record { fields, layout } => {
|
||||
let mut evaluated_values = Vec::with_capacity(fields.len());
|
||||
for (_, v) in fields {
|
||||
evaluated_values.push(self.eval_internal(obs, v)?);
|
||||
}
|
||||
Ok(Value::Record(
|
||||
@@ -791,11 +802,11 @@ impl VM {
|
||||
std::rc::Rc::new(evaluated_values),
|
||||
))
|
||||
}
|
||||
BoundKind::Expansion { bound_expanded, .. } => {
|
||||
let mut curr = bound_expanded;
|
||||
NodeKind::Expansion { expanded, .. } => {
|
||||
let mut curr = expanded;
|
||||
if !O::ACTIVE {
|
||||
while let BoundKind::Expansion {
|
||||
bound_expanded: next,
|
||||
while let NodeKind::Expansion {
|
||||
expanded: next,
|
||||
..
|
||||
} = &curr.kind
|
||||
{
|
||||
@@ -804,11 +815,15 @@ impl VM {
|
||||
}
|
||||
self.eval_internal(obs, curr)
|
||||
}
|
||||
BoundKind::Extension(ext) => Err(format!(
|
||||
NodeKind::Extension(ext) => Err(format!(
|
||||
"Execution of extension '{}' not implemented yet",
|
||||
ext.display_name()
|
||||
)),
|
||||
BoundKind::Error => Err("Cannot execute a poisoned AST node".to_string()),
|
||||
NodeKind::Error => Err("Cannot execute a poisoned AST node".to_string()),
|
||||
// Syntax-only variants that should never appear in runtime phase
|
||||
NodeKind::MacroDecl { .. } | NodeKind::Template(_) | NodeKind::Placeholder(_) | NodeKind::Splice(_) => {
|
||||
Err("Syntax-only node reached the VM".to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -818,12 +833,12 @@ impl VM {
|
||||
args: &ExecNode,
|
||||
) -> Result<(), String> {
|
||||
match &args.kind {
|
||||
BoundKind::Tuple { elements } => {
|
||||
NodeKind::Tuple { elements } => {
|
||||
for e in elements {
|
||||
let mut curr = e.as_ref();
|
||||
if !O::ACTIVE {
|
||||
while let BoundKind::Expansion {
|
||||
bound_expanded: next,
|
||||
while let NodeKind::Expansion {
|
||||
expanded: next,
|
||||
..
|
||||
} = &curr.kind
|
||||
{
|
||||
@@ -832,7 +847,7 @@ impl VM {
|
||||
}
|
||||
|
||||
match &curr.kind {
|
||||
BoundKind::Constant(v) if !O::ACTIVE => self.stack.push(v.clone()),
|
||||
NodeKind::Constant(v) if !O::ACTIVE => self.stack.push(v.clone()),
|
||||
_ => {
|
||||
let val = self.eval_internal(obs, curr)?;
|
||||
self.stack.push(val);
|
||||
@@ -841,7 +856,7 @@ impl VM {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
BoundKind::Constant(v) => {
|
||||
NodeKind::Constant(v) => {
|
||||
if let Some(slice) = v.as_slice() {
|
||||
self.stack.extend_from_slice(slice);
|
||||
} else {
|
||||
@@ -849,11 +864,11 @@ impl VM {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
BoundKind::Expansion { bound_expanded, .. } => {
|
||||
let mut curr = bound_expanded;
|
||||
NodeKind::Expansion { expanded, .. } => {
|
||||
let mut curr = expanded;
|
||||
if !O::ACTIVE {
|
||||
while let BoundKind::Expansion {
|
||||
bound_expanded: next,
|
||||
while let NodeKind::Expansion {
|
||||
expanded: next,
|
||||
..
|
||||
} = &curr.kind
|
||||
{
|
||||
@@ -1008,17 +1023,15 @@ impl VM {
|
||||
offset: &mut usize,
|
||||
) -> Result<(), String> {
|
||||
match &pattern.kind {
|
||||
BoundKind::Define { addr, .. } => {
|
||||
NodeKind::Identifier { binding, .. } => {
|
||||
let addr = match binding {
|
||||
IdentifierBinding::Declaration { addr, .. } | IdentifierBinding::Reference(addr) => *addr,
|
||||
};
|
||||
let val = values.get(*offset).cloned().unwrap_or(Value::Void);
|
||||
*offset += 1;
|
||||
self.set_value(*addr, val)
|
||||
self.set_value(addr, val)
|
||||
}
|
||||
BoundKind::Set { addr, .. } => {
|
||||
let val = values.get(*offset).cloned().unwrap_or(Value::Void);
|
||||
*offset += 1;
|
||||
self.set_value(*addr, val)
|
||||
}
|
||||
BoundKind::Tuple { elements } => {
|
||||
NodeKind::Tuple { elements } => {
|
||||
if let Some(sub_values) = values.get(*offset).and_then(|v| v.as_slice()) {
|
||||
*offset += 1;
|
||||
let mut sub_offset = 0;
|
||||
|
||||
Reference in New Issue
Block a user