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:
2026-03-21 14:12:14 +01:00
parent 99fef2fc86
commit e65402364d
20 changed files with 6523 additions and 6068 deletions
-2
View File
@@ -1,5 +1,3 @@
;; Benchmark: 19.9us
;; Benchmark-Repeat: 110
(do (do
(repeat n 10 (print n) (repeat n 10 (print n)
) )
+387 -386
View File
@@ -1,386 +1,387 @@
use crate::ast::compiler::bound_nodes::{ use crate::ast::compiler::bound_nodes::{
Address, AnalyzedNode, BoundKind, GlobalIdx, Node, NodeMetrics, TypedNode, TypedPhase, Address, AnalyzedNode, GlobalIdx, IdentifierBinding, Node,
}; NodeKind, NodeMetrics, TypedNode, TypedPhase,
use crate::ast::types::Purity; };
use std::collections::{HashMap, HashSet}; use crate::ast::types::Purity;
use std::rc::Rc; use std::collections::{HashMap, HashSet};
use std::rc::Rc;
pub struct Analyzer<'a> {
root_purity: &'a [Purity], pub struct Analyzer<'a> {
/// Stack of currently visiting lambdas to detect direct recursion. root_purity: &'a [Purity],
lambda_stack: Vec<crate::ast::types::Identity>, /// Stack of currently visiting lambdas to detect direct recursion.
/// Map of global index to its Lambda identity if known. lambda_stack: Vec<crate::ast::types::Identity>,
globals_to_lambdas: HashMap<GlobalIdx, crate::ast::types::Identity>, /// Map of global index to its Lambda identity if known.
/// Set of identities that were found to be recursive. globals_to_lambdas: HashMap<GlobalIdx, crate::ast::types::Identity>,
recursive_identities: HashSet<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( impl<'a> Analyzer<'a> {
node: &TypedNode, pub fn analyze(
root_purity: &'a [Purity], node: &TypedNode,
) -> AnalyzedNode { root_purity: &'a [Purity],
let mut analyzer = Self { ) -> AnalyzedNode {
root_purity, let mut analyzer = Self {
lambda_stack: Vec::new(), root_purity,
globals_to_lambdas: HashMap::new(), lambda_stack: Vec::new(),
recursive_identities: HashSet::new(), globals_to_lambdas: HashMap::new(),
}; recursive_identities: HashSet::new(),
};
// First pass: map globals to their lambda identities
analyzer.collect_globals(node); // 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())) // Second pass: full analysis (decorating TypedNode into AnalyzedNode)
} analyzer.visit(Rc::new(node.clone()))
}
fn collect_globals(&mut self, node: &TypedNode) {
match &node.kind { fn collect_globals(&mut self, node: &TypedNode) {
BoundKind::Define { match &node.kind {
addr: Address::Global(global_index), NodeKind::Def {
value, pattern,
.. value,
} => { ..
if let BoundKind::Lambda { .. } = &value.kind { } => {
self.globals_to_lambdas if let NodeKind::Identifier {
.insert(*global_index, value.identity.clone()); binding: IdentifierBinding::Declaration { addr: Address::Global(global_index), .. },
} ..
self.collect_globals(value); } = &pattern.kind
} && let NodeKind::Lambda { .. } = &value.kind
BoundKind::Block { exprs } => { {
for e in exprs { self.globals_to_lambdas
self.collect_globals(e); .insert(*global_index, value.identity.clone());
} }
} self.collect_globals(value);
_ => { }
node.kind NodeKind::Block { exprs } => {
.for_each_child(|child| self.collect_globals(child)); for e in exprs {
} self.collect_globals(e);
} }
} }
_ => {
fn visit(&mut self, node_rc: Rc<TypedNode>) -> AnalyzedNode { node.kind
let node = &*node_rc; .for_each_child(|child| self.collect_globals(child));
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), fn visit(&mut self, node_rc: Rc<TypedNode>) -> AnalyzedNode {
let node = &*node_rc;
BoundKind::Get { addr, name } => { let mut is_recursive = false;
let p = match addr {
Address::Global(idx) => { let (new_kind, purity) = match &node.kind {
self.root_purity.get(idx.0 as usize).cloned().unwrap_or(Purity::Pure) NodeKind::Constant(v) => (NodeKind::Constant(v.clone()), Purity::Pure),
} NodeKind::Nop => (NodeKind::Nop, Purity::Pure),
_ => Purity::Pure,
}; NodeKind::Identifier { symbol, binding } => {
( let p = if let IdentifierBinding::Reference(Address::Global(idx)) = binding {
BoundKind::Get { self.root_purity.get(idx.0 as usize).cloned().unwrap_or(Purity::Pure)
addr: *addr, } else {
name: name.clone(), Purity::Pure
}, };
p, (
) NodeKind::Identifier {
} symbol: symbol.clone(),
binding: binding.clone(),
BoundKind::FieldAccessor(k) => (BoundKind::FieldAccessor(*k), Purity::Pure), },
p,
BoundKind::GetField { rec, field } => { )
let rec_m = self.visit(rec.clone()); }
let p = rec_m.ty.purity;
( NodeKind::FieldAccessor(k) => (NodeKind::FieldAccessor(*k), Purity::Pure),
BoundKind::GetField {
rec: Rc::new(rec_m), NodeKind::GetField { rec, field } => {
field: *field, let rec_m = self.visit(rec.clone());
}, let p = rec_m.ty.purity;
p, (
) NodeKind::GetField {
} rec: Rc::new(rec_m),
field: *field,
BoundKind::Set { addr, value } => { },
let val_m = self.visit(value.clone()); p,
( )
BoundKind::Set { }
addr: *addr,
value: Rc::new(val_m), NodeKind::Assign { target, value, info } => {
}, let target_m = self.visit(target.clone());
Purity::Impure, let val_m = self.visit(value.clone());
) (
} NodeKind::Assign {
target: Rc::new(target_m),
BoundKind::Define { value: Rc::new(val_m),
name, info: info.clone(),
addr, },
kind, Purity::Impure,
value, )
captured_by, }
} => {
let val_m = self.visit(value.clone()); NodeKind::Def {
( pattern,
BoundKind::Define { value,
name: name.clone(), info,
addr: *addr, } => {
kind: *kind, let pat_m = self.visit(pattern.clone());
value: Rc::new(val_m), let val_m = self.visit(value.clone());
captured_by: captured_by.clone(), (
}, NodeKind::Def {
Purity::Impure, pattern: Rc::new(pat_m),
) value: Rc::new(val_m),
} info: info.clone(),
},
BoundKind::If { Purity::Impure,
cond, )
then_br, }
else_br,
} => { NodeKind::If {
let cond_m = self.visit(cond.clone()); cond,
let then_m = self.visit(then_br.clone()); then_br,
let else_m = else_br.as_ref().map(|e| self.visit(e.clone())); else_br,
} => {
let mut p = cond_m.ty.purity.min(then_m.ty.purity); let cond_m = self.visit(cond.clone());
if let Some(ref em) = else_m { let then_m = self.visit(then_br.clone());
p = p.min(em.ty.purity); 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);
BoundKind::If { if let Some(ref em) = else_m {
cond: Rc::new(cond_m), p = p.min(em.ty.purity);
then_br: Rc::new(then_m), }
else_br: else_m.map(Rc::new), (
}, NodeKind::If {
p, cond: Rc::new(cond_m),
) then_br: Rc::new(then_m),
} else_br: else_m.map(Rc::new),
},
BoundKind::Lambda { p,
params, )
upvalues, }
body,
positional_count, NodeKind::Lambda {
} => { params,
self.lambda_stack.push(node.identity.clone()); body,
let params_m = self.visit(params.clone()); info,
let body_m = self.visit(body.clone()); } => {
self.lambda_stack.pop(); self.lambda_stack.push(node.identity.clone());
let params_m = self.visit(params.clone());
is_recursive = self.recursive_identities.contains(&node.identity); let body_m = self.visit(body.clone());
( self.lambda_stack.pop();
BoundKind::Lambda {
params: Rc::new(params_m), is_recursive = self.recursive_identities.contains(&node.identity);
upvalues: upvalues.clone(), (
body: Rc::new(body_m), NodeKind::Lambda {
positional_count: *positional_count, params: Rc::new(params_m),
}, body: Rc::new(body_m),
Purity::Pure, info: info.clone(),
) },
} Purity::Pure,
)
BoundKind::Destructure { pattern, value } => { }
let pat_m = self.visit(pattern.clone());
let val_m = self.visit(value.clone()); NodeKind::Call { callee, args } => {
( let callee_m = self.visit(callee.clone());
BoundKind::Destructure { let args_m = self.visit(args.clone());
pattern: Rc::new(pat_m),
value: Rc::new(val_m), if let NodeKind::Identifier {
}, binding: IdentifierBinding::Reference(Address::Global(idx)),
Purity::Impure, ..
) } = &callee.kind
} && let Some(lambda_id) = self.globals_to_lambdas.get(idx)
&& self.lambda_stack.contains(lambda_id)
BoundKind::Call { callee, args } => { {
let callee_m = self.visit(callee.clone()); self.recursive_identities.insert(lambda_id.clone());
let args_m = self.visit(args.clone()); is_recursive = true;
}
if let BoundKind::Get {
addr: Address::Global(idx), let p_func = if let NodeKind::Identifier {
.. binding: IdentifierBinding::Reference(Address::Global(idx)),
} = &callee.kind ..
&& let Some(lambda_id) = self.globals_to_lambdas.get(idx) } = &callee.kind
&& self.lambda_stack.contains(lambda_id) {
{ self.root_purity
self.recursive_identities.insert(lambda_id.clone()); .get(idx.0 as usize)
is_recursive = true; .cloned()
} .unwrap_or(Purity::Impure)
} else {
let p_func = if let BoundKind::Get { Purity::Impure
addr: Address::Global(idx), };
.. let p = callee_m.ty.purity.min(args_m.ty.purity).min(p_func);
} = &callee.kind (
{ NodeKind::Call {
self.root_purity callee: Rc::new(callee_m),
.get(idx.0 as usize) args: Rc::new(args_m),
.cloned() },
.unwrap_or(Purity::Impure) p,
} else { )
Purity::Impure }
};
let p = callee_m.ty.purity.min(args_m.ty.purity).min(p_func); NodeKind::Again { args } => {
( let args_m = self.visit(args.clone());
BoundKind::Call { if let Some(lambda_id) = self.lambda_stack.last() {
callee: Rc::new(callee_m), self.recursive_identities.insert(lambda_id.clone());
args: Rc::new(args_m), is_recursive = true;
}, }
p, (
) NodeKind::Again {
} args: Rc::new(args_m),
},
BoundKind::Again { args } => { Purity::Impure,
let args_m = self.visit(args.clone()); )
if let Some(lambda_id) = self.lambda_stack.last() { }
self.recursive_identities.insert(lambda_id.clone()); NodeKind::Pipe {
is_recursive = true; inputs,
} lambda,
( } => {
BoundKind::Again { let mut analyzed_inputs = Vec::with_capacity(inputs.len());
args: Rc::new(args_m), for input in inputs {
}, analyzed_inputs.push(Rc::new(self.visit(input.clone())));
Purity::Impure, }
) let a_lambda = Rc::new(self.visit(lambda.clone()));
} (
BoundKind::Pipe { NodeKind::Pipe {
inputs, inputs: analyzed_inputs,
lambda, lambda: a_lambda,
out_type, },
} => { Purity::Impure,
let mut analyzed_inputs = Vec::with_capacity(inputs.len()); )
for input in inputs { }
analyzed_inputs.push(Rc::new(self.visit(input.clone())));
} NodeKind::Block { exprs } => {
let a_lambda = Rc::new(self.visit(lambda.clone())); let mut new_exprs = Vec::with_capacity(exprs.len());
( let mut p = Purity::Pure;
BoundKind::Pipe { for e in exprs {
inputs: analyzed_inputs, let em = self.visit(e.clone());
lambda: a_lambda, p = p.min(em.ty.purity);
out_type: out_type.clone(), new_exprs.push(Rc::new(em));
}, }
Purity::Impure, (NodeKind::Block { exprs: new_exprs }, p)
) }
}
NodeKind::Tuple { elements } => {
BoundKind::Block { exprs } => { let mut new_elements = Vec::with_capacity(elements.len());
let mut new_exprs = Vec::with_capacity(exprs.len()); let mut p = Purity::Pure;
let mut p = Purity::Pure; for e in elements {
for e in exprs { let em = self.visit(e.clone());
let em = self.visit(e.clone()); p = p.min(em.ty.purity);
p = p.min(em.ty.purity); new_elements.push(Rc::new(em));
new_exprs.push(Rc::new(em)); }
} (
(BoundKind::Block { exprs: new_exprs }, p) NodeKind::Tuple {
} elements: new_elements,
},
BoundKind::Tuple { elements } => { p,
let mut new_elements = Vec::with_capacity(elements.len()); )
let mut p = Purity::Pure; }
for e in elements {
let em = self.visit(e.clone()); NodeKind::Record { fields, layout } => {
p = p.min(em.ty.purity); let mut new_fields = Vec::with_capacity(fields.len());
new_elements.push(Rc::new(em)); let mut p = Purity::Pure;
} for (key_node, val_node) in fields {
( let km = self.visit(key_node.clone());
BoundKind::Tuple { let vm = self.visit(val_node.clone());
elements: new_elements, p = p.min(vm.ty.purity);
}, new_fields.push((Rc::new(km), Rc::new(vm)));
p, }
) (
} NodeKind::Record {
fields: new_fields,
BoundKind::Record { layout, values } => { layout: layout.clone(),
let mut new_values = Vec::with_capacity(values.len()); },
let mut p = Purity::Pure; p,
for v in values { )
let vm = self.visit(v.clone()); }
p = p.min(vm.ty.purity);
new_values.push(Rc::new(vm)); NodeKind::Expansion {
} original_call,
( expanded,
BoundKind::Record { } => {
layout: layout.clone(), let expanded_m = self.visit(expanded.clone());
values: new_values, (
}, NodeKind::Expansion {
p, original_call: original_call.clone(),
) expanded: Rc::new(expanded_m.clone()),
} },
expanded_m.ty.purity,
BoundKind::Expansion { )
original_call, }
bound_expanded,
} => { NodeKind::Extension(_) => (NodeKind::Nop, Purity::Impure),
let expanded_m = self.visit(bound_expanded.clone()); NodeKind::Error => (NodeKind::Error, Purity::Impure),
(
BoundKind::Expansion { // Syntax-only variants should not appear in typed phases
original_call: original_call.clone(), NodeKind::MacroDecl { .. }
bound_expanded: Rc::new(expanded_m.clone()), | NodeKind::Template(_)
}, | NodeKind::Placeholder(_)
expanded_m.ty.purity, | NodeKind::Splice(_) => (NodeKind::Error, Purity::Impure),
) };
}
Node {
BoundKind::Extension(_) => (BoundKind::Nop, Purity::Impure), identity: node.identity.clone(),
BoundKind::Error => (BoundKind::Error, Purity::Impure), kind: new_kind,
}; ty: NodeMetrics {
original: node_rc,
Node { purity,
identity: node.identity.clone(), is_recursive,
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> {
trait NodeExt { fn for_each_child<F: FnMut(&TypedNode)>(&self, mut f: F) {
fn for_each_child<F: FnMut(&TypedNode)>(&self, f: F); match self {
} NodeKind::If {
cond,
impl NodeExt for BoundKind<TypedPhase> { then_br,
fn for_each_child<F: FnMut(&TypedNode)>(&self, mut f: F) { else_br,
match self { } => {
BoundKind::If { f(cond);
cond, f(then_br);
then_br, if let Some(e) = else_br {
else_br, f(e);
} => { }
f(cond); }
f(then_br); NodeKind::Def { pattern, value, .. } => {
if let Some(e) = else_br { f(pattern);
f(e); f(value);
} }
} NodeKind::Assign { target, value, .. } => {
BoundKind::Define { value, .. } | BoundKind::Set { value, .. } => { f(target);
f(value); f(value);
} }
BoundKind::GetField { rec, .. } => { NodeKind::GetField { rec, .. } => {
f(rec); f(rec);
} }
BoundKind::Lambda { params, body, .. } => { NodeKind::Lambda { params, body, .. } => {
f(params); f(params);
f(body); f(body);
} }
BoundKind::Call { callee, args } => { NodeKind::Call { callee, args } => {
f(callee); f(callee);
f(args); f(args);
} }
BoundKind::Block { exprs } => { NodeKind::Block { exprs } => {
for e in exprs { for e in exprs {
f(e); f(e);
} }
} }
BoundKind::Tuple { elements } => { NodeKind::Tuple { elements } => {
for e in elements { for e in elements {
f(e); f(e);
} }
} }
BoundKind::Record { values, .. } => { NodeKind::Record { fields, .. } => {
for v in values { for (key, val) in fields {
f(v); f(key);
} f(val);
} }
BoundKind::Expansion { bound_expanded, .. } => { }
f(bound_expanded); NodeKind::Expansion { expanded, .. } => {
} f(expanded);
_ => {} }
} _ => {}
} }
} }
}
+828 -789
View File
File diff suppressed because it is too large Load Diff
+31 -3
View File
@@ -181,11 +181,39 @@ impl CompilerPhase for RuntimePhase {
type RecordLayout = Arc<crate::ast::types::RecordLayout>; 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)] #[derive(Debug, PartialEq)]
pub struct Node<P: CompilerPhase = BoundPhase> { pub struct Node<P: CompilerPhase = BoundPhase> {
pub identity: Identity, pub identity: Identity,
pub kind: BoundKind<P>, pub kind: NodeKind<P>,
pub ty: P::Metadata, pub ty: P::Metadata,
} }
@@ -418,7 +446,7 @@ impl<P: CompilerPhase> BoundKind<P> {
BoundKind::Destructure { .. } => "DESTRUCTURE".to_string(), BoundKind::Destructure { .. } => "DESTRUCTURE".to_string(),
BoundKind::Lambda { params, upvalues, .. } => { BoundKind::Lambda { params, upvalues, .. } => {
let p_str = match &params.kind { let p_str = match &params.kind {
BoundKind::Tuple { elements } => format!("p:{}", elements.len()), NodeKind::Tuple { elements } => format!("p:{}", elements.len()),
_ => "p:1".to_string(), _ => "p:1".to_string(),
}; };
format!("LAMBDA({}, Captures:{})", p_str, upvalues.len()) format!("LAMBDA({}, Captures:{})", p_str, upvalues.len())
+122 -129
View File
@@ -1,129 +1,122 @@
use crate::ast::compiler::bound_nodes::{BoundKind, CompilerPhase, Node}; use crate::ast::compiler::bound_nodes::{CompilerPhase, DefBinding, Node, NodeKind};
use crate::ast::types::Identity; use crate::ast::types::Identity;
use std::collections::HashMap; use std::collections::HashMap;
use std::rc::Rc; use std::rc::Rc;
pub struct CapturePass; pub struct CapturePass;
impl CapturePass { impl CapturePass {
pub fn apply<P: CompilerPhase>(node: Node<P>, capture_map: &HashMap<Identity, Vec<Identity>>) -> Node<P> { pub fn apply<P: CompilerPhase<DefInfo = DefBinding>>(node: Node<P>, capture_map: &HashMap<Identity, Vec<Identity>>) -> Node<P> {
Self::transform(node, capture_map) Self::transform(node, capture_map)
} }
fn transform<P: CompilerPhase>(mut node: Node<P>, capture_map: &HashMap<Identity, Vec<Identity>>) -> Node<P> { fn transform<P: CompilerPhase<DefInfo = DefBinding>>(mut node: Node<P>, capture_map: &HashMap<Identity, Vec<Identity>>) -> Node<P> {
node.kind = match node.kind { node.kind = match node.kind {
BoundKind::Define { NodeKind::Def {
name, pattern,
addr, value,
kind, mut info,
value, } => {
mut captured_by, if let Some(capturers) = capture_map.get(&node.identity) {
} => { info.captured_by.extend(capturers.iter().cloned());
if let Some(capturers) = capture_map.get(&node.identity) { }
captured_by.extend(capturers.iter().cloned()); NodeKind::Def {
} pattern: Rc::new(Self::transform(pattern.as_ref().clone(), capture_map)),
BoundKind::Define { value: Rc::new(Self::transform(value.as_ref().clone(), capture_map)),
name, info,
addr, }
kind, }
value: Rc::new(Self::transform(value.as_ref().clone(), capture_map)),
captured_by, NodeKind::Lambda {
} params,
} body,
info,
BoundKind::Lambda { } => NodeKind::Lambda {
params, params: Rc::new(Self::transform(params.as_ref().clone(), capture_map)),
upvalues, body: Rc::new(Self::transform(body.as_ref().clone(), capture_map)),
body, info,
positional_count, },
} => BoundKind::Lambda {
params: Rc::new(Self::transform(params.as_ref().clone(), capture_map)), NodeKind::Call { callee, args } => NodeKind::Call {
upvalues, callee: Rc::new(Self::transform(callee.as_ref().clone(), capture_map)),
body: Rc::new(Self::transform(body.as_ref().clone(), capture_map)), args: Rc::new(Self::transform(args.as_ref().clone(), capture_map)),
positional_count, },
},
NodeKind::Again { args } => NodeKind::Again {
BoundKind::Call { callee, args } => BoundKind::Call { args: Rc::new(Self::transform(args.as_ref().clone(), capture_map)),
callee: Rc::new(Self::transform(callee.as_ref().clone(), capture_map)), },
args: Rc::new(Self::transform(args.as_ref().clone(), capture_map)),
}, NodeKind::If {
cond,
BoundKind::Again { args } => BoundKind::Again { then_br,
args: Rc::new(Self::transform(args.as_ref().clone(), capture_map)), else_br,
}, } => NodeKind::If {
cond: Rc::new(Self::transform(cond.as_ref().clone(), capture_map)),
BoundKind::If { then_br: Rc::new(Self::transform(then_br.as_ref().clone(), capture_map)),
cond, else_br: else_br
then_br, .map(|e| Rc::new(Self::transform(e.as_ref().clone(), capture_map))),
else_br, },
} => BoundKind::If {
cond: Rc::new(Self::transform(cond.as_ref().clone(), capture_map)), NodeKind::Assign { target, value, info } => NodeKind::Assign {
then_br: Rc::new(Self::transform(then_br.as_ref().clone(), capture_map)), target: Rc::new(Self::transform(target.as_ref().clone(), capture_map)),
else_br: else_br value: Rc::new(Self::transform(value.as_ref().clone(), capture_map)),
.map(|e| Rc::new(Self::transform(e.as_ref().clone(), capture_map))), info,
}, },
BoundKind::Destructure { pattern, value } => BoundKind::Destructure { NodeKind::Pipe {
pattern: Rc::new(Self::transform(pattern.as_ref().clone(), capture_map)), inputs,
value: Rc::new(Self::transform(value.as_ref().clone(), capture_map)), lambda,
}, } => {
let mut t_inputs = Vec::with_capacity(inputs.len());
BoundKind::Pipe { for input in inputs {
inputs, t_inputs.push(Rc::new(Self::transform(input.as_ref().clone(), capture_map)));
lambda, }
out_type, NodeKind::Pipe {
} => { inputs: t_inputs,
let mut t_inputs = Vec::with_capacity(inputs.len()); lambda: Rc::new(Self::transform(lambda.as_ref().clone(), capture_map)),
for input in inputs { }
t_inputs.push(Rc::new(Self::transform(input.as_ref().clone(), capture_map))); }
}
BoundKind::Pipe { NodeKind::Block { exprs } => NodeKind::Block {
inputs: t_inputs, exprs: exprs
lambda: Rc::new(Self::transform(lambda.as_ref().clone(), capture_map)), .into_iter()
out_type, .map(|e| Rc::new(Self::transform(e.as_ref().clone(), capture_map)))
} .collect(),
} },
BoundKind::Block { exprs } => BoundKind::Block { NodeKind::Tuple { elements } => NodeKind::Tuple {
exprs: exprs elements: elements
.into_iter() .into_iter()
.map(|e| Rc::new(Self::transform(e.as_ref().clone(), capture_map))) .map(|e| Rc::new(Self::transform(e.as_ref().clone(), capture_map)))
.collect(), .collect(),
}, },
BoundKind::Tuple { elements } => BoundKind::Tuple { NodeKind::Record { fields, layout } => NodeKind::Record {
elements: elements fields: fields
.into_iter() .into_iter()
.map(|e| Rc::new(Self::transform(e.as_ref().clone(), capture_map))) .map(|(k, v)| (k, Rc::new(Self::transform(v.as_ref().clone(), capture_map))))
.collect(), .collect(),
}, layout,
},
BoundKind::Record { layout, values } => BoundKind::Record {
layout, NodeKind::Expansion {
values: values original_call,
.into_iter() expanded,
.map(|v| Rc::new(Self::transform(v.as_ref().clone(), capture_map))) } => NodeKind::Expansion {
.collect(), original_call,
}, expanded: Rc::new(Self::transform(
expanded.as_ref().clone(),
BoundKind::Expansion { capture_map,
original_call, )),
bound_expanded, },
} => BoundKind::Expansion {
original_call, NodeKind::GetField { rec, field } => NodeKind::GetField {
bound_expanded: Rc::new(Self::transform( rec: Rc::new(Self::transform(rec.as_ref().clone(), capture_map)),
bound_expanded.as_ref().clone(), field,
capture_map, },
)),
}, other => other,
};
BoundKind::GetField { rec, field } => BoundKind::GetField { node
rec: Rc::new(Self::transform(rec.as_ref().clone(), capture_map)), }
field, }
},
other => other,
};
node
}
}
+240 -239
View File
@@ -1,239 +1,240 @@
use crate::ast::compiler::bound_nodes::{BoundKind, CompilerPhase, Node}; use crate::ast::compiler::bound_nodes::{CompilerPhase, Node, NodeKind};
/// Human-readable AST dumper for the bound AST. /// Human-readable AST dumper for the bound AST.
pub struct Dumper { pub struct Dumper {
output: String, output: String,
indent: usize, indent: usize,
} }
impl Dumper { impl Dumper {
/// Produces a formatted string representation of the given bound AST node and its children. /// Produces a formatted string representation of the given bound AST node and its children.
pub fn dump<P: CompilerPhase>(node: &Node<P>) -> String { pub fn dump<P: CompilerPhase>(node: &Node<P>) -> String {
let mut dumper = Self { let mut dumper = Self {
output: String::new(), output: String::new(),
indent: 0, indent: 0,
}; };
dumper.visit(node); dumper.visit(node);
dumper.output dumper.output
} }
fn write_indent(&mut self) { fn write_indent(&mut self) {
for _ in 0..self.indent { for _ in 0..self.indent {
self.output.push_str(" "); self.output.push_str(" ");
} }
} }
fn log<P: CompilerPhase>(&mut self, label: &str, node: &Node<P>) { fn log<P: CompilerPhase>(&mut self, label: &str, node: &Node<P>) {
self.write_indent(); self.write_indent();
self.output.push_str(label); self.output.push_str(label);
self.output self.output
.push_str(&format!(" <Metadata: {:?}>\n", node.ty)); .push_str(&format!(" <Metadata: {:?}>\n", node.ty));
} }
fn visit<P: CompilerPhase>(&mut self, node: &Node<P>) { fn visit<P: CompilerPhase>(&mut self, node: &Node<P>) {
match &node.kind { match &node.kind {
BoundKind::Nop => self.log("Nop", node), NodeKind::Nop => self.log("Nop", node),
BoundKind::Constant(v) => { NodeKind::Constant(v) => {
self.log(&format!("Constant: {}", v), node); self.log(&format!("Constant: {}", v), node);
} }
BoundKind::Get { addr, name } => { NodeKind::Identifier { symbol, binding } => {
self.log(&format!("Get: {} ({:?})", name.name, addr), node) self.log(&format!("Identifier: {} ({:?})", symbol.name, binding), node)
} }
BoundKind::FieldAccessor(k) => self.log(&format!("FieldAccessor: .{}", k.name()), node), NodeKind::FieldAccessor(k) => self.log(&format!("FieldAccessor: .{}", k.name()), node),
BoundKind::GetField { rec, field } => { NodeKind::GetField { rec, field } => {
self.log(&format!("GetField: .{}", field.name()), node); self.log(&format!("GetField: .{}", field.name()), node);
self.indent += 1; self.indent += 1;
self.visit(rec); self.visit(rec);
self.indent -= 1; self.indent -= 1;
} }
BoundKind::Set { addr, value } => { NodeKind::Assign { target, value, info } => {
self.log(&format!("Set: {:?}", addr), node); self.log(&format!("Assign: {:?}", info), node);
self.indent += 1; self.indent += 1;
self.visit(value); self.write_indent();
self.indent -= 1; self.output.push_str("Target:\n");
} self.visit(target);
self.write_indent();
BoundKind::Define { self.output.push_str("Value:\n");
name, self.visit(value);
addr, self.indent -= 1;
kind, }
value,
captured_by, NodeKind::Def {
} => { pattern,
let k_str = match kind { value,
crate::ast::compiler::bound_nodes::DeclarationKind::Variable => "Variable", info,
crate::ast::compiler::bound_nodes::DeclarationKind::Parameter => "Parameter", } => {
}; self.log(&format!("Def ({:?})", info), node);
let capture_info = if captured_by.is_empty() { self.indent += 1;
String::from("not captured") self.write_indent();
} else { self.output.push_str("Pattern:\n");
format!("captured by {} lambdas", captured_by.len()) self.visit(pattern);
}; self.write_indent();
self.log( self.output.push_str("Value:\n");
&format!( self.visit(value);
"Define {} (Name: '{}', Address: {:?}, {})", self.indent -= 1;
k_str, name.name, addr, capture_info }
),
node, NodeKind::If {
); cond,
then_br,
self.indent += 1; else_br,
if !captured_by.is_empty() { } => {
for capturer in captured_by { self.log("If", node);
self.write_indent(); self.indent += 1;
let loc = capturer
.location self.write_indent();
.unwrap_or(crate::ast::types::SourceLocation { line: 0, col: 0 }); self.output.push_str("Condition:\n");
self.output.push_str(&format!( self.visit(cond);
"- Capturer: Lambda at line {}, col {}\n",
loc.line, loc.col self.write_indent();
)); self.output.push_str("Then:\n");
} self.visit(then_br);
}
self.visit(value); if let Some(e) = else_br {
self.indent -= 1; self.write_indent();
} self.output.push_str("Else:\n");
self.visit(e);
BoundKind::Destructure { pattern, value } => { }
self.log("Destructure", node); self.indent -= 1;
self.indent += 1; }
self.write_indent();
self.output.push_str("Pattern:\n"); NodeKind::Lambda {
self.visit(pattern); params,
self.write_indent(); body,
self.output.push_str("Value:\n"); info,
self.visit(value); } => {
self.indent -= 1; self.log(&format!("Lambda ({:?})", info), node);
} self.indent += 1;
BoundKind::If { self.write_indent();
cond, self.output.push_str("Parameters:\n");
then_br, self.visit(params);
else_br,
} => { self.visit(body);
self.log("If", node); self.indent -= 1;
self.indent += 1; }
self.write_indent(); NodeKind::Call { callee, args } => {
self.output.push_str("Condition:\n"); self.log("Call", node);
self.visit(cond); self.indent += 1;
self.write_indent(); self.write_indent();
self.output.push_str("Then:\n"); self.output.push_str("Callee:\n");
self.visit(then_br); self.visit(callee);
if let Some(e) = else_br { self.write_indent();
self.write_indent(); self.output.push_str("Arguments:\n");
self.output.push_str("Else:\n"); self.visit(args);
self.visit(e);
} self.indent -= 1;
self.indent -= 1; }
}
NodeKind::Again { args } => {
BoundKind::Lambda { self.log("Again", node);
params, self.indent += 1;
upvalues, self.visit(args);
body, self.indent -= 1;
.. }
} => { NodeKind::Pipe { inputs, lambda } => {
self.log(&format!("Lambda (Upvalues: {})", upvalues.len()), node); self.log("Pipe", node);
self.indent += 1; self.indent += 1;
for input in inputs {
self.write_indent(); self.visit(input);
self.output.push_str("Parameters:\n"); }
self.visit(params); self.visit(lambda);
self.indent -= 1;
if !upvalues.is_empty() { }
self.write_indent();
self.output.push_str(&format!("Upvalues: {:?}\n", upvalues)); NodeKind::Block { exprs } => {
} self.log("Block", node);
self.visit(body); self.indent += 1;
self.indent -= 1; for expr in exprs {
} self.visit(expr);
}
BoundKind::Call { callee, args } => { self.indent -= 1;
self.log("Call", node); }
self.indent += 1;
NodeKind::Tuple { elements } => {
self.write_indent(); self.log("Tuple", node);
self.output.push_str("Callee:\n"); self.indent += 1;
self.visit(callee); for el in elements {
self.visit(el);
self.write_indent(); }
self.output.push_str("Arguments:\n"); self.indent -= 1;
self.visit(args); }
self.indent -= 1; NodeKind::Record { fields, layout } => {
} self.log(
&format!("Record (Layout: {:?})", layout),
BoundKind::Again { args } => { node,
self.log("Again", node); );
self.indent += 1; self.indent += 1;
self.visit(args); for (key, val) in fields {
self.indent -= 1; self.write_indent();
} self.output.push_str("Key:\n");
BoundKind::Pipe { inputs, lambda, .. } => { self.indent += 1;
self.log("Pipe", node); self.visit(key);
self.indent += 1; self.indent -= 1;
for input in inputs { self.write_indent();
self.visit(input); self.output.push_str("Value:\n");
} self.indent += 1;
self.visit(lambda); self.visit(val);
self.indent -= 1; self.indent -= 1;
} }
self.indent -= 1;
BoundKind::Block { exprs } => { }
self.log("Block", node);
self.indent += 1; NodeKind::Expansion {
for expr in exprs { original_call,
self.visit(expr); expanded,
} } => {
self.indent -= 1; self.log(
} &format!("Expansion (Original: {:?})", original_call.kind),
node,
BoundKind::Tuple { elements } => { );
self.log("Tuple", node); self.indent += 1;
self.indent += 1; self.visit(expanded);
for el in elements { self.indent -= 1;
self.visit(el); }
}
self.indent -= 1; NodeKind::MacroDecl { name, params, body } => {
} self.log(&format!("MacroDecl: {}", name.name), node);
self.indent += 1;
BoundKind::Record { layout, values } => { self.visit(params);
self.log( self.visit(body);
&format!("Record (Layout: {} fields)", layout.fields.len()), self.indent -= 1;
node, }
);
self.indent += 1; NodeKind::Template(inner) => {
for v in values { self.log("Template", node);
self.visit(v); self.indent += 1;
} self.visit(inner);
self.indent -= 1; self.indent -= 1;
} }
BoundKind::Expansion { NodeKind::Placeholder(inner) => {
original_call, self.log("Placeholder", node);
bound_expanded, self.indent += 1;
} => { self.visit(inner);
self.log( self.indent -= 1;
&format!("Expansion (Original: {:?})", original_call.kind), }
node,
); NodeKind::Splice(inner) => {
self.indent += 1; self.log("Splice", node);
self.visit(bound_expanded); self.indent += 1;
self.indent -= 1; self.visit(inner);
} self.indent -= 1;
}
BoundKind::Extension(ext) => {
self.log(&ext.display_name(), node); NodeKind::Extension(ext) => {
} self.log(&ext.display_name(), node);
BoundKind::Error => { }
self.log("ERROR_NODE", node); NodeKind::Error => {
} self.log("ERROR_NODE", node);
} }
} }
} }
}
+111 -99
View File
@@ -1,99 +1,111 @@
use crate::ast::compiler::bound_nodes::{Address, BoundKind, CompilerPhase, GlobalIdx, Node}; use crate::ast::compiler::bound_nodes::{
use std::collections::HashMap; Address, BoundLike, GlobalIdx, IdentifierBinding, Node, NodeKind,
use std::rc::Rc; };
use std::collections::HashMap;
/// A pass that collects all global function definitions (lambdas) into a registry. use std::rc::Rc;
/// This allows the Specializer to retrieve the original AST of a function for monomorphization.
pub struct LambdaCollector<'a, P: CompilerPhase> { /// A pass that collects all global function definitions (lambdas) into a registry.
registry: &'a mut HashMap<GlobalIdx, Rc<Node<P>>>, /// 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: 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>>>) { impl<'a, P> LambdaCollector<'a, P>
let mut collector = Self { registry }; where
collector.visit(node); P: BoundLike,
} {
/// Performs a full traversal of the AST and populates the provided registry.
fn visit(&mut self, node: &Node<P>) { pub fn collect(node: &Node<P>, registry: &'a mut HashMap<GlobalIdx, Rc<Node<P>>>) {
match &node.kind { let mut collector = Self { registry };
BoundKind::Block { exprs } => { collector.visit(node);
for expr in exprs { }
self.visit(expr);
} fn visit(&mut self, node: &Node<P>) {
} match &node.kind {
NodeKind::Block { exprs } => {
BoundKind::Define { addr, value, .. } => { for expr in exprs {
// Register global function definitions (lambdas) self.visit(expr);
if let Address::Global(global_index) = addr { }
let mut current = value; }
while let BoundKind::Expansion { bound_expanded, .. } = &current.kind {
current = bound_expanded; NodeKind::Def { pattern, value, .. } => {
} // Register global function definitions (lambdas)
if let NodeKind::Identifier {
if let BoundKind::Lambda { .. } = &current.kind { binding: IdentifierBinding::Declaration {
self.registry addr: Address::Global(global_index),
.insert(*global_index, (*current).clone()); ..
} },
} ..
self.visit(value); } = &pattern.kind
} {
let mut current = value;
BoundKind::Set { addr, value } => { while let NodeKind::Expansion { expanded, .. } = &current.kind {
// Also track assignments to globals if they hold lambdas. current = expanded;
if let Address::Global(global_index) = addr { }
let mut current = value;
while let BoundKind::Expansion { bound_expanded, .. } = &current.kind { if let NodeKind::Lambda { .. } = &current.kind {
current = bound_expanded; self.registry
} .insert(*global_index, (*current).clone());
}
if let BoundKind::Lambda { .. } = &current.kind { }
self.registry self.visit(value);
.insert(*global_index, (*current).clone()); }
}
} NodeKind::Assign { value, info, .. } => {
self.visit(value); // Also track assignments to globals if they hold lambdas.
} if let Some(Address::Global(global_index)) = &info.addr {
let mut current = value;
BoundKind::If { while let NodeKind::Expansion { expanded, .. } = &current.kind {
cond, current = expanded;
then_br, }
else_br,
} => { if let NodeKind::Lambda { .. } = &current.kind {
self.visit(cond); self.registry
self.visit(then_br); .insert(*global_index, (*current).clone());
if let Some(e) = else_br { }
self.visit(e); }
} self.visit(value);
} }
BoundKind::Lambda { params, body, .. } => { NodeKind::If {
self.visit(params); cond,
self.visit(body); then_br,
} else_br,
} => {
BoundKind::Call { callee, args } => { self.visit(cond);
self.visit(callee); self.visit(then_br);
self.visit(args); if let Some(e) = else_br {
} self.visit(e);
}
BoundKind::Tuple { elements } => { }
for el in elements {
self.visit(el); NodeKind::Lambda { params, body, .. } => {
} self.visit(params);
} self.visit(body);
}
BoundKind::Record { values, .. } => {
for v in values { NodeKind::Call { callee, args } => {
self.visit(v); self.visit(callee);
} self.visit(args);
} }
BoundKind::Expansion { bound_expanded, .. } => { NodeKind::Tuple { elements } => {
self.visit(bound_expanded); for el in elements {
} self.visit(el);
}
_ => {} // Leaf nodes }
}
} NodeKind::Record { fields, .. } => {
} for (_, v) in fields {
self.visit(v);
}
}
NodeKind::Expansion { expanded, .. } => {
self.visit(expanded);
}
_ => {} // Leaf nodes
}
}
}
+283 -231
View File
@@ -1,231 +1,283 @@
use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, ExecNode, Node, RuntimeMetadata, StackOffset, VirtualId}; use crate::ast::compiler::bound_nodes::{
use std::collections::HashMap; Address, AnalyzedNode, AssignBinding, ExecNode,
use std::rc::Rc; IdentifierBinding, LambdaBinding, Node, NodeKind, RuntimeMetadata,
StackOffset, VirtualId,
struct StackAllocator { };
mapping: HashMap<u32, u32>, use std::collections::HashMap;
next_slot: u32, use std::rc::Rc;
}
struct StackAllocator {
impl StackAllocator { mapping: HashMap<u32, u32>,
fn new() -> Self { next_slot: u32,
Self { }
mapping: HashMap::new(),
next_slot: 0, impl StackAllocator {
} fn new() -> Self {
} Self {
mapping: HashMap::new(),
fn map_slot(&mut self, slot: VirtualId) -> StackOffset { next_slot: 0,
let entry = self.mapping.entry(slot.0).or_insert_with(|| { }
let s = self.next_slot; }
self.next_slot += 1;
s fn map_slot(&mut self, slot: VirtualId) -> StackOffset {
}); let entry = self.mapping.entry(slot.0).or_insert_with(|| {
StackOffset(*entry) let s = self.next_slot;
} self.next_slot += 1;
s
fn map_address(&mut self, addr: Address<VirtualId>) -> Address<StackOffset> { });
match addr { StackOffset(*entry)
Address::Local(slot) => Address::Local(self.map_slot(slot)), }
Address::Upvalue(idx) => Address::Upvalue(idx),
Address::Global(idx) => Address::Global(idx), 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 { pub struct Lowering;
let mut allocator = StackAllocator::new();
let mut exec_node = Self::transform(Rc::new(node), true, &mut allocator); impl Lowering {
/// Lowers an AnalyzedNode to an ExecNode, marking tail positions and calculating stack sizes.
// If the top-level node is a Lambda, it already has its internal stack_size pub fn lower(node: AnalyzedNode) -> ExecNode {
// calculated during transform(). For non-lambdas (like raw expressions), let mut allocator = StackAllocator::new();
// we use the allocator's next_slot to determine the required root stack size. let mut exec_node = Self::transform(Rc::new(node), true, &mut allocator);
if !matches!(exec_node.kind, BoundKind::Lambda { .. }) {
exec_node.ty.stack_size = allocator.next_slot; // If the top-level node is a Lambda, it already has its internal stack_size
} // calculated during transform(). For non-lambdas (like raw expressions),
exec_node // 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;
fn transform( }
node_rc: Rc<AnalyzedNode>, exec_node
is_tail_position: bool, }
allocator: &mut StackAllocator,
) -> ExecNode { fn transform(
let node = &*node_rc; node_rc: Rc<AnalyzedNode>,
let mut lambda_stack_size = 0; is_tail_position: bool,
allocator: &mut StackAllocator,
let new_kind = match &node.kind { ) -> ExecNode {
BoundKind::Call { callee, args } => BoundKind::Call { let node = &*node_rc;
callee: Rc::new(Self::transform(callee.clone(), false, allocator)), let mut lambda_stack_size = 0;
args: Rc::new(Self::transform(args.clone(), false, allocator)),
}, let new_kind = match &node.kind {
NodeKind::Call { callee, args } => {
BoundKind::Again { args } => { // Optimized Field Access: Call { callee: FieldAccessor, args: Tuple[1] } → GetField
if !is_tail_position { if let NodeKind::FieldAccessor(k) = &callee.kind
panic!("'again' is only allowed in tail position to avoid dead code."); && let NodeKind::Tuple { elements } = &args.kind
} && elements.len() == 1
BoundKind::Again { {
args: Rc::new(Self::transform(args.clone(), false, allocator)), let rec = Rc::new(Self::transform(elements[0].clone(), false, allocator));
} return Node {
} identity: node.identity.clone(),
BoundKind::Pipe { kind: NodeKind::GetField {
inputs, rec,
lambda, field: *k,
out_type, },
} => { ty: RuntimeMetadata {
let mut t_inputs = Vec::with_capacity(inputs.len()); ty: node.ty.original.ty.clone(),
for input in inputs { is_tail: is_tail_position,
t_inputs.push(Rc::new(Self::transform(input.clone(), false, allocator))); original: node_rc,
} stack_size: 0,
BoundKind::Pipe { },
inputs: t_inputs, };
lambda: Rc::new(Self::transform(lambda.clone(), false, allocator)), }
out_type: out_type.clone(),
} NodeKind::Call {
} callee: Rc::new(Self::transform(callee.clone(), false, allocator)),
args: Rc::new(Self::transform(args.clone(), false, allocator)),
BoundKind::If { }
cond, }
then_br,
else_br, NodeKind::Again { args } => {
} => BoundKind::If { if !is_tail_position {
cond: Rc::new(Self::transform(cond.clone(), false, allocator)), panic!("'again' is only allowed in tail position to avoid dead code.");
then_br: Rc::new(Self::transform(then_br.clone(), is_tail_position, allocator)), }
else_br: else_br NodeKind::Again {
.as_ref() args: Rc::new(Self::transform(args.clone(), false, allocator)),
.map(|e| Rc::new(Self::transform(e.clone(), is_tail_position, allocator))), }
}, }
NodeKind::Pipe {
BoundKind::Block { exprs } => { inputs,
if exprs.is_empty() { lambda,
BoundKind::Block { exprs: vec![] } } => {
} else { let mut t_inputs = Vec::with_capacity(inputs.len());
let last_idx = exprs.len() - 1; for input in inputs {
let mut new_exprs = Vec::with_capacity(exprs.len()); t_inputs.push(Rc::new(Self::transform(input.clone(), false, allocator)));
}
for (i, expr) in exprs.iter().enumerate() { NodeKind::Pipe {
let is_last = i == last_idx; inputs: t_inputs,
new_exprs.push(Rc::new(Self::transform( lambda: Rc::new(Self::transform(lambda.clone(), false, allocator)),
expr.clone(), }
is_tail_position && is_last, }
allocator,
))); NodeKind::If {
} cond,
BoundKind::Block { exprs: new_exprs } then_br,
} else_br,
} } => NodeKind::If {
cond: Rc::new(Self::transform(cond.clone(), false, allocator)),
BoundKind::Lambda { then_br: Rc::new(Self::transform(then_br.clone(), is_tail_position, allocator)),
params, else_br: else_br
upvalues, .as_ref()
body, .map(|e| Rc::new(Self::transform(e.clone(), is_tail_position, allocator))),
positional_count, },
} => {
// Upvalues refer to the PARENT scope's addresses. NodeKind::Block { exprs } => {
let mapped_upvalues = upvalues if exprs.is_empty() {
.iter() NodeKind::Block { exprs: vec![] }
.map(|a| allocator.map_address(*a)) } else {
.collect(); let last_idx = exprs.len() - 1;
let mut new_exprs = Vec::with_capacity(exprs.len());
// New allocator for the lambda's own stack frame
let mut lambda_allocator = StackAllocator::new(); for (i, expr) in exprs.iter().enumerate() {
let t_params = Rc::new(Self::transform(params.clone(), false, &mut lambda_allocator)); let is_last = i == last_idx;
let t_body = Rc::new(Self::transform(body.clone(), true, &mut lambda_allocator)); new_exprs.push(Rc::new(Self::transform(
lambda_stack_size = lambda_allocator.next_slot; expr.clone(),
is_tail_position && is_last,
BoundKind::Lambda { allocator,
params: t_params, )));
upvalues: mapped_upvalues, }
body: t_body, NodeKind::Block { exprs: new_exprs }
positional_count: *positional_count, }
} }
}
NodeKind::Lambda {
BoundKind::Set { addr, value } => BoundKind::Set { params,
addr: allocator.map_address(*addr), body,
value: Rc::new(Self::transform(value.clone(), false, allocator)), info: lambda_info,
}, } => {
BoundKind::Define { // Upvalues refer to the PARENT scope's addresses.
name, let mapped_upvalues = lambda_info
addr, .upvalues
kind, .iter()
value, .map(|a| allocator.map_address(*a))
captured_by, .collect();
} => BoundKind::Define {
name: name.clone(), // New allocator for the lambda's own stack frame
addr: allocator.map_address(*addr), let mut lambda_allocator = StackAllocator::new();
kind: *kind, let t_params = Rc::new(Self::transform(params.clone(), false, &mut lambda_allocator));
value: Rc::new(Self::transform(value.clone(), false, allocator)), let t_body = Rc::new(Self::transform(body.clone(), true, &mut lambda_allocator));
captured_by: captured_by.clone(), lambda_stack_size = lambda_allocator.next_slot;
},
BoundKind::Destructure { pattern, value } => BoundKind::Destructure { NodeKind::Lambda {
pattern: Rc::new(Self::transform(pattern.clone(), false, allocator)), params: t_params,
value: Rc::new(Self::transform(value.clone(), false, allocator)), body: t_body,
}, info: LambdaBinding {
BoundKind::Record { layout, values } => { upvalues: mapped_upvalues,
let new_values = values positional_count: lambda_info.positional_count,
.iter() },
.map(|v| Rc::new(Self::transform(v.clone(), false, allocator))) }
.collect(); }
BoundKind::Record {
layout: layout.clone(), NodeKind::Assign { target, value, info } => {
values: new_values, let new_info = if let Some(addr) = info.addr {
} AssignBinding {
} addr: Some(allocator.map_address(addr)),
BoundKind::Tuple { elements } => { }
let new_elements = elements } else {
.iter() AssignBinding { addr: None }
.map(|e| Rc::new(Self::transform(e.clone(), false, allocator))) };
.collect(); NodeKind::Assign {
BoundKind::Tuple { target: Rc::new(Self::transform(target.clone(), false, allocator)),
elements: new_elements, value: Rc::new(Self::transform(value.clone(), false, allocator)),
} info: new_info,
} }
BoundKind::Constant(v) => BoundKind::Constant(v.clone()), }
BoundKind::Get { addr, name } => BoundKind::Get { NodeKind::Def {
addr: allocator.map_address(*addr), pattern,
name: name.clone(), value,
}, info,
BoundKind::FieldAccessor(k) => BoundKind::FieldAccessor(*k), } => NodeKind::Def {
BoundKind::GetField { rec, field } => BoundKind::GetField { pattern: Rc::new(Self::transform(pattern.clone(), false, allocator)),
rec: Rc::new(Self::transform(rec.clone(), false, allocator)), value: Rc::new(Self::transform(value.clone(), false, allocator)),
field: *field, info: info.clone(),
}, },
BoundKind::Nop => BoundKind::Nop, NodeKind::Identifier { symbol, binding } => {
BoundKind::Expansion { let new_binding = match binding {
original_call, IdentifierBinding::Reference(addr) => {
bound_expanded, IdentifierBinding::Reference(allocator.map_address(*addr))
} => BoundKind::Expansion { }
original_call: original_call.clone(), IdentifierBinding::Declaration { addr, kind } => {
bound_expanded: Rc::new(Self::transform( IdentifierBinding::Declaration {
bound_expanded.clone(), addr: allocator.map_address(*addr),
is_tail_position, kind: *kind,
allocator, }
)), }
}, };
BoundKind::Extension(_) => BoundKind::Nop, NodeKind::Identifier {
BoundKind::Error => BoundKind::Error, symbol: symbol.clone(),
}; binding: new_binding,
}
let stack_size = if let BoundKind::Lambda { .. } = &new_kind { }
lambda_stack_size NodeKind::Record { fields, layout } => {
} else { let new_fields = fields
0 .iter()
}; .map(|(k, v)| {
(
Node { Rc::new(Self::transform(k.clone(), false, allocator)),
identity: node.identity.clone(), Rc::new(Self::transform(v.clone(), false, allocator)),
kind: new_kind, )
ty: RuntimeMetadata { })
ty: node.ty.original.ty.clone(), .collect();
is_tail: is_tail_position, NodeKind::Record {
original: node_rc, fields: new_fields,
stack_size, 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
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+103 -101
View File
@@ -1,101 +1,103 @@
use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, Node, NodeMetrics}; use crate::ast::compiler::bound_nodes::{
use crate::ast::types::{Purity, RecordLayout, StaticType, Value}; Address, AnalyzedNode, IdentifierBinding, Node, NodeKind, NodeMetrics,
use std::cell::RefCell; };
use std::rc::Rc; use crate::ast::types::{Purity, RecordLayout, StaticType, Value};
use std::cell::RefCell;
pub struct Folder<'a> { use std::rc::Rc;
pub globals: &'a Option<Rc<RefCell<Vec<Value>>>>,
} 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 } 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 { pub fn make_constant_node(&self, val: Value, template: &AnalyzedNode) -> AnalyzedNode {
identity: template.identity.clone(), let ty = val.static_type();
kind: BoundKind::Constant(val.clone()), let typed_original = Rc::new(Node {
ty: ty.clone(), identity: template.identity.clone(),
}); kind: NodeKind::Constant(val.clone()),
Node { ty: ty.clone(),
identity: template.identity.clone(), });
kind: BoundKind::Constant(val), Node {
ty: NodeMetrics { identity: template.identity.clone(),
original: typed_original, kind: NodeKind::Constant(val),
purity: Purity::Pure, ty: NodeMetrics {
is_recursive: false, 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(), pub fn make_nop_node(&self, template: &AnalyzedNode) -> AnalyzedNode {
kind: BoundKind::Nop, let typed_original = Rc::new(Node {
ty: StaticType::Void, identity: template.identity.clone(),
}); kind: NodeKind::Nop,
Node { ty: StaticType::Void,
identity: template.identity.clone(), });
kind: BoundKind::Nop, Node {
ty: NodeMetrics { identity: template.identity.clone(),
original: typed_original, kind: NodeKind::Nop,
purity: Purity::Pure, ty: NodeMetrics {
is_recursive: false, original: typed_original,
}, purity: Purity::Pure,
} is_recursive: false,
} },
}
pub fn try_fold_record( }
&self,
layout: &std::sync::Arc<RecordLayout>, pub fn try_fold_record(
values: &[Rc<AnalyzedNode>], &self,
template: &AnalyzedNode, layout: &std::sync::Arc<RecordLayout>,
) -> Option<AnalyzedNode> { values: &[Rc<AnalyzedNode>],
let mut constant_values = Vec::with_capacity(values.len()); template: &AnalyzedNode,
) -> Option<AnalyzedNode> {
for v_node in values { let mut constant_values = Vec::with_capacity(values.len());
if let BoundKind::Constant(val) = &v_node.kind {
constant_values.push(val.clone()); for v_node in values {
} else { if let NodeKind::Constant(val) = &v_node.kind {
return None; 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))
} 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, pub fn try_fold_pure(
arg_nodes: &[Rc<AnalyzedNode>], &self,
) -> Option<AnalyzedNode> { callee: &AnalyzedNode,
if callee.ty.purity < Purity::Pure { arg_nodes: &[Rc<AnalyzedNode>],
return None; ) -> 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 { let mut arg_values = Vec::with_capacity(arg_nodes.len());
arg_values.push(val.clone()); for node in arg_nodes {
} else { if let NodeKind::Constant(val) = &node.kind {
return None; arg_values.push(val.clone());
} } else {
} return None;
let func_val = match &callee.kind { }
BoundKind::Get { }
addr: Address::Global(idx), let func_val = match &callee.kind {
.. NodeKind::Identifier {
} => self.globals.as_ref()?.borrow().get(idx.0 as usize)?.clone(), binding: IdentifierBinding::Reference(Address::Global(idx)),
BoundKind::Constant(val) => val.clone(), ..
_ => return None, } => self.globals.as_ref()?.borrow().get(idx.0 as usize)?.clone(),
}; NodeKind::Constant(val) => val.clone(),
let result = match func_val { _ => return None,
Value::Function(f) => (f.func)(&arg_values), };
_ => return None, let result = match func_val {
}; Value::Function(f) => (f.func)(&arg_values),
Some(self.make_constant_node(result, callee)) _ => return None,
} };
} Some(self.make_constant_node(result, callee))
}
}
+222 -165
View File
@@ -1,165 +1,222 @@
use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, VirtualId}; use crate::ast::compiler::bound_nodes::{
use crate::ast::types::{Purity, Value}; Address, AnalyzedNode, IdentifierBinding, NodeKind, VirtualId,
use crate::ast::vm::Closure; };
use std::cell::RefCell; use crate::ast::types::{Purity, Value};
use std::collections::HashSet; use crate::ast::vm::Closure;
use std::rc::Rc; use std::cell::RefCell;
use std::collections::HashSet;
use super::substitution_map::SubstitutionMap; use std::rc::Rc;
use super::utils::UsageInfo;
use super::substitution_map::SubstitutionMap;
pub struct Inliner<'a> { use super::utils::UsageInfo;
pub globals: &'a Option<Rc<RefCell<Vec<Value>>>>,
pub root_purity: &'a Option<Rc<RefCell<Vec<Purity>>>>, 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>>>>, impl<'a> Inliner<'a> {
root_purity: &'a Option<Rc<RefCell<Vec<Purity>>>>, pub fn new(
) -> Self { globals: &'a Option<Rc<RefCell<Vec<Value>>>>,
Self { root_purity: &'a Option<Rc<RefCell<Vec<Purity>>>>,
globals, ) -> Self {
root_purity, Self {
} globals,
} root_purity,
}
pub fn is_inlinable_value(&self, val: &Value, addr: Address<VirtualId>) -> bool { }
let type_ok = match val {
Value::Int(_) pub fn is_inlinable_value(&self, val: &Value, addr: Address<VirtualId>) -> bool {
| Value::Float(_) let type_ok = match val {
| Value::Bool(_) Value::Int(_)
| Value::Text(_) | Value::Float(_)
| Value::Keyword(_) | Value::Bool(_)
| Value::Record(_, _) | Value::Text(_)
| Value::DateTime(_) => true, | Value::Keyword(_)
Value::Object(obj) => { | Value::Record(_, _)
if let Some(closure) = obj.as_any().downcast_ref::<Closure>() { | Value::DateTime(_) => true,
closure.upvalues.is_empty() && !closure.function_node.ty.is_recursive Value::Object(obj) => {
} else { if let Some(closure) = obj.as_any().downcast_ref::<Closure>() {
false closure.upvalues.is_empty() && !closure.function_node.ty.is_recursive
} } else {
} false
_ => false, }
}; }
_ => false,
if !type_ok { };
return false;
} if !type_ok {
return false;
if let Address::Global(idx) = addr { }
if let Some(purity_rc) = &self.root_purity {
let purity = purity_rc if let Address::Global(idx) = addr {
.borrow() if let Some(purity_rc) = &self.root_purity {
.get(idx.0 as usize) let purity = purity_rc
.cloned() .borrow()
.unwrap_or(Purity::Impure); .get(idx.0 as usize)
return purity >= Purity::Pure; .cloned()
} .unwrap_or(Purity::Impure);
return false; return purity >= Purity::Pure;
} }
return false;
true }
}
true
pub fn prepare_beta_reduction( }
&self,
params: &AnalyzedNode, pub fn prepare_beta_reduction(
arg_vals: &[Rc<AnalyzedNode>], &self,
body: &AnalyzedNode, params: &AnalyzedNode,
sub: &mut SubstitutionMap, arg_vals: &[Rc<AnalyzedNode>],
) -> Option<()> { body: &AnalyzedNode,
let mut body_usage = UsageInfo::default(); sub: &mut SubstitutionMap,
body_usage.collect(body); ) -> Option<()> {
let mut body_usage = UsageInfo::default();
let mut slot_index = 0; body_usage.collect(body);
self.map_params_to_args(params, arg_vals, &mut slot_index, sub, &body_usage);
let mut slot_index = 0;
if slot_index != arg_vals.len() { self.map_params_to_args(params, arg_vals, &mut slot_index, sub, &body_usage);
return None;
} if slot_index != arg_vals.len() {
return None;
let mut param_slots = HashSet::new(); }
self.collect_parameter_slots_set(params, &mut param_slots);
let mut param_slots = HashSet::new();
for slot in param_slots { self.collect_parameter_slots_set(params, &mut param_slots);
let addr = Address::Local(slot);
if (body_usage.is_used(&addr) || body_usage.is_assigned(&addr)) for slot in param_slots {
&& sub.get_value(&addr).is_none() let addr = Address::Local(slot);
&& !sub.ast_substitutions.contains_key(&addr) if (body_usage.is_used(&addr) || body_usage.is_assigned(&addr))
{ && sub.get_value(&addr).is_none()
return 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;
} if sub.values.is_empty() && sub.ast_substitutions.is_empty() && !arg_vals.is_empty() {
return None;
Some(()) }
}
Some(())
pub fn map_params_to_args( }
&self,
pattern: &AnalyzedNode, pub fn map_params_to_args(
args: &[Rc<AnalyzedNode>], &self,
offset: &mut usize, pattern: &AnalyzedNode,
sub: &mut SubstitutionMap, args: &[Rc<AnalyzedNode>],
body_usage: &UsageInfo, offset: &mut usize,
) { sub: &mut SubstitutionMap,
match &pattern.kind { body_usage: &UsageInfo,
BoundKind::Define { addr, .. } => { ) {
if let Some(arg) = args.get(*offset) match &pattern.kind {
&& !body_usage.is_assigned(addr) NodeKind::Def { pattern: inner_pattern, .. } => {
{ // Extract addr from the pattern's Identifier binding
let mut core_arg = arg.as_ref(); let addr = if let NodeKind::Identifier {
while let BoundKind::Expansion { bound_expanded, .. } = &core_arg.kind { binding: IdentifierBinding::Declaration { addr, .. },
core_arg = bound_expanded.as_ref(); ..
} } = &inner_pattern.kind
{
if let BoundKind::Constant(val) = &core_arg.kind { Some(*addr)
sub.add_value(*addr, val.clone()); } else {
} else if let BoundKind::Lambda { upvalues, .. } = &core_arg.kind None
&& upvalues.is_empty() };
{
sub.add_ast_substitution(*addr, core_arg.clone()); if let Some(addr) = addr {
} else if let BoundKind::Get { if let Some(arg) = args.get(*offset)
addr: Address::Global(_), && !body_usage.is_assigned(&addr)
.. {
} = &core_arg.kind let mut core_arg = arg.as_ref();
{ while let NodeKind::Expansion { expanded, .. } = &core_arg.kind {
sub.add_ast_substitution(*addr, core_arg.clone()); core_arg = expanded.as_ref();
} }
}
if let NodeKind::Constant(val) = &core_arg.kind {
if let Address::Local(slot) = addr { sub.add_value(addr, val.clone());
sub.map_slot(*slot); } else if let NodeKind::Lambda { info: lambda_info, .. } = &core_arg.kind
} && lambda_info.upvalues.is_empty()
*offset += 1; {
} sub.add_ast_substitution(addr, core_arg.clone());
BoundKind::Tuple { elements } => { } else if let NodeKind::Identifier {
for el in elements { binding: IdentifierBinding::Reference(Address::Global(_)),
self.map_params_to_args(el, args, offset, sub, body_usage); ..
} } = &core_arg.kind
} {
_ => {} sub.add_ast_substitution(addr, core_arg.clone());
} }
} }
pub fn collect_parameter_slots_set(&self, node: &AnalyzedNode, slots: &mut HashSet<VirtualId>) { if let Address::Local(slot) = addr {
match &node.kind { sub.map_slot(slot);
BoundKind::Define { }
addr: Address::Local(slot), }
.. *offset += 1;
} => { }
slots.insert(*slot); NodeKind::Identifier {
} binding: IdentifierBinding::Declaration { addr, .. },
BoundKind::Tuple { elements } => { ..
for el in elements { } => {
self.collect_parameter_slots_set(el, slots); 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);
}
}
_ => {}
}
}
}
+237 -210
View File
@@ -1,210 +1,237 @@
use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, Node, UpvalueIdx, VirtualId}; use crate::ast::compiler::bound_nodes::{
use crate::ast::types::Value; Address, AnalyzedNode, AssignBinding, IdentifierBinding,
use std::collections::{HashMap, HashSet}; LambdaBinding, Node, NodeKind, UpvalueIdx, VirtualId,
use std::rc::Rc; };
use crate::ast::types::Value;
#[derive(Default)] use std::collections::{HashMap, HashSet};
pub struct SubstitutionMap { use std::rc::Rc;
pub values: HashMap<Address<VirtualId>, Value>,
pub ast_substitutions: HashMap<Address<VirtualId>, Rc<AnalyzedNode>>, #[derive(Default)]
pub slot_mapping: HashMap<VirtualId, VirtualId>, pub struct SubstitutionMap {
pub assigned: HashSet<Address<VirtualId>>, pub values: HashMap<Address<VirtualId>, Value>,
pub next_slot: u32, pub ast_substitutions: HashMap<Address<VirtualId>, Rc<AnalyzedNode>>,
pub used: HashSet<Address<VirtualId>>, pub slot_mapping: HashMap<VirtualId, VirtualId>,
pub captured_slots: HashSet<VirtualId>, pub assigned: HashSet<Address<VirtualId>>,
} pub next_slot: u32,
pub used: HashSet<Address<VirtualId>>,
impl SubstitutionMap { pub captured_slots: HashSet<VirtualId>,
pub fn new() -> Self { }
Self::default()
} impl SubstitutionMap {
pub fn new() -> Self {
/// Creates a new SubstitutionMap for an inner scope (like an inlined Lambda). Self::default()
/// 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 { /// Creates a new SubstitutionMap for an inner scope (like an inlined Lambda).
let mut inner = Self::new(); /// Safely inherits only Global substitutions, because Local and Upvalue
for (k, v) in &self.values { /// addresses are relative to the specific function frame and would overlap.
if matches!(k, Address::Global(_)) { pub fn new_inner(&self) -> Self {
inner.values.insert(*k, v.clone()); let mut inner = Self::new();
} for (k, v) in &self.values {
} if matches!(k, Address::Global(_)) {
for (k, v) in &self.ast_substitutions { inner.values.insert(*k, v.clone());
if matches!(k, Address::Global(_)) { }
inner.ast_substitutions.insert(*k, v.clone()); }
} for (k, v) in &self.ast_substitutions {
} if matches!(k, Address::Global(_)) {
inner inner.ast_substitutions.insert(*k, v.clone());
} }
}
pub fn new_for_inlining(&self) -> Self { inner
let mut inner = self.new_inner(); }
inner.next_slot = self.next_slot;
inner pub fn new_for_inlining(&self) -> Self {
} let mut inner = self.new_inner();
inner.next_slot = self.next_slot;
pub fn add_ast_substitution(&mut self, addr: Address<VirtualId>, node: AnalyzedNode) { inner
self.ast_substitutions.insert(addr, Rc::new(node)); }
}
pub fn add_ast_substitution(&mut self, addr: Address<VirtualId>, node: AnalyzedNode) {
pub fn map_slot(&mut self, old_slot: VirtualId) -> VirtualId { self.ast_substitutions.insert(addr, Rc::new(node));
if let Some(&new_slot) = self.slot_mapping.get(&old_slot) { }
return new_slot;
} pub fn map_slot(&mut self, old_slot: VirtualId) -> VirtualId {
let new_slot = VirtualId(self.next_slot); if let Some(&new_slot) = self.slot_mapping.get(&old_slot) {
self.slot_mapping.insert(old_slot, new_slot); return new_slot;
self.next_slot += 1; }
new_slot let new_slot = VirtualId(self.next_slot);
} self.slot_mapping.insert(old_slot, new_slot);
self.next_slot += 1;
pub fn map_address(&mut self, addr: Address<VirtualId>) -> Address<VirtualId> { new_slot
match addr { }
Address::Local(slot) => Address::Local(self.map_slot(slot)),
other => other, 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 add_value(&mut self, addr: Address<VirtualId>, val: Value) {
pub fn get_value(&self, addr: &Address<VirtualId>) -> Option<&Value> { self.values.insert(addr, val);
self.values.get(addr) }
}
pub fn get_value(&self, addr: &Address<VirtualId>) -> Option<&Value> {
pub fn remove_value(&mut self, addr: &Address<VirtualId>) { self.values.get(addr)
self.values.remove(addr); }
}
pub fn remove_value(&mut self, addr: &Address<VirtualId>) {
fn reindex_addr(&self, addr: Address<VirtualId>, mapping: &[Option<u32>]) -> Address<VirtualId> { self.values.remove(addr);
if let Address::Upvalue(idx) = addr }
&& let Some(res) = mapping.get(idx.0 as usize)
&& let Some(new_idx) = res fn reindex_addr(&self, addr: Address<VirtualId>, mapping: &[Option<u32>]) -> Address<VirtualId> {
{ if let Address::Upvalue(idx) = addr
Address::Upvalue(UpvalueIdx(*new_idx)) && let Some(res) = mapping.get(idx.0 as usize)
} else { && let Some(new_idx) = res
addr {
} 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 } => ( pub fn reindex_upvalues(&self, node_rc: Rc<AnalyzedNode>, mapping: &[Option<u32>]) -> Rc<AnalyzedNode> {
BoundKind::Get { let node = &*node_rc;
addr: self.reindex_addr(*addr, mapping), let (new_kind, metrics) = match &node.kind {
name: name.clone(), NodeKind::Identifier { symbol, binding } => {
}, let new_binding = match binding {
node.ty.clone(), IdentifierBinding::Reference(addr) => {
), IdentifierBinding::Reference(self.reindex_addr(*addr, mapping))
BoundKind::Lambda { }
params, IdentifierBinding::Declaration { addr, kind } => {
upvalues, IdentifierBinding::Declaration {
body, addr: self.reindex_addr(*addr, mapping),
positional_count, kind: *kind,
} => { }
let mut next_upvalues = Vec::new(); }
for addr in upvalues { };
next_upvalues.push(self.reindex_addr(*addr, mapping)); (
} NodeKind::Identifier {
( symbol: symbol.clone(),
BoundKind::Lambda { binding: new_binding,
params: params.clone(), },
upvalues: next_upvalues, node.ty.clone(),
body: body.clone(), )
positional_count: *positional_count, }
}, NodeKind::Lambda {
node.ty.clone(), params,
) body,
} info: lambda_info,
BoundKind::If { } => {
cond, let mut next_upvalues = Vec::new();
then_br, for addr in &lambda_info.upvalues {
else_br, next_upvalues.push(self.reindex_addr(*addr, mapping));
} => { }
let cond = self.reindex_upvalues(cond.clone(), mapping); (
let then_br = self.reindex_upvalues(then_br.clone(), mapping); NodeKind::Lambda {
let else_br = else_br.as_ref().map(|e| self.reindex_upvalues(e.clone(), mapping)); params: params.clone(),
( body: body.clone(),
BoundKind::If { info: LambdaBinding {
cond, upvalues: next_upvalues,
then_br, positional_count: lambda_info.positional_count,
else_br, },
}, },
node.ty.clone(), node.ty.clone(),
) )
} }
BoundKind::Block { exprs } => { NodeKind::If {
let exprs = exprs cond,
.iter() then_br,
.map(|e| self.reindex_upvalues(e.clone(), mapping)) else_br,
.collect(); } => {
(BoundKind::Block { exprs }, node.ty.clone()) let cond = self.reindex_upvalues(cond.clone(), mapping);
} let then_br = self.reindex_upvalues(then_br.clone(), mapping);
BoundKind::Call { callee, args } => { let else_br = else_br.as_ref().map(|e| self.reindex_upvalues(e.clone(), mapping));
let callee = self.reindex_upvalues(callee.clone(), mapping); (
let args = self.reindex_upvalues(args.clone(), mapping); NodeKind::If {
(BoundKind::Call { callee, args }, node.ty.clone()) cond,
} then_br,
BoundKind::Define { else_br,
name, },
addr, node.ty.clone(),
kind, )
value, }
captured_by, NodeKind::Block { exprs } => {
} => { let exprs = exprs
let value = self.reindex_upvalues(value.clone(), mapping); .iter()
( .map(|e| self.reindex_upvalues(e.clone(), mapping))
BoundKind::Define { .collect();
name: name.clone(), (NodeKind::Block { exprs }, node.ty.clone())
addr: *addr, }
kind: *kind, NodeKind::Call { callee, args } => {
value, let callee = self.reindex_upvalues(callee.clone(), mapping);
captured_by: captured_by.clone(), let args = self.reindex_upvalues(args.clone(), mapping);
}, (NodeKind::Call { callee, args }, node.ty.clone())
node.ty.clone(), }
) NodeKind::Def {
} pattern,
BoundKind::Set { addr, value } => { value,
let value = self.reindex_upvalues(value.clone(), mapping); info,
( } => {
BoundKind::Set { let pattern = self.reindex_upvalues(pattern.clone(), mapping);
addr: self.reindex_addr(*addr, mapping), let value = self.reindex_upvalues(value.clone(), mapping);
value, (
}, NodeKind::Def {
node.ty.clone(), pattern,
) value,
} info: info.clone(),
BoundKind::Tuple { elements } => { },
let elements = elements node.ty.clone(),
.iter() )
.map(|e| self.reindex_upvalues(e.clone(), mapping)) }
.collect(); NodeKind::Assign {
(BoundKind::Tuple { elements }, node.ty.clone()) target,
} value,
BoundKind::Record { layout, values } => { info: assign_info,
let values = values } => {
.iter() let target = self.reindex_upvalues(target.clone(), mapping);
.map(|v| self.reindex_upvalues(v.clone(), mapping)) let value = self.reindex_upvalues(value.clone(), mapping);
.collect(); let new_info = if let Some(addr) = assign_info.addr {
(BoundKind::Record { layout: layout.clone(), values }, node.ty.clone()) AssignBinding {
} addr: Some(self.reindex_addr(addr, mapping)),
BoundKind::Expansion { original_call, bound_expanded } => { }
let bound_expanded = self.reindex_upvalues(bound_expanded.clone(), mapping); } else {
( assign_info.clone()
BoundKind::Expansion { };
original_call: original_call.clone(), (
bound_expanded, NodeKind::Assign {
}, target,
node.ty.clone(), value,
) info: new_info,
} },
k => (k.clone(), node.ty.clone()), node.ty.clone(),
}; )
Rc::new(Node { }
identity: node.identity.clone(), NodeKind::Tuple { elements } => {
kind: new_kind, let elements = elements
ty: metrics, .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
View File
@@ -1,182 +1,219 @@
use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, GlobalIdx, VirtualId}; use crate::ast::compiler::bound_nodes::{
use crate::ast::types::{Identity, Value}; Address, AnalyzedNode, GlobalIdx, IdentifierBinding,
use crate::ast::vm::Closure; NodeKind, VirtualId,
use std::collections::HashSet; };
use crate::ast::types::{Identity, Value};
// --- PathTracker --- use crate::ast::vm::Closure;
#[derive(Default)] use std::collections::HashSet;
pub struct PathTracker {
pub inlining_depth: usize, // --- PathTracker ---
pub inlining_stack: HashSet<GlobalIdx>, #[derive(Default)]
pub identity_stack: HashSet<Identity>, pub struct PathTracker {
} pub inlining_depth: usize,
pub inlining_stack: HashSet<GlobalIdx>,
impl PathTracker { pub identity_stack: HashSet<Identity>,
pub fn new() -> Self { }
Self::default()
} impl PathTracker {
pub fn new() -> Self {
pub fn enter_lambda(&mut self, identity: &Identity) -> bool { Self::default()
if self.identity_stack.contains(identity) { }
return false;
} pub fn enter_lambda(&mut self, identity: &Identity) -> bool {
self.identity_stack.insert(identity.clone()); if self.identity_stack.contains(identity) {
true return false;
} }
self.identity_stack.insert(identity.clone());
pub fn exit_lambda(&mut self, identity: &Identity) { true
self.identity_stack.remove(identity); }
}
} pub fn exit_lambda(&mut self, identity: &Identity) {
self.identity_stack.remove(identity);
// --- UsageInfo --- }
#[derive(Default)] }
pub struct UsageInfo {
pub used: HashSet<Address<VirtualId>>, // --- UsageInfo ---
pub assigned: HashSet<Address<VirtualId>>, #[derive(Default)]
pub used_identities: HashSet<Identity>, pub struct UsageInfo {
} pub used: HashSet<Address<VirtualId>>,
pub assigned: HashSet<Address<VirtualId>>,
impl UsageInfo { pub used_identities: HashSet<Identity>,
pub fn is_assigned(&self, addr: &Address<VirtualId>) -> bool { }
self.assigned.contains(addr)
} impl UsageInfo {
pub fn is_assigned(&self, addr: &Address<VirtualId>) -> bool {
pub fn is_used(&self, addr: &Address<VirtualId>) -> bool { self.assigned.contains(addr)
self.used.contains(addr) }
}
pub fn is_used(&self, addr: &Address<VirtualId>) -> bool {
pub fn collect(&mut self, node: &AnalyzedNode) { self.used.contains(addr)
match &node.kind { }
BoundKind::Destructure { pattern, value } => {
self.collect_pattern(pattern); pub fn collect(&mut self, node: &AnalyzedNode) {
self.collect(value); match &node.kind {
} NodeKind::Def { pattern, value, .. } => {
BoundKind::Constant(v) => { self.collect_pattern(pattern);
if let Value::Object(obj) = v self.collect(value);
&& let Some(closure) = obj.as_any().downcast_ref::<Closure>() }
{ NodeKind::Constant(v) => {
self.used_identities if let Value::Object(obj) = v
.insert(closure.function_node.identity.clone()); && let Some(closure) = obj.as_any().downcast_ref::<Closure>()
self.collect(&closure.function_node); {
} self.used_identities
} .insert(closure.function_node.identity.clone());
BoundKind::Get { addr, .. } => { self.collect(&closure.function_node);
self.used.insert(*addr); }
} }
BoundKind::Set { addr, value } => { NodeKind::Identifier { binding, .. } => {
self.assigned.insert(*addr); let addr = match binding {
self.collect(value); IdentifierBinding::Reference(addr) => *addr,
} IdentifierBinding::Declaration { addr, .. } => *addr,
BoundKind::GetField { rec, .. } => { };
self.collect(rec); self.used.insert(addr);
} }
BoundKind::Lambda { NodeKind::Assign { target, value, info, .. } => {
params, if let Some(addr) = info.addr {
body, self.assigned.insert(addr);
upvalues, } else {
.. // Destructuring assign: collect assigned addresses from target pattern
} => { self.collect_assigned_from_target(target);
self.used_identities.insert(node.identity.clone()); }
self.collect(value);
let mut inner_info = UsageInfo::default(); }
inner_info.collect(params); NodeKind::GetField { rec, .. } => {
inner_info.collect(body); self.collect(rec);
}
// Propagate globals and identities NodeKind::Lambda {
for addr in &inner_info.used { params,
if let Address::Global(_) = addr { body,
self.used.insert(*addr); info: lambda_info,
} } => {
} self.used_identities.insert(node.identity.clone());
for addr in &inner_info.assigned {
if let Address::Global(_) = addr { let mut inner_info = UsageInfo::default();
self.assigned.insert(*addr); inner_info.collect(params);
} inner_info.collect(body);
}
self.used_identities.extend(inner_info.used_identities); // Propagate globals and identities
for addr in &inner_info.used {
// Map used upvalues to parent scope if let Address::Global(_) = addr {
for addr in &inner_info.used { self.used.insert(*addr);
if let Address::Upvalue(idx) = addr }
&& let Some(parent_addr) = upvalues.get(idx.0 as usize) }
{ for addr in &inner_info.assigned {
self.used.insert(*parent_addr); if let Address::Global(_) = addr {
} self.assigned.insert(*addr);
} }
// Map assigned upvalues to parent scope }
for addr in &inner_info.assigned { self.used_identities.extend(inner_info.used_identities);
if let Address::Upvalue(idx) = addr
&& let Some(parent_addr) = upvalues.get(idx.0 as usize) // Map used upvalues to parent scope
{ for addr in &inner_info.used {
self.assigned.insert(*parent_addr); if let Address::Upvalue(idx) = addr
} && let Some(parent_addr) = lambda_info.upvalues.get(idx.0 as usize)
} {
} self.used.insert(*parent_addr);
BoundKind::Block { exprs } => { }
for e in exprs { }
self.collect(e); // Map assigned upvalues to parent scope
} for addr in &inner_info.assigned {
} if let Address::Upvalue(idx) = addr
BoundKind::If { && let Some(parent_addr) = lambda_info.upvalues.get(idx.0 as usize)
cond, {
then_br, self.assigned.insert(*parent_addr);
else_br, }
} => { }
self.collect(cond); }
self.collect(then_br); NodeKind::Block { exprs } => {
if let Some(e) = else_br { for e in exprs {
self.collect(e); self.collect(e);
} }
} }
BoundKind::Call { callee, args } => { NodeKind::If {
self.collect(callee); cond,
self.collect(args); then_br,
} else_br,
BoundKind::Tuple { elements } => { } => {
for e in elements { self.collect(cond);
self.collect(e); self.collect(then_br);
} if let Some(e) = else_br {
} self.collect(e);
BoundKind::Record { values, .. } => { }
for v in values { }
self.collect(v); NodeKind::Call { callee, args } => {
} self.collect(callee);
} self.collect(args);
BoundKind::Define { value, .. } => { }
self.collect(value); NodeKind::Tuple { elements } => {
} for e in elements {
BoundKind::Expansion { bound_expanded, .. } => { self.collect(e);
self.collect(bound_expanded); }
} }
BoundKind::Again { args } => { NodeKind::Record { fields, .. } => {
self.collect(args); for (_, v) in fields {
} self.collect(v);
BoundKind::Pipe { inputs, lambda, .. } => { }
for input in inputs { }
self.collect(input); NodeKind::Expansion { expanded, .. } => {
} self.collect(expanded);
self.collect(lambda); }
} NodeKind::Again { args } => {
BoundKind::Nop self.collect(args);
| BoundKind::FieldAccessor(_) }
| BoundKind::Extension(_) NodeKind::Pipe { inputs, lambda, .. } => {
| BoundKind::Error => {} for input in inputs {
} self.collect(input);
} }
self.collect(lambda);
pub fn collect_pattern(&mut self, node: &AnalyzedNode) { }
match &node.kind { NodeKind::Nop
BoundKind::Define { .. } => {} | NodeKind::FieldAccessor(_)
BoundKind::Set { addr, .. } => { | NodeKind::Extension(_)
self.assigned.insert(*addr); | NodeKind::Error => {}
} // Syntax-only variants that should not appear in AnalyzedPhase
BoundKind::Tuple { elements } => { NodeKind::MacroDecl { .. }
for el in elements { | NodeKind::Template(_)
self.collect_pattern(el); | 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
View File
@@ -1,276 +1,276 @@
use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, AnalyzedPhase, BoundKind, Node, NodeMetrics, VirtualId}; use crate::ast::compiler::bound_nodes::{
use crate::ast::types::{Purity, Signature, StaticType, Value}; Address, AnalyzedNode, AnalyzedPhase, IdentifierBinding, Node, NodeKind, NodeMetrics, VirtualId,
use std::cell::RefCell; };
use std::collections::HashMap; use crate::ast::types::{Purity, Signature, StaticType, Value};
use std::rc::Rc; use std::cell::RefCell;
use std::collections::HashMap;
#[derive(Debug, Clone, PartialEq, Eq, Hash)] use std::rc::Rc;
pub struct MonoCacheKey {
pub address: Address<VirtualId>, #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub arg_types: Vec<StaticType>, 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 type CompileFunc = Rc<dyn Fn(Rc<Node<AnalyzedPhase>>, &[StaticType]) -> Result<(Value, StaticType), String>>;
pub trait FunctionRegistry { pub type RtlLookupFunc = Rc<dyn Fn(&str, &[StaticType]) -> Option<(Value, StaticType)>>;
fn resolve(&self, addr: Address<VirtualId>) -> Option<Rc<Node<AnalyzedPhase>>>;
fn resolve_analyzed(&self, _addr: Address<VirtualId>) -> Option<Rc<AnalyzedNode>> { pub trait FunctionRegistry {
None 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 type MonoCache = HashMap<MonoCacheKey, (Value, StaticType)>;
pub cache: Rc<RefCell<MonoCache>>,
registry: Option<Rc<dyn FunctionRegistry>>, pub struct Specializer {
compiler: Option<CompileFunc>, pub cache: Rc<RefCell<MonoCache>>,
rtl_lookup: Option<RtlLookupFunc>, registry: Option<Rc<dyn FunctionRegistry>>,
} compiler: Option<CompileFunc>,
rtl_lookup: Option<RtlLookupFunc>,
impl Specializer { }
pub fn new(
registry: Option<Rc<dyn FunctionRegistry>>, impl Specializer {
compiler: Option<CompileFunc>, pub fn new(
rtl_lookup: Option<RtlLookupFunc>, registry: Option<Rc<dyn FunctionRegistry>>,
cache: Option<Rc<RefCell<MonoCache>>>, compiler: Option<CompileFunc>,
) -> Self { rtl_lookup: Option<RtlLookupFunc>,
Self { cache: Option<Rc<RefCell<MonoCache>>>,
cache: cache.unwrap_or_else(|| Rc::new(RefCell::new(HashMap::new()))), ) -> Self {
registry, Self {
compiler, cache: cache.unwrap_or_else(|| Rc::new(RefCell::new(HashMap::new()))),
rtl_lookup, registry,
} compiler,
} rtl_lookup,
}
pub fn specialize(&self, node: AnalyzedNode) -> AnalyzedNode { }
self.visit_node(node)
} 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 } => { fn visit_node(&self, node: AnalyzedNode) -> AnalyzedNode {
let (new_callee, new_args, _ret_ty) = let (new_kind, metrics) = match node.kind {
self.specialize_call_logic(callee, args, node.ty.original.ty.clone()); NodeKind::Call { callee, args } => {
let (new_callee, new_args, _ret_ty) =
let new_metrics = node.ty.clone(); self.specialize_call_logic(callee, args, node.ty.original.ty.clone());
(
BoundKind::Call { let new_metrics = node.ty.clone();
callee: Rc::new(new_callee), (
args: Rc::new(new_args), NodeKind::Call {
}, callee: Rc::new(new_callee),
new_metrics, args: Rc::new(new_args),
) },
} new_metrics,
)
BoundKind::If { }
cond,
then_br, NodeKind::If {
else_br, cond,
} => { then_br,
let cond = Rc::new(self.visit_node(cond.as_ref().clone())); else_br,
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()))); let cond = Rc::new(self.visit_node(cond.as_ref().clone()));
( let then_br = Rc::new(self.visit_node(then_br.as_ref().clone()));
BoundKind::If { let else_br = else_br.map(|e| Rc::new(self.visit_node(e.as_ref().clone())));
cond, (
then_br, NodeKind::If {
else_br, cond,
}, then_br,
node.ty.clone(), 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()) NodeKind::Block { exprs } => {
} let exprs = exprs.into_iter().map(|e| Rc::new(self.visit_node(e.as_ref().clone()))).collect();
BoundKind::Lambda { (NodeKind::Block { exprs }, node.ty.clone())
params, }
upvalues, NodeKind::Lambda {
body, params,
positional_count, body,
} => { info,
let params = Rc::new(self.visit_node(params.as_ref().clone())); } => {
let body = Rc::new(self.visit_node(body.as_ref().clone())); 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, NodeKind::Lambda {
upvalues, params,
body, body,
positional_count, info,
}, },
node.ty.clone(), node.ty.clone(),
) )
} }
BoundKind::Define { NodeKind::Def {
name, pattern,
addr, value,
kind, info,
value, } => {
captured_by, let value = Rc::new(self.visit_node(value.as_ref().clone()));
} => { (
let value = Rc::new(self.visit_node(value.as_ref().clone())); NodeKind::Def {
( pattern,
BoundKind::Define { value,
name: name.clone(), info,
addr, },
kind, node.ty.clone(),
value, )
captured_by: captured_by.clone(), }
}, NodeKind::Assign { target, value, info } => {
node.ty.clone(), let value = Rc::new(self.visit_node(value.as_ref().clone()));
) (NodeKind::Assign { target, value, info }, node.ty.clone())
} }
BoundKind::Set { addr, value } => { NodeKind::Tuple { elements } => {
let value = Rc::new(self.visit_node(value.as_ref().clone())); let elements = elements.into_iter().map(|e| Rc::new(self.visit_node(e.as_ref().clone()))).collect();
(BoundKind::Set { addr, value }, node.ty.clone()) (NodeKind::Tuple { elements }, node.ty.clone())
} }
BoundKind::Tuple { elements } => { NodeKind::Record { fields, layout } => {
let elements = elements.into_iter().map(|e| Rc::new(self.visit_node(e.as_ref().clone()))).collect(); let fields = fields.into_iter().map(|(k, v)| (k, Rc::new(self.visit_node(v.as_ref().clone())))).collect();
(BoundKind::Tuple { elements }, node.ty.clone()) (NodeKind::Record { fields, layout }, node.ty.clone())
} }
BoundKind::Record { layout, values } => { NodeKind::Expansion {
let values = values.into_iter().map(|v| Rc::new(self.visit_node(v.as_ref().clone()))).collect(); original_call,
(BoundKind::Record { layout, values }, node.ty.clone()) expanded,
} } => {
BoundKind::Expansion { let expanded = Rc::new(self.visit_node(expanded.as_ref().clone()));
original_call, (
bound_expanded, NodeKind::Expansion {
} => { original_call,
let bound_expanded = Rc::new(self.visit_node(bound_expanded.as_ref().clone())); expanded,
( },
BoundKind::Expansion { node.ty.clone(),
original_call, )
bound_expanded, }
}, k => (k, node.ty.clone()),
node.ty.clone(), };
)
} Node {
k => (k, node.ty.clone()), identity: node.identity,
}; kind: new_kind,
ty: metrics,
Node { }
identity: node.identity, }
kind: new_kind,
ty: metrics, fn specialize_call_logic(
} &self,
} callee: Rc<AnalyzedNode>,
args: Rc<AnalyzedNode>,
fn specialize_call_logic( original_ty: StaticType,
&self, ) -> (AnalyzedNode, AnalyzedNode, StaticType) {
callee: Rc<AnalyzedNode>, let new_callee = self.visit_node(callee.as_ref().clone());
args: Rc<AnalyzedNode>, let new_args = self.visit_node(args.as_ref().clone());
original_ty: StaticType,
) -> (AnalyzedNode, AnalyzedNode, StaticType) { let address = if let NodeKind::Identifier {
let new_callee = self.visit_node(callee.as_ref().clone()); binding: IdentifierBinding::Reference(addr),
let new_args = self.visit_node(args.as_ref().clone()); ..
} = &new_callee.kind
let address = if let BoundKind::Get { addr, .. } = &new_callee.kind { {
*addr *addr
} else { } else {
return (new_callee, new_args, original_ty); return (new_callee, new_args, original_ty);
}; };
let arg_types: Vec<StaticType> = let arg_types: Vec<StaticType> =
if let StaticType::Tuple(elements) = &new_args.ty.original.ty { if let StaticType::Tuple(elements) = &new_args.ty.original.ty {
elements.clone() elements.clone()
} else { } else {
vec![new_args.ty.original.ty.clone()] vec![new_args.ty.original.ty.clone()]
}; };
if arg_types.iter().any(|t| matches!(t, StaticType::Any)) { if arg_types.iter().any(|t| matches!(t, StaticType::Any)) {
return (new_callee, new_args, original_ty); return (new_callee, new_args, original_ty);
} }
let key = MonoCacheKey { let key = MonoCacheKey {
address, address,
arg_types: arg_types.clone(), arg_types: arg_types.clone(),
}; };
if let Some((val, ret_ty)) = self.cache.borrow().get(&key) { if let Some((val, ret_ty)) = self.cache.borrow().get(&key) {
let specialized_callee = self.make_constant_node( let specialized_callee = self.make_constant_node(
val.clone(), val.clone(),
StaticType::Function(Box::new(Signature { StaticType::Function(Box::new(Signature {
params: StaticType::Tuple(arg_types), params: StaticType::Tuple(arg_types),
ret: ret_ty.clone(), ret: ret_ty.clone(),
})), })),
&new_callee, &new_callee,
); );
return (specialized_callee, new_args, ret_ty.clone()); return (specialized_callee, new_args, ret_ty.clone());
} }
if let Some(rtl_lookup) = &self.rtl_lookup if let Some(rtl_lookup) = &self.rtl_lookup
&& let BoundKind::Get { name, .. } = &new_callee.kind && let NodeKind::Identifier { symbol, .. } = &new_callee.kind
&& let Some((val, ret_ty)) = rtl_lookup(&name.name, &arg_types) && let Some((val, ret_ty)) = rtl_lookup(&symbol.name, &arg_types)
{ {
self.cache self.cache
.borrow_mut() .borrow_mut()
.insert(key.clone(), (val.clone(), ret_ty.clone())); .insert(key.clone(), (val.clone(), ret_ty.clone()));
let specialized_callee = self.make_constant_node( let specialized_callee = self.make_constant_node(
val.clone(), val.clone(),
StaticType::Function(Box::new(Signature { StaticType::Function(Box::new(Signature {
params: StaticType::Tuple(arg_types), params: StaticType::Tuple(arg_types),
ret: ret_ty.clone(), ret: ret_ty.clone(),
})), })),
&new_callee, &new_callee,
); );
return (specialized_callee, new_args, ret_ty); return (specialized_callee, new_args, ret_ty);
} }
if let Some(registry) = &self.registry if let Some(registry) = &self.registry
&& let Some(func_node) = registry.resolve_analyzed(address) && let Some(func_node) = registry.resolve_analyzed(address)
&& func_node.ty.is_recursive && func_node.ty.is_recursive
{ {
return (new_callee, new_args, original_ty); return (new_callee, new_args, original_ty);
} }
if let Some(compiler) = &self.compiler if let Some(compiler) = &self.compiler
&& let Some(func_node) = self.registry.as_ref().and_then(|r| r.resolve(address)) && 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) && let Ok((compiled_val, ret_ty)) = compiler(func_node, &arg_types)
{ {
self.cache self.cache
.borrow_mut() .borrow_mut()
.insert(key, (compiled_val.clone(), ret_ty.clone())); .insert(key, (compiled_val.clone(), ret_ty.clone()));
// Only replace the callee if the compiled value is actually a function/object. // 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. // 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. // 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 { if let Value::Object(_) | Value::Function(_) = &compiled_val {
let specialized_callee = self.make_constant_node( let specialized_callee = self.make_constant_node(
compiled_val, compiled_val,
StaticType::Function(Box::new(Signature { StaticType::Function(Box::new(Signature {
params: StaticType::Tuple(arg_types), params: StaticType::Tuple(arg_types),
ret: ret_ty.clone(), ret: ret_ty.clone(),
})), })),
&new_callee, &new_callee,
); );
return (specialized_callee, new_args, ret_ty); return (specialized_callee, new_args, ret_ty);
} }
} }
(new_callee, new_args, original_ty) (new_callee, new_args, original_ty)
} }
fn make_constant_node( fn make_constant_node(
&self, &self,
val: Value, val: Value,
ty: StaticType, ty: StaticType,
template: &AnalyzedNode, template: &AnalyzedNode,
) -> AnalyzedNode { ) -> AnalyzedNode {
let typed_original = Rc::new(Node { let typed_original = Rc::new(Node {
identity: template.identity.clone(), identity: template.identity.clone(),
kind: BoundKind::Constant(val.clone()), kind: NodeKind::Constant(val.clone()),
ty: ty.clone(), ty: ty.clone(),
}); });
Node { Node {
identity: template.identity.clone(), identity: template.identity.clone(),
kind: BoundKind::Constant(val), kind: NodeKind::Constant(val),
ty: NodeMetrics { ty: NodeMetrics {
original: typed_original, original: typed_original,
purity: Purity::Pure, purity: Purity::Pure,
is_recursive: false, is_recursive: false,
}, },
} }
} }
} }
File diff suppressed because it is too large Load Diff
+19 -18
View File
@@ -10,8 +10,8 @@ use std::path::{Path, PathBuf};
use std::rc::Rc; use std::rc::Rc;
use crate::ast::compiler::bound_nodes::{ use crate::ast::compiler::bound_nodes::{
Address, AnalyzedNode, BoundKind, ExecNode, GlobalAnalyzedRegistry, GlobalFunctionRegistry, GlobalIdx, Address, AnalyzedNode, ExecNode, GlobalAnalyzedRegistry, GlobalFunctionRegistry, GlobalIdx,
Node, VirtualId, LambdaBinding, Node, NodeKind, VirtualId,
}; };
use crate::ast::compiler::dumper::Dumper; use crate::ast::compiler::dumper::Dumper;
use crate::ast::compiler::lambda_collector::LambdaCollector; use crate::ast::compiler::lambda_collector::LambdaCollector;
@@ -117,7 +117,7 @@ impl MacroEvaluator for RuntimeMacroEvaluator {
node: &SyntaxNode, node: &SyntaxNode,
bindings: &HashMap<Rc<str>, SyntaxNode>, bindings: &HashMap<Rc<str>, SyntaxNode>,
) -> Result<Value, String> { ) -> 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) && let Some(arg_node) = bindings.get(&sym.name)
{ {
return Ok(Value::Object(Rc::new(arg_node.clone()) as Rc<dyn Object>)); return Ok(Value::Object(Rc::new(arg_node.clone()) as Rc<dyn Object>));
@@ -390,8 +390,8 @@ impl Environment {
fn discover_globals(&self, node: &SyntaxNode) { fn discover_globals(&self, node: &SyntaxNode) {
match &node.kind { match &node.kind {
SyntaxKind::Def { target, .. } => { SyntaxKind::Def { pattern, .. } => {
if let SyntaxKind::Identifier(sym) = &target.kind { if let SyntaxKind::Identifier { symbol: sym, .. } = &pattern.kind {
let mut root_scopes = self.root_scopes.borrow_mut(); let mut root_scopes = self.root_scopes.borrow_mut();
let last_idx = root_scopes.len() - 1; let last_idx = root_scopes.len() - 1;
let current_scope = &mut root_scopes[last_idx]; let current_scope = &mut root_scopes[last_idx];
@@ -417,9 +417,9 @@ impl Environment {
fn extract_names(node: &SyntaxNode) -> Vec<Rc<str>> { fn extract_names(node: &SyntaxNode) -> Vec<Rc<str>> {
match &node.kind { match &node.kind {
SyntaxKind::Identifier(sym) => vec![sym.name.clone()], SyntaxKind::Identifier { symbol: sym, .. } => vec![sym.name.clone()],
SyntaxKind::Tuple { elements } => { SyntaxKind::Tuple { elements } => {
elements.iter().flat_map(extract_names).collect() elements.iter().flat_map(|e| extract_names(e)).collect()
} }
_ => vec![], _ => vec![],
} }
@@ -476,20 +476,22 @@ impl Environment {
LambdaCollector::collect(&bound_ast, &mut self.function_registry.borrow_mut()); LambdaCollector::collect(&bound_ast, &mut self.function_registry.borrow_mut());
let checker = TypeChecker::new(self.root_types.clone()); 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 bound_ast
} else { } else {
Node { Node {
identity: bound_ast.identity.clone(), identity: bound_ast.identity.clone(),
kind: BoundKind::Lambda { kind: NodeKind::Lambda {
params: std::rc::Rc::new(Node { params: std::rc::Rc::new(Node {
identity: bound_ast.identity.clone(), identity: bound_ast.identity.clone(),
kind: BoundKind::Tuple { elements: vec![] }, kind: NodeKind::Tuple { elements: vec![] },
ty: (), ty: (),
}), }),
upvalues: vec![],
body: std::rc::Rc::new(bound_ast), body: std::rc::Rc::new(bound_ast),
positional_count: Some(0), info: LambdaBinding {
upvalues: vec![],
positional_count: Some(0),
},
}, },
ty: (), ty: (),
} }
@@ -650,20 +652,19 @@ impl Environment {
pub fn instantiate(&self, node: ExecNode) -> Rc<crate::ast::types::NativeFunction> { pub fn instantiate(&self, node: ExecNode) -> Rc<crate::ast::types::NativeFunction> {
let root_values = self.root_values.clone(); let root_values = self.root_values.clone();
if let BoundKind::Lambda { if let NodeKind::Lambda {
params, params,
upvalues,
body, body,
positional_count, info,
} = &node.kind } = &node.kind
&& upvalues.is_empty() && info.upvalues.is_empty()
{ {
let closure = Rc::new(crate::ast::vm::Closure::new( let closure = Rc::new(crate::ast::vm::Closure::new(
params.clone(), params.clone(),
body.ty.original.clone(), body.ty.original.clone(),
body.clone(), body.clone(),
Vec::new(), Vec::new(),
*positional_count, info.positional_count,
node.ty.stack_size, node.ty.stack_size,
)); ));
let closure_obj: Rc<dyn crate::ast::types::Object> = closure; let closure_obj: Rc<dyn crate::ast::types::Object> = closure;
+65 -134
View File
@@ -1,134 +1,65 @@
use crate::ast::types::{Identity, Object, Value}; use crate::ast::compiler::bound_nodes::{Node, NodeKind, SyntaxPhase};
use std::any::Any; use crate::ast::types::{Identity, Object};
use std::fmt::Debug; use std::any::Any;
use std::rc::Rc; use std::fmt::Debug;
use std::rc::Rc;
/// A name with an optional context for macro hygiene.
#[derive(Debug, Clone, PartialEq, Eq, Hash)] /// A name with an optional context for macro hygiene.
pub struct Symbol { #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub name: Rc<str>, pub struct Symbol {
/// Points to the identity of the Expansion node if this symbol pub name: Rc<str>,
/// was created/referenced inside a macro expansion. /// Points to the identity of the Expansion node if this symbol
pub context: Option<Identity>, /// was created/referenced inside a macro expansion.
} pub context: Option<Identity>,
}
impl From<Rc<str>> for Symbol {
fn from(name: Rc<str>) -> Self { impl From<Rc<str>> for Symbol {
Self { fn from(name: Rc<str>) -> Self {
name, Self {
context: None, name,
} context: None,
} }
} }
}
impl From<&str> for Symbol {
fn from(name: &str) -> Self { impl From<&str> for Symbol {
Self { fn from(name: &str) -> Self {
name: Rc::from(name), Self {
context: None, name: Rc::from(name),
} context: None,
} }
} }
}
/// A parser AST Node wrapper to preserve identity and metadata
#[derive(Debug, Clone, PartialEq)] /// Type alias: the parser AST is now `Node<SyntaxPhase>`.
pub struct SyntaxNode { pub type SyntaxNode = Node<SyntaxPhase>;
pub identity: Identity,
pub kind: SyntaxKind, /// Type alias: the parser AST kind is now `NodeKind<SyntaxPhase>`.
} pub type SyntaxKind = NodeKind<SyntaxPhase>;
impl Object for SyntaxNode { impl Object for Node<SyntaxPhase> {
fn type_name(&self) -> &'static str { fn type_name(&self) -> &'static str {
"ast-node" "ast-node"
} }
fn as_any(&self) -> &dyn Any { fn as_any(&self) -> &dyn Any {
self self
} }
} }
/// The base for custom node types (extensions) /// The base for custom node types (extensions)
pub trait CustomNode: Debug { pub trait CustomNode: Debug {
fn display_name(&self) -> &'static str; fn display_name(&self) -> &'static str;
fn clone_box(&self) -> Box<dyn CustomNode>; fn clone_box(&self) -> Box<dyn CustomNode>;
} }
impl Clone for Box<dyn CustomNode> { impl Clone for Box<dyn CustomNode> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
self.clone_box() self.clone_box()
} }
} }
#[derive(Debug, Clone, PartialEq)] impl PartialEq for Box<dyn CustomNode> {
pub enum SyntaxKind { fn eq(&self, _other: &Self) -> bool {
Nop, false
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
}
}
+544 -522
View File
File diff suppressed because it is too large Load Diff
+101 -88
View File
@@ -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 crate::ast::types::{Object, Value};
use std::any::Any; use std::any::Any;
use std::cell::RefCell; use std::cell::RefCell;
@@ -105,7 +105,7 @@ impl VMObserver for TracingObserver {
self.indent = self.indent.saturating_sub(1); self.indent = self.indent.saturating_sub(1);
let pad = self.pad(); let pad = self.pad();
match &node.kind { match &node.kind {
BoundKind::Define { .. } | BoundKind::Set { .. } => { NodeKind::Def { .. } | NodeKind::Assign { .. } => {
let s_pad = format!("{}| ", pad); let s_pad = format!("{}| ", pad);
self.logs.push(format!("{}--- Scope Status ---", s_pad)); self.logs.push(format!("{}--- Scope Status ---", s_pad));
self.logs.push(format!( self.logs.push(format!(
@@ -303,58 +303,76 @@ impl VM {
#[inline(always)] #[inline(always)]
fn eval_core<O: VMObserver>(&mut self, obs: &mut O, node: &ExecNode) -> Result<Value, String> { fn eval_core<O: VMObserver>(&mut self, obs: &mut O, node: &ExecNode) -> Result<Value, String> {
match &node.kind { match &node.kind {
BoundKind::Nop => Ok(Value::Void), NodeKind::Nop => Ok(Value::Void),
BoundKind::Constant(v) => Ok(v.clone()), NodeKind::Constant(v) => Ok(v.clone()),
BoundKind::Define { NodeKind::Def { pattern, value, info } => {
addr,
value,
captured_by,
..
} => {
let val = self.eval_internal(obs, value)?; let val = self.eval_internal(obs, value)?;
match &pattern.kind {
let mut needs_cell_wrap = false; NodeKind::Identifier { binding, .. } => {
if !captured_by.is_empty() let addr = match binding {
&& let Address::Local(slot) = addr IdentifierBinding::Declaration { addr, .. } | IdentifierBinding::Reference(addr) => *addr,
{ };
let frame = self.frames.last().unwrap();
let abs_index = frame.stack_base + (slot.0 as usize); let mut needs_cell_wrap = false;
if !info.captured_by.is_empty()
// Robustness Fix: If the slot is already a Cell (due to forward capture && let Address::Local(slot) = addr
// in a recursive scenario or complex pre-allocation), don't wrap it again. {
// This prevents Cell(Cell(Value)) nesting which causes type errors. let frame = self.frames.last().unwrap();
if abs_index >= self.stack.len() || !matches!(self.stack[abs_index], Value::Cell(_)) { let abs_index = frame.stack_base + (slot.0 as usize);
needs_cell_wrap = true;
// 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)?;
}
} }
} }
// Def always evaluates to the unwrapped value for immediate use.
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.
Ok(val) Ok(val)
} }
BoundKind::Destructure { pattern, value } => { NodeKind::Assign { target, value, info } => {
let val = self.eval_internal(obs, value)?; let val = self.eval_internal(obs, value)?;
let mut offset = 0; if let Some(addr) = info.addr {
self.set_value(addr, val.clone())?;
// 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)?;
} else { } 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) 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)?; let rec_val = self.eval_internal(obs, rec)?;
match rec_val { match rec_val {
@@ -395,12 +413,7 @@ impl VM {
} }
} }
BoundKind::Set { addr, value } => { NodeKind::If {
let val = self.eval_internal(obs, value)?;
self.set_value(*addr, val.clone())?;
Ok(val)
}
BoundKind::If {
cond, cond,
then_br, then_br,
else_br, else_br,
@@ -414,10 +427,9 @@ impl VM {
Ok(Value::Void) Ok(Value::Void)
} }
} }
BoundKind::Pipe { NodeKind::Pipe {
inputs, inputs,
lambda, lambda,
out_type,
} => { } => {
use crate::ast::rtl::streams::StreamNode; use crate::ast::rtl::streams::StreamNode;
@@ -464,25 +476,24 @@ impl VM {
// Delegate to the RTL Factory for specialized buffer instantiation // Delegate to the RTL Factory for specialized buffer instantiation
let node = 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)) Ok(Value::Object(node))
} }
BoundKind::Block { exprs } => { NodeKind::Block { exprs } => {
let mut last = Value::Void; let mut last = Value::Void;
for e in exprs { for e in exprs {
last = self.eval_internal(obs, e)?; last = self.eval_internal(obs, e)?;
} }
Ok(last) Ok(last)
} }
BoundKind::Lambda { NodeKind::Lambda {
params, params,
upvalues,
body, body,
positional_count, info,
} => { } => {
let mut captured = Vec::with_capacity(upvalues.len()); let mut captured = Vec::with_capacity(info.upvalues.len());
for addr in upvalues { for addr in &info.upvalues {
captured.push(self.capture_upvalue(*addr)?); captured.push(self.capture_upvalue(*addr)?);
} }
let stack_size = node.ty.stack_size; let stack_size = node.ty.stack_size;
@@ -491,12 +502,12 @@ impl VM {
body.ty.original.clone(), body.ty.original.clone(),
body.clone(), body.clone(),
captured, captured,
*positional_count, info.positional_count,
stack_size, stack_size,
); );
Ok(Value::Object(Rc::new(closure))) Ok(Value::Object(Rc::new(closure)))
} }
BoundKind::Call { callee, args } => { NodeKind::Call { callee, args } => {
let func_val = self.eval_internal(obs, callee)?; let func_val = self.eval_internal(obs, callee)?;
let base = self.stack.len(); let base = self.stack.len();
@@ -654,7 +665,7 @@ impl VM {
} else { } else {
let args_for_unpack = self.stack[base..].to_vec(); let args_for_unpack = self.stack[base..].to_vec();
self.stack.truncate(base); self.stack.truncate(base);
if let BoundKind::Tuple { elements } = if let NodeKind::Tuple { elements } =
&closure.parameter_node.kind &closure.parameter_node.kind
{ {
let mut offset = 0; let mut offset = 0;
@@ -755,7 +766,7 @@ impl VM {
} }
} }
} }
BoundKind::Again { args } => { NodeKind::Again { args } => {
let base = self.stack.len(); let base = self.stack.len();
if let Err(e) = self.eval_args_to_stack(obs, args) { if let Err(e) = self.eval_args_to_stack(obs, args) {
self.stack.truncate(base); self.stack.truncate(base);
@@ -774,16 +785,16 @@ impl VM {
Err("'again' called outside of a closure".to_string()) Err("'again' called outside of a closure".to_string())
} }
} }
BoundKind::Tuple { elements } => { NodeKind::Tuple { elements } => {
let mut vals = Vec::with_capacity(elements.len()); let mut vals = Vec::with_capacity(elements.len());
for e in elements { for e in elements {
vals.push(self.eval_internal(obs, e)?); vals.push(self.eval_internal(obs, e)?);
} }
Ok(Value::make_tuple(vals)) Ok(Value::make_tuple(vals))
} }
BoundKind::Record { layout, values } => { NodeKind::Record { fields, layout } => {
let mut evaluated_values = Vec::with_capacity(values.len()); let mut evaluated_values = Vec::with_capacity(fields.len());
for v in values { for (_, v) in fields {
evaluated_values.push(self.eval_internal(obs, v)?); evaluated_values.push(self.eval_internal(obs, v)?);
} }
Ok(Value::Record( Ok(Value::Record(
@@ -791,11 +802,11 @@ impl VM {
std::rc::Rc::new(evaluated_values), std::rc::Rc::new(evaluated_values),
)) ))
} }
BoundKind::Expansion { bound_expanded, .. } => { NodeKind::Expansion { expanded, .. } => {
let mut curr = bound_expanded; let mut curr = expanded;
if !O::ACTIVE { if !O::ACTIVE {
while let BoundKind::Expansion { while let NodeKind::Expansion {
bound_expanded: next, expanded: next,
.. ..
} = &curr.kind } = &curr.kind
{ {
@@ -804,11 +815,15 @@ impl VM {
} }
self.eval_internal(obs, curr) self.eval_internal(obs, curr)
} }
BoundKind::Extension(ext) => Err(format!( NodeKind::Extension(ext) => Err(format!(
"Execution of extension '{}' not implemented yet", "Execution of extension '{}' not implemented yet",
ext.display_name() 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, args: &ExecNode,
) -> Result<(), String> { ) -> Result<(), String> {
match &args.kind { match &args.kind {
BoundKind::Tuple { elements } => { NodeKind::Tuple { elements } => {
for e in elements { for e in elements {
let mut curr = e.as_ref(); let mut curr = e.as_ref();
if !O::ACTIVE { if !O::ACTIVE {
while let BoundKind::Expansion { while let NodeKind::Expansion {
bound_expanded: next, expanded: next,
.. ..
} = &curr.kind } = &curr.kind
{ {
@@ -832,7 +847,7 @@ impl VM {
} }
match &curr.kind { 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)?; let val = self.eval_internal(obs, curr)?;
self.stack.push(val); self.stack.push(val);
@@ -841,7 +856,7 @@ impl VM {
} }
Ok(()) Ok(())
} }
BoundKind::Constant(v) => { NodeKind::Constant(v) => {
if let Some(slice) = v.as_slice() { if let Some(slice) = v.as_slice() {
self.stack.extend_from_slice(slice); self.stack.extend_from_slice(slice);
} else { } else {
@@ -849,11 +864,11 @@ impl VM {
} }
Ok(()) Ok(())
} }
BoundKind::Expansion { bound_expanded, .. } => { NodeKind::Expansion { expanded, .. } => {
let mut curr = bound_expanded; let mut curr = expanded;
if !O::ACTIVE { if !O::ACTIVE {
while let BoundKind::Expansion { while let NodeKind::Expansion {
bound_expanded: next, expanded: next,
.. ..
} = &curr.kind } = &curr.kind
{ {
@@ -1008,17 +1023,15 @@ impl VM {
offset: &mut usize, offset: &mut usize,
) -> Result<(), String> { ) -> Result<(), String> {
match &pattern.kind { 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); let val = values.get(*offset).cloned().unwrap_or(Value::Void);
*offset += 1; *offset += 1;
self.set_value(*addr, val) self.set_value(addr, val)
} }
BoundKind::Set { addr, .. } => { NodeKind::Tuple { elements } => {
let val = values.get(*offset).cloned().unwrap_or(Value::Void);
*offset += 1;
self.set_value(*addr, val)
}
BoundKind::Tuple { elements } => {
if let Some(sub_values) = values.get(*offset).and_then(|v| v.as_slice()) { if let Some(sub_values) = values.get(*offset).and_then(|v| v.as_slice()) {
*offset += 1; *offset += 1;
let mut sub_offset = 0; let mut sub_offset = 0;