Refactor bound node types for clarity
Introduce generic `CompilerPhase` trait to unify different stages of the bound AST. Rename `LocalSlot` to `VirtualId` and introduce `StackOffset` for clearer distinction between compile-time and run-time addressing. Update type aliases and implementations to reflect these changes.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
use crate::ast::compiler::bound_nodes::{
|
||||
Address, AnalyzedNode, BoundKind, GlobalIdx, NodeMetrics, TypedNode,
|
||||
Address, AnalyzedNode, BoundKind, GlobalIdx, Node, NodeMetrics, TypedNode, TypedPhase,
|
||||
};
|
||||
use crate::ast::types::Purity;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
@@ -318,7 +318,7 @@ impl<'a> Analyzer<'a> {
|
||||
BoundKind::Error => (BoundKind::Error, Purity::Impure),
|
||||
};
|
||||
|
||||
crate::ast::compiler::bound_nodes::Node {
|
||||
Node {
|
||||
identity: node.identity.clone(),
|
||||
kind: new_kind,
|
||||
ty: NodeMetrics {
|
||||
@@ -334,7 +334,7 @@ trait NodeExt {
|
||||
fn for_each_child<F: FnMut(&TypedNode)>(&self, f: F);
|
||||
}
|
||||
|
||||
impl NodeExt for BoundKind<crate::ast::types::StaticType> {
|
||||
impl NodeExt for BoundKind<TypedPhase> {
|
||||
fn for_each_child<F: FnMut(&TypedNode)>(&self, mut f: F) {
|
||||
match self {
|
||||
BoundKind::If {
|
||||
|
||||
+16
-16
@@ -1,5 +1,5 @@
|
||||
use crate::ast::compiler::bound_nodes::{
|
||||
Address, BoundKind, DeclarationKind, GlobalIdx, LocalSlot, Node, UpvalueIdx,
|
||||
Address, BoundKind, BoundPhase, DeclarationKind, GlobalIdx, Node, UpvalueIdx, VirtualId,
|
||||
};
|
||||
use crate::ast::diagnostics::Diagnostics;
|
||||
use crate::ast::nodes::{Symbol, SyntaxKind, SyntaxNode};
|
||||
@@ -9,7 +9,7 @@ use std::rc::Rc;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LocalInfo {
|
||||
pub addr: Address,
|
||||
pub addr: Address<VirtualId>,
|
||||
pub identity: Identity,
|
||||
pub _ty: StaticType,
|
||||
pub purity: Purity,
|
||||
@@ -38,7 +38,7 @@ struct FunctionCompiler {
|
||||
identity: Identity,
|
||||
scopes: Vec<CompilerScope>,
|
||||
slot_count: u32,
|
||||
upvalues: Vec<Address>,
|
||||
upvalues: Vec<Address<VirtualId>>,
|
||||
}
|
||||
|
||||
impl FunctionCompiler {
|
||||
@@ -65,7 +65,7 @@ impl FunctionCompiler {
|
||||
}
|
||||
}
|
||||
|
||||
fn define_variable(&mut self, name: &Symbol, identity: Identity) -> Result<Address, String> {
|
||||
fn define_variable(&mut self, name: &Symbol, identity: Identity) -> Result<Address<VirtualId>, String> {
|
||||
let current_scope = self.scopes.last_mut().unwrap();
|
||||
if current_scope.locals.contains_key(name) {
|
||||
return Err(format!(
|
||||
@@ -74,7 +74,7 @@ impl FunctionCompiler {
|
||||
));
|
||||
}
|
||||
|
||||
let slot = LocalSlot(self.slot_count);
|
||||
let slot = VirtualId(self.slot_count);
|
||||
current_scope.locals.insert(
|
||||
name.clone(),
|
||||
LocalInfo {
|
||||
@@ -97,7 +97,7 @@ impl FunctionCompiler {
|
||||
None
|
||||
}
|
||||
|
||||
fn add_upvalue(&mut self, addr: Address) -> UpvalueIdx {
|
||||
fn add_upvalue(&mut self, addr: Address<VirtualId>) -> UpvalueIdx {
|
||||
if let Some(idx) = self.upvalues.iter().position(|&a| a == addr) {
|
||||
return UpvalueIdx(idx as u32);
|
||||
}
|
||||
@@ -114,7 +114,7 @@ pub enum ExprContext {
|
||||
}
|
||||
|
||||
pub type BindingResult = (
|
||||
Node,
|
||||
Node<BoundPhase>,
|
||||
HashMap<Identity, Vec<Identity>>,
|
||||
Vec<CompilerScope>,
|
||||
u32,
|
||||
@@ -179,7 +179,7 @@ impl Binder {
|
||||
identity: Identity,
|
||||
_kind: crate::ast::compiler::bound_nodes::DeclarationKind,
|
||||
diag: &mut Diagnostics,
|
||||
) -> Option<Address> {
|
||||
) -> Option<Address<VirtualId>> {
|
||||
let current_fn_idx = self.functions.len() - 1;
|
||||
|
||||
if current_fn_idx == 0 {
|
||||
@@ -211,7 +211,7 @@ impl Binder {
|
||||
node: &SyntaxNode,
|
||||
ctx: ExprContext,
|
||||
diag: &mut Diagnostics,
|
||||
) -> Node {
|
||||
) -> Node<BoundPhase> {
|
||||
match &node.kind {
|
||||
SyntaxKind::Nop => self.make_node(node.identity.clone(), BoundKind::Nop),
|
||||
SyntaxKind::Constant(v) => {
|
||||
@@ -361,7 +361,7 @@ impl Binder {
|
||||
let body_bound = self.bind(body.as_ref(), ExprContext::Expression, diag);
|
||||
let compiled_fn = self.functions.pop().unwrap();
|
||||
|
||||
fn count_params(node: &Node) -> Option<u32> {
|
||||
fn count_params(node: &Node<BoundPhase>) -> Option<u32> {
|
||||
match &node.kind {
|
||||
BoundKind::Define {
|
||||
kind: DeclarationKind::Parameter,
|
||||
@@ -520,9 +520,9 @@ impl Binder {
|
||||
);
|
||||
self.make_node(node.identity.clone(), BoundKind::Error)
|
||||
}
|
||||
SyntaxKind::Error => crate::ast::compiler::bound_nodes::Node {
|
||||
SyntaxKind::Error => Node {
|
||||
identity: node.identity.clone(),
|
||||
kind: crate::ast::compiler::bound_nodes::BoundKind::Error,
|
||||
kind: BoundKind::Error,
|
||||
ty: (),
|
||||
},
|
||||
}
|
||||
@@ -533,7 +533,7 @@ impl Binder {
|
||||
sym: &Symbol,
|
||||
diag: &mut Diagnostics,
|
||||
identity: &Identity,
|
||||
) -> Option<Address> {
|
||||
) -> Option<Address<VirtualId>> {
|
||||
let current_fn_idx = self.functions.len() - 1;
|
||||
|
||||
// 1. Try local in current function
|
||||
@@ -603,7 +603,7 @@ impl Binder {
|
||||
node: &SyntaxNode,
|
||||
kind: DeclarationKind,
|
||||
diag: &mut Diagnostics,
|
||||
) -> Node {
|
||||
) -> Node<BoundPhase> {
|
||||
match &node.kind {
|
||||
SyntaxKind::Identifier(sym) => {
|
||||
if let Some(addr) = self.declare_variable(sym, node.identity.clone(), kind, diag) {
|
||||
@@ -647,7 +647,7 @@ impl Binder {
|
||||
&mut self,
|
||||
node: &SyntaxNode,
|
||||
diag: &mut Diagnostics,
|
||||
) -> Node {
|
||||
) -> Node<BoundPhase> {
|
||||
match &node.kind {
|
||||
SyntaxKind::Identifier(sym) => {
|
||||
if let Some(addr) = self.resolve_variable(sym, diag, &node.identity) {
|
||||
@@ -684,7 +684,7 @@ impl Binder {
|
||||
}
|
||||
}
|
||||
|
||||
fn make_node(&self, identity: Identity, kind: BoundKind<()>) -> Node {
|
||||
fn make_node(&self, identity: Identity, kind: BoundKind<BoundPhase>) -> Node<BoundPhase> {
|
||||
Node {
|
||||
identity,
|
||||
kind,
|
||||
|
||||
+199
-199
@@ -3,11 +3,20 @@ use crate::ast::types::{Identity, StaticType, Value};
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct LocalSlot(pub u32);
|
||||
pub struct VirtualId(pub u32);
|
||||
|
||||
impl std::fmt::Display for LocalSlot {
|
||||
impl std::fmt::Display for VirtualId {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "L{}", self.0)
|
||||
write!(f, "V{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct StackOffset(pub u32);
|
||||
|
||||
impl std::fmt::Display for StackOffset {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "S{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,46 +38,84 @@ impl std::fmt::Display for GlobalIdx {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum Address {
|
||||
Local(LocalSlot), // Stack-Slot index (relative to frame base)
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub enum Address<L> {
|
||||
Local(L), // Local address (VirtualId or StackOffset)
|
||||
Upvalue(UpvalueIdx), // Index in the closure's upvalue array
|
||||
Global(GlobalIdx), // Index in the global environment vector
|
||||
}
|
||||
|
||||
impl<L: Clone> Clone for Address<L> {
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
Address::Local(l) => Address::Local(l.clone()),
|
||||
Address::Upvalue(u) => Address::Upvalue(*u),
|
||||
Address::Global(g) => Address::Global(*g),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<L: Copy> Copy for Address<L> {}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum DeclarationKind {
|
||||
Variable,
|
||||
Parameter,
|
||||
}
|
||||
|
||||
/// Trait for DSL-specific AST extensions (like Pipe, Series-Access, etc.)
|
||||
pub trait BoundExtension<T>: std::fmt::Debug {
|
||||
fn clone_box(&self) -> Box<dyn BoundExtension<T>>;
|
||||
fn display_name(&self) -> String;
|
||||
pub trait CompilerPhase: std::fmt::Debug + 'static {
|
||||
type Metadata: std::fmt::Debug + Clone + PartialEq;
|
||||
type LocalAddress: std::fmt::Debug + Clone + Copy + PartialEq + Eq + std::hash::Hash;
|
||||
}
|
||||
|
||||
impl<T> Clone for Box<dyn BoundExtension<T>> {
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct BoundPhase;
|
||||
impl CompilerPhase for BoundPhase {
|
||||
type Metadata = ();
|
||||
type LocalAddress = VirtualId;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct TypedPhase;
|
||||
impl CompilerPhase for TypedPhase {
|
||||
type Metadata = StaticType;
|
||||
type LocalAddress = VirtualId;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct AnalyzedPhase;
|
||||
impl CompilerPhase for AnalyzedPhase {
|
||||
type Metadata = NodeMetrics;
|
||||
type LocalAddress = VirtualId;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct RuntimePhase;
|
||||
impl CompilerPhase for RuntimePhase {
|
||||
type Metadata = RuntimeMetadata;
|
||||
type LocalAddress = StackOffset;
|
||||
}
|
||||
|
||||
/// A bound AST node, decorated with phase-specific information P.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct Node<P: CompilerPhase = BoundPhase> {
|
||||
pub identity: Identity,
|
||||
pub kind: BoundKind<P>,
|
||||
pub ty: P::Metadata,
|
||||
}
|
||||
|
||||
impl<P: CompilerPhase> Clone for Node<P> {
|
||||
fn clone(&self) -> Self {
|
||||
self.clone_box()
|
||||
Self {
|
||||
identity: self.identity.clone(),
|
||||
kind: self.kind.clone(),
|
||||
ty: self.ty.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A bound AST node, decorated with type or metric information T.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Node<T = ()> {
|
||||
pub identity: Identity,
|
||||
pub kind: BoundKind<T>,
|
||||
pub ty: T,
|
||||
}
|
||||
pub type TypedNode = Node<TypedPhase>;
|
||||
|
||||
/// Type alias for a node that has been fully type-checked.
|
||||
pub type TypedNode = Node<StaticType>;
|
||||
|
||||
/// Type alias for the global function registry.
|
||||
pub type GlobalFunctionRegistry = std::collections::HashMap<GlobalIdx, Rc<Node>>;
|
||||
|
||||
/// Metrics collected during the analysis phase.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct NodeMetrics {
|
||||
pub original: Rc<TypedNode>,
|
||||
@@ -76,114 +123,144 @@ pub struct NodeMetrics {
|
||||
pub is_recursive: bool,
|
||||
}
|
||||
|
||||
/// Type alias for a node that has been analyzed.
|
||||
pub type AnalyzedNode = Node<NodeMetrics>;
|
||||
pub type AnalyzedNode = Node<AnalyzedPhase>;
|
||||
|
||||
/// Type alias for the global analyzed function registry.
|
||||
#[derive(Clone)]
|
||||
pub struct RuntimeMetadata {
|
||||
pub ty: StaticType,
|
||||
pub is_tail: bool,
|
||||
pub original: Rc<AnalyzedNode>,
|
||||
pub stack_size: u32,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for RuntimeMetadata {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("Metadata")
|
||||
.field("ty", &self.ty)
|
||||
.field("is_tail", &self.is_tail)
|
||||
.field("stack_size", &self.stack_size)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for RuntimeMetadata {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.ty == other.ty && self.is_tail == other.is_tail && Rc::ptr_eq(&self.original, &other.original) && self.stack_size == other.stack_size
|
||||
}
|
||||
}
|
||||
|
||||
pub type ExecNode = Node<RuntimePhase>;
|
||||
|
||||
pub type GlobalFunctionRegistry = std::collections::HashMap<GlobalIdx, Rc<Node<BoundPhase>>>;
|
||||
pub type GlobalAnalyzedRegistry = std::collections::HashMap<GlobalIdx, Rc<AnalyzedNode>>;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum BoundKind<T = ()> {
|
||||
pub trait BoundExtension<P: CompilerPhase>: std::fmt::Debug {
|
||||
fn clone_box(&self) -> Box<dyn BoundExtension<P>>;
|
||||
fn display_name(&self) -> String;
|
||||
}
|
||||
|
||||
impl<P: CompilerPhase> Clone for Box<dyn BoundExtension<P>> {
|
||||
fn clone(&self) -> Self {
|
||||
self.clone_box()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum BoundKind<P: CompilerPhase = BoundPhase> {
|
||||
Nop,
|
||||
Constant(Value),
|
||||
|
||||
// Variable Access (Resolved)
|
||||
Get {
|
||||
addr: Address,
|
||||
addr: Address<P::LocalAddress>,
|
||||
name: Symbol,
|
||||
},
|
||||
|
||||
// Variable Update (Assignment)
|
||||
Set {
|
||||
addr: Address,
|
||||
value: Rc<Node<T>>,
|
||||
addr: Address<P::LocalAddress>,
|
||||
value: Rc<Node<P>>,
|
||||
},
|
||||
|
||||
// Variable Declaration (Unified for Local/Global/Parameter)
|
||||
Define {
|
||||
name: Symbol,
|
||||
addr: Address,
|
||||
addr: Address<P::LocalAddress>,
|
||||
kind: DeclarationKind,
|
||||
value: Rc<Node<T>>,
|
||||
value: Rc<Node<P>>,
|
||||
captured_by: Vec<Identity>,
|
||||
},
|
||||
|
||||
/// A first-class field accessor (e.g. .name)
|
||||
FieldAccessor(crate::ast::types::Keyword),
|
||||
|
||||
/// Specialized field access (O(1) via RecordLayout)
|
||||
GetField {
|
||||
rec: Rc<Node<T>>,
|
||||
rec: Rc<Node<P>>,
|
||||
field: crate::ast::types::Keyword,
|
||||
},
|
||||
|
||||
If {
|
||||
cond: Rc<Node<T>>,
|
||||
then_br: Rc<Node<T>>,
|
||||
else_br: Option<Rc<Node<T>>>,
|
||||
cond: Rc<Node<P>>,
|
||||
then_br: Rc<Node<P>>,
|
||||
else_br: Option<Rc<Node<P>>>,
|
||||
},
|
||||
|
||||
/// A destructuring operation (can be a definition or an assignment depending on the pattern)
|
||||
Destructure {
|
||||
pattern: Rc<Node<T>>,
|
||||
value: Rc<Node<T>>,
|
||||
pattern: Rc<Node<P>>,
|
||||
value: Rc<Node<P>>,
|
||||
},
|
||||
|
||||
Lambda {
|
||||
params: Rc<Node<T>>,
|
||||
// The list of variables captured from enclosing scopes
|
||||
upvalues: Vec<Address>,
|
||||
body: Rc<Node<T>>,
|
||||
/// Static optimization: number of positional parameters if the pattern is flat.
|
||||
params: Rc<Node<P>>,
|
||||
upvalues: Vec<Address<P::LocalAddress>>,
|
||||
body: Rc<Node<P>>,
|
||||
positional_count: Option<u32>,
|
||||
},
|
||||
|
||||
Call {
|
||||
callee: Rc<Node<T>>,
|
||||
args: Rc<Node<T>>,
|
||||
callee: Rc<Node<P>>,
|
||||
args: Rc<Node<P>>,
|
||||
},
|
||||
|
||||
Again {
|
||||
args: Rc<Node<T>>,
|
||||
args: Rc<Node<P>>,
|
||||
},
|
||||
|
||||
Pipe {
|
||||
inputs: Vec<Rc<Node<T>>>,
|
||||
lambda: Rc<Node<T>>,
|
||||
inputs: Vec<Rc<Node<P>>>,
|
||||
lambda: Rc<Node<P>>,
|
||||
out_type: crate::ast::types::StaticType,
|
||||
},
|
||||
Block {
|
||||
exprs: Vec<Rc<Node<T>>>,
|
||||
exprs: Vec<Rc<Node<P>>>,
|
||||
},
|
||||
|
||||
Tuple {
|
||||
elements: Vec<Rc<Node<T>>>,
|
||||
elements: Vec<Rc<Node<P>>>,
|
||||
},
|
||||
|
||||
Record {
|
||||
layout: std::sync::Arc<crate::ast::types::RecordLayout>,
|
||||
values: Vec<Rc<Node<T>>>,
|
||||
values: Vec<Rc<Node<P>>>,
|
||||
},
|
||||
|
||||
/// An expanded macro call, preserving the original call for debugging and UI.
|
||||
Expansion {
|
||||
/// The original call from the syntax AST.
|
||||
original_call: Rc<crate::ast::nodes::SyntaxNode>,
|
||||
/// The result of binding the expanded AST.
|
||||
bound_expanded: Rc<Node<T>>,
|
||||
bound_expanded: Rc<Node<P>>,
|
||||
},
|
||||
|
||||
/// A diagnostic poison node, allowing compilation to continue after an error.
|
||||
Error,
|
||||
|
||||
/// DSL-specific extension slot
|
||||
Extension(Box<dyn BoundExtension<T>>),
|
||||
Extension(Box<dyn BoundExtension<P>>),
|
||||
}
|
||||
|
||||
impl<T> PartialEq for BoundKind<T>
|
||||
where
|
||||
T: PartialEq,
|
||||
{
|
||||
impl<P: CompilerPhase> Clone for BoundKind<P> {
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
BoundKind::Nop => BoundKind::Nop,
|
||||
BoundKind::Constant(v) => BoundKind::Constant(v.clone()),
|
||||
BoundKind::Get { addr, name } => BoundKind::Get { addr: addr.clone(), name: name.clone() },
|
||||
BoundKind::Set { addr, value } => BoundKind::Set { addr: addr.clone(), value: value.clone() },
|
||||
BoundKind::Define { name, addr, kind, value, captured_by } => BoundKind::Define { name: name.clone(), addr: addr.clone(), kind: *kind, value: value.clone(), captured_by: captured_by.clone() },
|
||||
BoundKind::FieldAccessor(k) => BoundKind::FieldAccessor(k.clone()),
|
||||
BoundKind::GetField { rec, field } => BoundKind::GetField { rec: rec.clone(), field: field.clone() },
|
||||
BoundKind::If { cond, then_br, else_br } => BoundKind::If { cond: cond.clone(), then_br: then_br.clone(), else_br: else_br.clone() },
|
||||
BoundKind::Destructure { pattern, value } => BoundKind::Destructure { pattern: pattern.clone(), value: value.clone() },
|
||||
BoundKind::Lambda { params, upvalues, body, positional_count } => BoundKind::Lambda { params: params.clone(), upvalues: upvalues.clone(), body: body.clone(), positional_count: *positional_count },
|
||||
BoundKind::Call { callee, args } => BoundKind::Call { callee: callee.clone(), args: args.clone() },
|
||||
BoundKind::Again { args } => BoundKind::Again { args: args.clone() },
|
||||
BoundKind::Pipe { inputs, lambda, out_type } => BoundKind::Pipe { inputs: inputs.clone(), lambda: lambda.clone(), out_type: out_type.clone() },
|
||||
BoundKind::Block { exprs } => BoundKind::Block { exprs: exprs.clone() },
|
||||
BoundKind::Tuple { elements } => BoundKind::Tuple { elements: elements.clone() },
|
||||
BoundKind::Record { layout, values } => BoundKind::Record { layout: layout.clone(), values: values.clone() },
|
||||
BoundKind::Expansion { original_call, bound_expanded } => BoundKind::Expansion { original_call: original_call.clone(), bound_expanded: bound_expanded.clone() },
|
||||
BoundKind::Error => BoundKind::Error,
|
||||
BoundKind::Extension(ext) => BoundKind::Extension(ext.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: CompilerPhase> PartialEq for BoundKind<P> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
match (self, other) {
|
||||
(BoundKind::Nop, BoundKind::Nop) => true,
|
||||
@@ -191,87 +268,32 @@ where
|
||||
(BoundKind::Get { addr: aa, name: na }, BoundKind::Get { addr: ab, name: nb }) => {
|
||||
aa == ab && na == nb
|
||||
}
|
||||
(
|
||||
BoundKind::Set {
|
||||
addr: aa,
|
||||
value: va,
|
||||
},
|
||||
BoundKind::Set {
|
||||
addr: ab,
|
||||
value: vb,
|
||||
},
|
||||
) => aa == ab && Rc::ptr_eq(va, vb),
|
||||
(
|
||||
BoundKind::Define {
|
||||
name: na,
|
||||
addr: aa,
|
||||
kind: ka,
|
||||
value: va,
|
||||
captured_by: ca,
|
||||
},
|
||||
BoundKind::Define {
|
||||
name: nb,
|
||||
addr: ab,
|
||||
kind: kb,
|
||||
value: vb,
|
||||
captured_by: cb,
|
||||
},
|
||||
) => na == nb && aa == ab && ka == kb && Rc::ptr_eq(va, vb) && ca == cb,
|
||||
(BoundKind::Set { addr: aa, value: va }, BoundKind::Set { addr: ab, value: vb }) => {
|
||||
aa == ab && Rc::ptr_eq(va, vb)
|
||||
}
|
||||
(BoundKind::Define { name: na, addr: aa, kind: ka, value: va, captured_by: ca }, BoundKind::Define { name: nb, addr: ab, kind: kb, value: vb, captured_by: cb }) => {
|
||||
na == nb && aa == ab && ka == kb && Rc::ptr_eq(va, vb) && ca == cb
|
||||
}
|
||||
(BoundKind::FieldAccessor(a), BoundKind::FieldAccessor(b)) => a == b,
|
||||
(
|
||||
BoundKind::GetField { rec: ra, field: fa },
|
||||
BoundKind::GetField { rec: rb, field: fb },
|
||||
) => Rc::ptr_eq(ra, rb) && fa == fb,
|
||||
(
|
||||
BoundKind::If {
|
||||
cond: ca,
|
||||
then_br: ta,
|
||||
else_br: ea,
|
||||
},
|
||||
BoundKind::If {
|
||||
cond: cb,
|
||||
then_br: tb,
|
||||
else_br: eb,
|
||||
},
|
||||
) => Rc::ptr_eq(ca, cb) && Rc::ptr_eq(ta, tb) && match (ea, eb) {
|
||||
(Some(a), Some(b)) => Rc::ptr_eq(a, b),
|
||||
(None, None) => true,
|
||||
_ => false,
|
||||
},
|
||||
(
|
||||
BoundKind::Destructure {
|
||||
pattern: pa,
|
||||
value: va,
|
||||
},
|
||||
BoundKind::Destructure {
|
||||
pattern: pb,
|
||||
value: vb,
|
||||
},
|
||||
) => Rc::ptr_eq(pa, pb) && Rc::ptr_eq(va, vb),
|
||||
(
|
||||
BoundKind::Lambda {
|
||||
params: pa,
|
||||
upvalues: ua,
|
||||
body: ba,
|
||||
positional_count: pca,
|
||||
},
|
||||
BoundKind::Lambda {
|
||||
params: pb,
|
||||
upvalues: ub,
|
||||
body: bb,
|
||||
positional_count: pcb,
|
||||
},
|
||||
) => Rc::ptr_eq(pa, pb) && ua == ub && Rc::ptr_eq(ba, bb) && pca == pcb,
|
||||
(
|
||||
BoundKind::Call {
|
||||
callee: ca,
|
||||
args: aa,
|
||||
},
|
||||
BoundKind::Call {
|
||||
callee: cb,
|
||||
args: ab,
|
||||
},
|
||||
) => Rc::ptr_eq(ca, cb) && Rc::ptr_eq(aa, ab),
|
||||
(BoundKind::GetField { rec: ra, field: fa }, BoundKind::GetField { rec: rb, field: fb }) => {
|
||||
Rc::ptr_eq(ra, rb) && fa == fb
|
||||
}
|
||||
(BoundKind::If { cond: ca, then_br: ta, else_br: ea }, BoundKind::If { cond: cb, then_br: tb, else_br: eb }) => {
|
||||
Rc::ptr_eq(ca, cb) && Rc::ptr_eq(ta, tb) && match (ea, eb) {
|
||||
(Some(a), Some(b)) => Rc::ptr_eq(a, b),
|
||||
(None, None) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
(BoundKind::Destructure { pattern: pa, value: va }, BoundKind::Destructure { pattern: pb, value: vb }) => {
|
||||
Rc::ptr_eq(pa, pb) && Rc::ptr_eq(va, vb)
|
||||
}
|
||||
(BoundKind::Lambda { params: pa, upvalues: ua, body: ba, positional_count: pca }, BoundKind::Lambda { params: pb, upvalues: ub, body: bb, positional_count: pcb }) => {
|
||||
Rc::ptr_eq(pa, pb) && ua == ub && Rc::ptr_eq(ba, bb) && pca == pcb
|
||||
}
|
||||
(BoundKind::Call { callee: ca, args: aa }, BoundKind::Call { callee: cb, args: ab }) => {
|
||||
Rc::ptr_eq(ca, cb) && Rc::ptr_eq(aa, ab)
|
||||
}
|
||||
(BoundKind::Again { args: aa }, BoundKind::Again { args: ab }) => Rc::ptr_eq(aa, ab),
|
||||
(BoundKind::Block { exprs: ea }, BoundKind::Block { exprs: eb }) => {
|
||||
ea.len() == eb.len() && ea.iter().zip(eb.iter()).all(|(a, b)| Rc::ptr_eq(a, b))
|
||||
@@ -279,46 +301,26 @@ where
|
||||
(BoundKind::Tuple { elements: ea }, BoundKind::Tuple { elements: eb }) => {
|
||||
ea.len() == eb.len() && ea.iter().zip(eb.iter()).all(|(a, b)| Rc::ptr_eq(a, b))
|
||||
}
|
||||
(
|
||||
BoundKind::Record {
|
||||
layout: la,
|
||||
values: va,
|
||||
},
|
||||
BoundKind::Record {
|
||||
layout: lb,
|
||||
values: vb,
|
||||
},
|
||||
) => std::sync::Arc::ptr_eq(la, lb) && va.len() == vb.len() && va.iter().zip(vb.iter()).all(|(a, b)| Rc::ptr_eq(a, b)),
|
||||
(
|
||||
BoundKind::Expansion {
|
||||
original_call: ca,
|
||||
bound_expanded: ea,
|
||||
},
|
||||
BoundKind::Expansion {
|
||||
original_call: cb,
|
||||
bound_expanded: eb,
|
||||
},
|
||||
) => Rc::ptr_eq(ca, cb) && Rc::ptr_eq(ea, eb),
|
||||
(BoundKind::Record { layout: la, values: va }, BoundKind::Record { layout: lb, values: vb }) => {
|
||||
std::sync::Arc::ptr_eq(la, lb) && va.len() == vb.len() && va.iter().zip(vb.iter()).all(|(a, b)| Rc::ptr_eq(a, b))
|
||||
}
|
||||
(BoundKind::Expansion { original_call: ca, bound_expanded: ea }, BoundKind::Expansion { original_call: cb, bound_expanded: eb }) => {
|
||||
Rc::ptr_eq(ca, cb) && Rc::ptr_eq(ea, eb)
|
||||
}
|
||||
(BoundKind::Error, BoundKind::Error) => true,
|
||||
(BoundKind::Extension(_), BoundKind::Extension(_)) => false,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A single field in a Record literal (Key-Value pair)
|
||||
pub type RecordField<T> = (Node<T>, Node<T>);
|
||||
|
||||
impl<T> BoundKind<T> {
|
||||
impl<P: CompilerPhase> BoundKind<P> {
|
||||
pub fn display_name(&self) -> String {
|
||||
match self {
|
||||
BoundKind::Nop => "NOP".to_string(),
|
||||
BoundKind::Constant(v) => format!("CONST({})", v),
|
||||
BoundKind::Get { addr, name } => format!("GET({}, {:?})", name.name, addr),
|
||||
BoundKind::Set { addr, .. } => format!("SET({:?})", addr),
|
||||
BoundKind::Define {
|
||||
name, addr, kind, ..
|
||||
} => {
|
||||
BoundKind::Define { name, addr, kind, .. } => {
|
||||
let k_str = match kind {
|
||||
DeclarationKind::Variable => "VAR",
|
||||
DeclarationKind::Parameter => "PARAM",
|
||||
@@ -329,9 +331,7 @@ impl<T> BoundKind<T> {
|
||||
BoundKind::GetField { field, .. } => format!("GET_FIELD(.{})", field.name()),
|
||||
BoundKind::If { .. } => "IF".to_string(),
|
||||
BoundKind::Destructure { .. } => "DESTRUCTURE".to_string(),
|
||||
BoundKind::Lambda {
|
||||
params, upvalues, ..
|
||||
} => {
|
||||
BoundKind::Lambda { params, upvalues, .. } => {
|
||||
let p_str = match ¶ms.kind {
|
||||
BoundKind::Tuple { elements } => format!("p:{}", elements.len()),
|
||||
_ => "p:1".to_string(),
|
||||
|
||||
+78
-103
@@ -1,67 +1,34 @@
|
||||
use crate::ast::compiler::bound_nodes::{BoundKind, Node};
|
||||
use crate::ast::compiler::bound_nodes::{BoundKind, CompilerPhase, Node};
|
||||
use crate::ast::types::Identity;
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct CapturePass;
|
||||
|
||||
impl CapturePass {
|
||||
pub fn apply<T: Clone>(node: Node<T>, capture_map: &HashMap<Identity, Vec<Identity>>) -> Node<T> {
|
||||
pub fn apply<P: CompilerPhase>(node: Node<P>, capture_map: &HashMap<Identity, Vec<Identity>>) -> Node<P> {
|
||||
Self::transform(node, capture_map)
|
||||
}
|
||||
|
||||
fn transform<T: Clone>(mut node: Node<T>, capture_map: &HashMap<Identity, Vec<Identity>>) -> Node<T> {
|
||||
use std::rc::Rc;
|
||||
match node.kind {
|
||||
fn transform<P: CompilerPhase>(mut node: Node<P>, capture_map: &HashMap<Identity, Vec<Identity>>) -> Node<P> {
|
||||
node.kind = match node.kind {
|
||||
BoundKind::Define {
|
||||
name,
|
||||
addr,
|
||||
kind,
|
||||
value,
|
||||
..
|
||||
mut captured_by,
|
||||
} => {
|
||||
let captured_by = capture_map.get(&node.identity).cloned().unwrap_or_default();
|
||||
node.kind = BoundKind::Define {
|
||||
if let Some(capturers) = capture_map.get(&node.identity) {
|
||||
captured_by.extend(capturers.iter().cloned());
|
||||
}
|
||||
BoundKind::Define {
|
||||
name,
|
||||
addr,
|
||||
kind,
|
||||
value: Rc::new(Self::transform(value.as_ref().clone(), capture_map)),
|
||||
captured_by,
|
||||
};
|
||||
}
|
||||
|
||||
BoundKind::If {
|
||||
cond,
|
||||
then_br,
|
||||
else_br,
|
||||
} => {
|
||||
node.kind = BoundKind::If {
|
||||
cond: Rc::new(Self::transform(cond.as_ref().clone(), capture_map)),
|
||||
then_br: Rc::new(Self::transform(then_br.as_ref().clone(), capture_map)),
|
||||
else_br: else_br.map(|e| Rc::new(Self::transform(e.as_ref().clone(), capture_map))),
|
||||
};
|
||||
}
|
||||
|
||||
BoundKind::Set { addr, value } => {
|
||||
node.kind = BoundKind::Set {
|
||||
addr,
|
||||
value: Rc::new(Self::transform(value.as_ref().clone(), capture_map)),
|
||||
};
|
||||
}
|
||||
|
||||
BoundKind::FieldAccessor(_) => {}
|
||||
|
||||
BoundKind::GetField { rec, field } => {
|
||||
node.kind = BoundKind::GetField {
|
||||
rec: Rc::new(Self::transform(rec.as_ref().clone(), capture_map)),
|
||||
field,
|
||||
};
|
||||
}
|
||||
|
||||
BoundKind::Destructure { pattern, value } => {
|
||||
node.kind = BoundKind::Destructure {
|
||||
pattern: Rc::new(Self::transform(pattern.as_ref().clone(), capture_map)),
|
||||
value: Rc::new(Self::transform(value.as_ref().clone(), capture_map)),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
BoundKind::Lambda {
|
||||
@@ -69,27 +36,38 @@ impl CapturePass {
|
||||
upvalues,
|
||||
body,
|
||||
positional_count,
|
||||
} => {
|
||||
node.kind = BoundKind::Lambda {
|
||||
params: Rc::new(Self::transform(params.as_ref().clone(), capture_map)),
|
||||
upvalues,
|
||||
body: Rc::new(Self::transform(body.as_ref().clone(), capture_map)),
|
||||
positional_count,
|
||||
};
|
||||
}
|
||||
} => BoundKind::Lambda {
|
||||
params: Rc::new(Self::transform(params.as_ref().clone(), capture_map)),
|
||||
upvalues,
|
||||
body: Rc::new(Self::transform(body.as_ref().clone(), capture_map)),
|
||||
positional_count,
|
||||
},
|
||||
|
||||
BoundKind::Call { callee, args } => {
|
||||
node.kind = BoundKind::Call {
|
||||
callee: Rc::new(Self::transform(callee.as_ref().clone(), capture_map)),
|
||||
args: Rc::new(Self::transform(args.as_ref().clone(), capture_map)),
|
||||
};
|
||||
}
|
||||
BoundKind::Call { callee, args } => BoundKind::Call {
|
||||
callee: Rc::new(Self::transform(callee.as_ref().clone(), capture_map)),
|
||||
args: Rc::new(Self::transform(args.as_ref().clone(), capture_map)),
|
||||
},
|
||||
|
||||
BoundKind::Again { args } => BoundKind::Again {
|
||||
args: Rc::new(Self::transform(args.as_ref().clone(), capture_map)),
|
||||
},
|
||||
|
||||
BoundKind::If {
|
||||
cond,
|
||||
then_br,
|
||||
else_br,
|
||||
} => BoundKind::If {
|
||||
cond: Rc::new(Self::transform(cond.as_ref().clone(), capture_map)),
|
||||
then_br: Rc::new(Self::transform(then_br.as_ref().clone(), capture_map)),
|
||||
else_br: else_br
|
||||
.map(|e| Rc::new(Self::transform(e.as_ref().clone(), capture_map))),
|
||||
},
|
||||
|
||||
BoundKind::Destructure { pattern, value } => BoundKind::Destructure {
|
||||
pattern: Rc::new(Self::transform(pattern.as_ref().clone(), capture_map)),
|
||||
value: Rc::new(Self::transform(value.as_ref().clone(), capture_map)),
|
||||
},
|
||||
|
||||
BoundKind::Again { args } => {
|
||||
node.kind = BoundKind::Again {
|
||||
args: Rc::new(Self::transform(args.as_ref().clone(), capture_map)),
|
||||
};
|
||||
}
|
||||
BoundKind::Pipe {
|
||||
inputs,
|
||||
lambda,
|
||||
@@ -99,56 +77,53 @@ impl CapturePass {
|
||||
for input in inputs {
|
||||
t_inputs.push(Rc::new(Self::transform(input.as_ref().clone(), capture_map)));
|
||||
}
|
||||
node.kind = BoundKind::Pipe {
|
||||
BoundKind::Pipe {
|
||||
inputs: t_inputs,
|
||||
lambda: Rc::new(Self::transform(lambda.as_ref().clone(), capture_map)),
|
||||
out_type: out_type.clone(),
|
||||
};
|
||||
}
|
||||
BoundKind::Block { exprs } => {
|
||||
node.kind = BoundKind::Block {
|
||||
exprs: exprs
|
||||
.into_iter()
|
||||
.map(|e| Rc::new(Self::transform(e.as_ref().clone(), capture_map)))
|
||||
.collect(),
|
||||
};
|
||||
out_type,
|
||||
}
|
||||
}
|
||||
|
||||
BoundKind::Tuple { elements } => {
|
||||
node.kind = BoundKind::Tuple {
|
||||
elements: elements
|
||||
.into_iter()
|
||||
.map(|e| Rc::new(Self::transform(e.as_ref().clone(), capture_map)))
|
||||
.collect(),
|
||||
};
|
||||
}
|
||||
BoundKind::Block { exprs } => BoundKind::Block {
|
||||
exprs: exprs
|
||||
.into_iter()
|
||||
.map(|e| Rc::new(Self::transform(e.as_ref().clone(), capture_map)))
|
||||
.collect(),
|
||||
},
|
||||
|
||||
BoundKind::Record { layout, values } => {
|
||||
node.kind = BoundKind::Record {
|
||||
layout,
|
||||
values: values
|
||||
.into_iter()
|
||||
.map(|v| Rc::new(Self::transform(v.as_ref().clone(), capture_map)))
|
||||
.collect(),
|
||||
};
|
||||
}
|
||||
BoundKind::Tuple { elements } => BoundKind::Tuple {
|
||||
elements: elements
|
||||
.into_iter()
|
||||
.map(|e| Rc::new(Self::transform(e.as_ref().clone(), capture_map)))
|
||||
.collect(),
|
||||
},
|
||||
|
||||
BoundKind::Record { layout, values } => BoundKind::Record {
|
||||
layout,
|
||||
values: values
|
||||
.into_iter()
|
||||
.map(|v| Rc::new(Self::transform(v.as_ref().clone(), capture_map)))
|
||||
.collect(),
|
||||
},
|
||||
|
||||
BoundKind::Expansion {
|
||||
original_call,
|
||||
bound_expanded,
|
||||
} => {
|
||||
node.kind = BoundKind::Expansion {
|
||||
original_call,
|
||||
bound_expanded: Rc::new(Self::transform(bound_expanded.as_ref().clone(), capture_map)),
|
||||
};
|
||||
}
|
||||
} => BoundKind::Expansion {
|
||||
original_call,
|
||||
bound_expanded: Rc::new(Self::transform(
|
||||
bound_expanded.as_ref().clone(),
|
||||
capture_map,
|
||||
)),
|
||||
},
|
||||
|
||||
BoundKind::Nop
|
||||
| BoundKind::Constant(_)
|
||||
| BoundKind::Get { .. }
|
||||
| BoundKind::Extension(_)
|
||||
| BoundKind::Error => {}
|
||||
}
|
||||
BoundKind::GetField { rec, field } => BoundKind::GetField {
|
||||
rec: Rc::new(Self::transform(rec.as_ref().clone(), capture_map)),
|
||||
field,
|
||||
},
|
||||
|
||||
other => other,
|
||||
};
|
||||
node
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use crate::ast::compiler::bound_nodes::{BoundKind, Node};
|
||||
use std::fmt::Debug;
|
||||
use crate::ast::compiler::bound_nodes::{BoundKind, CompilerPhase, Node};
|
||||
|
||||
/// Human-readable AST dumper for the bound AST.
|
||||
pub struct Dumper {
|
||||
@@ -9,7 +8,7 @@ pub struct Dumper {
|
||||
|
||||
impl Dumper {
|
||||
/// Produces a formatted string representation of the given bound AST node and its children.
|
||||
pub fn dump<T: Debug>(node: &Node<T>) -> String {
|
||||
pub fn dump<P: CompilerPhase>(node: &Node<P>) -> String {
|
||||
let mut dumper = Self {
|
||||
output: String::new(),
|
||||
indent: 0,
|
||||
@@ -24,14 +23,14 @@ impl Dumper {
|
||||
}
|
||||
}
|
||||
|
||||
fn log<T: Debug>(&mut self, label: &str, node: &Node<T>) {
|
||||
fn log<P: CompilerPhase>(&mut self, label: &str, node: &Node<P>) {
|
||||
self.write_indent();
|
||||
self.output.push_str(label);
|
||||
self.output
|
||||
.push_str(&format!(" <Metadata: {:?}>\n", node.ty));
|
||||
}
|
||||
|
||||
fn visit<T: Debug>(&mut self, node: &Node<T>) {
|
||||
fn visit<P: CompilerPhase>(&mut self, node: &Node<P>) {
|
||||
match &node.kind {
|
||||
BoundKind::Nop => self.log("Nop", node),
|
||||
BoundKind::Constant(v) => {
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
use crate::ast::compiler::bound_nodes::{Address, BoundKind, GlobalIdx, Node};
|
||||
use crate::ast::compiler::bound_nodes::{Address, BoundKind, CompilerPhase, GlobalIdx, Node};
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
/// A pass that collects all global function definitions (lambdas) into a registry.
|
||||
/// This allows the Specializer to retrieve the original AST of a function for monomorphization.
|
||||
pub struct LambdaCollector<'a, T> {
|
||||
registry: &'a mut HashMap<GlobalIdx, Rc<Node<T>>>,
|
||||
pub struct LambdaCollector<'a, P: CompilerPhase> {
|
||||
registry: &'a mut HashMap<GlobalIdx, Rc<Node<P>>>,
|
||||
}
|
||||
|
||||
impl<'a, T: Clone> LambdaCollector<'a, T> {
|
||||
impl<'a, P: CompilerPhase> LambdaCollector<'a, P> {
|
||||
/// Performs a full traversal of the AST and populates the provided registry.
|
||||
pub fn collect(node: &Node<T>, registry: &'a mut HashMap<GlobalIdx, Rc<Node<T>>>) {
|
||||
pub fn collect(node: &Node<P>, registry: &'a mut HashMap<GlobalIdx, Rc<Node<P>>>) {
|
||||
let mut collector = Self { registry };
|
||||
collector.visit(node);
|
||||
}
|
||||
|
||||
fn visit(&mut self, node: &Node<T>) {
|
||||
fn visit(&mut self, node: &Node<P>) {
|
||||
match &node.kind {
|
||||
BoundKind::Block { exprs } => {
|
||||
for expr in exprs {
|
||||
|
||||
@@ -1,31 +1,7 @@
|
||||
use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, LocalSlot, Node};
|
||||
use crate::ast::types::StaticType;
|
||||
use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, ExecNode, Node, RuntimeMetadata, StackOffset, VirtualId};
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Debug;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RuntimeMetadata {
|
||||
pub ty: StaticType,
|
||||
pub is_tail: bool,
|
||||
/// The analyzed node, containing metrics and a link to the original TypedNode.
|
||||
pub original: Rc<AnalyzedNode>,
|
||||
pub stack_size: u32,
|
||||
}
|
||||
|
||||
impl Debug for RuntimeMetadata {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("Metadata")
|
||||
.field("ty", &self.ty)
|
||||
.field("is_tail", &self.is_tail)
|
||||
.field("stack_size", &self.stack_size)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// The ExecNode is the AST used by the VM. It carries TCO flags and links to metrics.
|
||||
pub type ExecNode = Node<RuntimeMetadata>;
|
||||
|
||||
struct StackAllocator {
|
||||
mapping: HashMap<u32, u32>,
|
||||
next_slot: u32,
|
||||
@@ -39,19 +15,20 @@ impl StackAllocator {
|
||||
}
|
||||
}
|
||||
|
||||
fn map_slot(&mut self, slot: LocalSlot) -> LocalSlot {
|
||||
fn map_slot(&mut self, slot: VirtualId) -> StackOffset {
|
||||
let entry = self.mapping.entry(slot.0).or_insert_with(|| {
|
||||
let s = self.next_slot;
|
||||
self.next_slot += 1;
|
||||
s
|
||||
});
|
||||
LocalSlot(*entry)
|
||||
StackOffset(*entry)
|
||||
}
|
||||
|
||||
fn map_address(&mut self, addr: Address) -> Address {
|
||||
fn map_address(&mut self, addr: Address<VirtualId>) -> Address<StackOffset> {
|
||||
match addr {
|
||||
Address::Local(slot) => Address::Local(self.map_slot(slot)),
|
||||
other => other,
|
||||
Address::Upvalue(idx) => Address::Upvalue(idx),
|
||||
Address::Global(idx) => Address::Global(idx),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -769,7 +769,7 @@ mod tests {
|
||||
locals.insert(
|
||||
Symbol::from("*"),
|
||||
crate::ast::compiler::binder::LocalInfo {
|
||||
addr: Address::Local(crate::ast::compiler::bound_nodes::LocalSlot(0)),
|
||||
addr: Address::Local(crate::ast::compiler::bound_nodes::VirtualId(0)),
|
||||
identity: crate::ast::types::NodeIdentity::new(crate::ast::types::SourceLocation {
|
||||
line: 0,
|
||||
col: 0,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, LocalSlot};
|
||||
use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, VirtualId};
|
||||
use crate::ast::types::{Purity, Value};
|
||||
use crate::ast::vm::Closure;
|
||||
use std::cell::RefCell;
|
||||
@@ -24,7 +24,7 @@ impl<'a> Inliner<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_inlinable_value(&self, val: &Value, addr: Address) -> bool {
|
||||
pub fn is_inlinable_value(&self, val: &Value, addr: Address<VirtualId>) -> bool {
|
||||
let type_ok = match val {
|
||||
Value::Int(_)
|
||||
| Value::Float(_)
|
||||
@@ -141,7 +141,7 @@ impl<'a> Inliner<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn collect_parameter_slots_set(&self, node: &AnalyzedNode, slots: &mut HashSet<LocalSlot>) {
|
||||
pub fn collect_parameter_slots_set(&self, node: &AnalyzedNode, slots: &mut HashSet<VirtualId>) {
|
||||
match &node.kind {
|
||||
BoundKind::Define {
|
||||
addr: Address::Local(slot),
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, LocalSlot, Node, UpvalueIdx};
|
||||
use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, Node, UpvalueIdx, VirtualId};
|
||||
use crate::ast::types::Value;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct SubstitutionMap {
|
||||
pub values: HashMap<Address, Value>,
|
||||
pub ast_substitutions: HashMap<Address, Rc<AnalyzedNode>>,
|
||||
pub slot_mapping: HashMap<LocalSlot, LocalSlot>,
|
||||
pub assigned: HashSet<Address>,
|
||||
pub values: HashMap<Address<VirtualId>, Value>,
|
||||
pub ast_substitutions: HashMap<Address<VirtualId>, Rc<AnalyzedNode>>,
|
||||
pub slot_mapping: HashMap<VirtualId, VirtualId>,
|
||||
pub assigned: HashSet<Address<VirtualId>>,
|
||||
pub next_slot: u32,
|
||||
pub used: HashSet<Address>,
|
||||
pub captured_slots: HashSet<LocalSlot>,
|
||||
pub used: HashSet<Address<VirtualId>>,
|
||||
pub captured_slots: HashSet<VirtualId>,
|
||||
}
|
||||
|
||||
impl SubstitutionMap {
|
||||
@@ -43,40 +43,40 @@ impl SubstitutionMap {
|
||||
inner
|
||||
}
|
||||
|
||||
pub fn add_ast_substitution(&mut self, addr: Address, node: AnalyzedNode) {
|
||||
pub fn add_ast_substitution(&mut self, addr: Address<VirtualId>, node: AnalyzedNode) {
|
||||
self.ast_substitutions.insert(addr, Rc::new(node));
|
||||
}
|
||||
|
||||
pub fn map_slot(&mut self, old_slot: LocalSlot) -> LocalSlot {
|
||||
pub fn map_slot(&mut self, old_slot: VirtualId) -> VirtualId {
|
||||
if let Some(&new_slot) = self.slot_mapping.get(&old_slot) {
|
||||
return new_slot;
|
||||
}
|
||||
let new_slot = LocalSlot(self.next_slot);
|
||||
let new_slot = VirtualId(self.next_slot);
|
||||
self.slot_mapping.insert(old_slot, new_slot);
|
||||
self.next_slot += 1;
|
||||
new_slot
|
||||
}
|
||||
|
||||
pub fn map_address(&mut self, addr: Address) -> Address {
|
||||
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, val: Value) {
|
||||
pub fn add_value(&mut self, addr: Address<VirtualId>, val: Value) {
|
||||
self.values.insert(addr, val);
|
||||
}
|
||||
|
||||
pub fn get_value(&self, addr: &Address) -> Option<&Value> {
|
||||
pub fn get_value(&self, addr: &Address<VirtualId>) -> Option<&Value> {
|
||||
self.values.get(addr)
|
||||
}
|
||||
|
||||
pub fn remove_value(&mut self, addr: &Address) {
|
||||
pub fn remove_value(&mut self, addr: &Address<VirtualId>) {
|
||||
self.values.remove(addr);
|
||||
}
|
||||
|
||||
fn reindex_addr(&self, addr: Address, mapping: &[Option<u32>]) -> Address {
|
||||
fn reindex_addr(&self, addr: Address<VirtualId>, mapping: &[Option<u32>]) -> Address<VirtualId> {
|
||||
if let Address::Upvalue(idx) = addr
|
||||
&& let Some(res) = mapping.get(idx.0 as usize)
|
||||
&& let Some(new_idx) = res
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, GlobalIdx};
|
||||
use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, GlobalIdx, VirtualId};
|
||||
use crate::ast::types::{Identity, Value};
|
||||
use crate::ast::vm::Closure;
|
||||
use std::collections::HashSet;
|
||||
@@ -32,17 +32,17 @@ impl PathTracker {
|
||||
// --- UsageInfo ---
|
||||
#[derive(Default)]
|
||||
pub struct UsageInfo {
|
||||
pub used: HashSet<Address>,
|
||||
pub assigned: HashSet<Address>,
|
||||
pub used: HashSet<Address<VirtualId>>,
|
||||
pub assigned: HashSet<Address<VirtualId>>,
|
||||
pub used_identities: HashSet<Identity>,
|
||||
}
|
||||
|
||||
impl UsageInfo {
|
||||
pub fn is_assigned(&self, addr: &Address) -> bool {
|
||||
pub fn is_assigned(&self, addr: &Address<VirtualId>) -> bool {
|
||||
self.assigned.contains(addr)
|
||||
}
|
||||
|
||||
pub fn is_used(&self, addr: &Address) -> bool {
|
||||
pub fn is_used(&self, addr: &Address<VirtualId>) -> bool {
|
||||
self.used.contains(addr)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, Node, NodeMetrics};
|
||||
use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, AnalyzedPhase, BoundKind, Node, NodeMetrics, VirtualId};
|
||||
use crate::ast::types::{Purity, Signature, StaticType, Value};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
@@ -6,16 +6,16 @@ use std::rc::Rc;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct MonoCacheKey {
|
||||
pub address: Address,
|
||||
pub address: Address<VirtualId>,
|
||||
pub arg_types: Vec<StaticType>,
|
||||
}
|
||||
|
||||
pub type CompileFunc = Rc<dyn Fn(Rc<Node>, &[StaticType]) -> Result<(Value, StaticType), String>>;
|
||||
pub type CompileFunc = Rc<dyn Fn(Rc<Node<AnalyzedPhase>>, &[StaticType]) -> Result<(Value, StaticType), String>>;
|
||||
pub type RtlLookupFunc = Rc<dyn Fn(&str, &[StaticType]) -> Option<(Value, StaticType)>>;
|
||||
|
||||
pub trait FunctionRegistry {
|
||||
fn resolve(&self, addr: Address) -> Option<Rc<Node>>;
|
||||
fn resolve_analyzed(&self, _addr: Address) -> Option<Rc<AnalyzedNode>> {
|
||||
fn resolve(&self, addr: Address<VirtualId>) -> Option<Rc<Node<AnalyzedPhase>>>;
|
||||
fn resolve_analyzed(&self, _addr: Address<VirtualId>) -> Option<Rc<AnalyzedNode>> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::ast::compiler::bound_nodes::{Address, BoundKind, Node, TypedNode};
|
||||
use crate::ast::compiler::bound_nodes::{Address, BoundKind, BoundPhase, CompilerPhase, Node, TypedNode, VirtualId};
|
||||
use crate::ast::diagnostics::Diagnostics;
|
||||
use crate::ast::types::StaticType;
|
||||
use std::collections::HashMap;
|
||||
@@ -33,7 +33,7 @@ impl<'a> TypeContext<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_type(&self, addr: Address) -> StaticType {
|
||||
fn get_type(&self, addr: Address<VirtualId>) -> StaticType {
|
||||
match addr {
|
||||
Address::Local(slot) => self
|
||||
.slots
|
||||
@@ -54,7 +54,7 @@ impl<'a> TypeContext<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn set_type(&mut self, addr: Address, ty: StaticType) {
|
||||
fn set_type(&mut self, addr: Address<VirtualId>, ty: StaticType) {
|
||||
match addr {
|
||||
Address::Local(slot) => {
|
||||
self.slots.insert(slot.0, ty);
|
||||
@@ -81,7 +81,18 @@ impl TypeChecker {
|
||||
|
||||
pub fn check(
|
||||
&self,
|
||||
node: &Node,
|
||||
node: &Node<BoundPhase>,
|
||||
arg_types: &[StaticType],
|
||||
diag: &mut Diagnostics,
|
||||
) -> TypedNode {
|
||||
self.check_node_as_bound(node, arg_types, diag)
|
||||
}
|
||||
|
||||
/// Allows re-checking a node from any phase as if it were a bound node.
|
||||
/// This is useful for specialization where we re-type a TypedNode with more specific info.
|
||||
pub fn check_node_as_bound<P: CompilerPhase<LocalAddress = VirtualId>>(
|
||||
&self,
|
||||
node: &Node<P>,
|
||||
arg_types: &[StaticType],
|
||||
diag: &mut Diagnostics,
|
||||
) -> TypedNode {
|
||||
@@ -92,20 +103,17 @@ impl TypeChecker {
|
||||
body,
|
||||
positional_count,
|
||||
} => {
|
||||
// 1. Determine types of captured variables (Root lambdas have none)
|
||||
let mut upvalue_types = Vec::with_capacity(upvalues.len());
|
||||
for _ in upvalues {
|
||||
for &_addr in upvalues {
|
||||
upvalue_types.push(StaticType::Any);
|
||||
}
|
||||
|
||||
// 2. Create the specialized context
|
||||
let root_ctx = TypeContext::new(0, vec![], &self.root_types, None);
|
||||
let mut lambda_ctx =
|
||||
TypeContext::new(64, upvalue_types, &self.root_types, Some(&root_ctx));
|
||||
|
||||
// 3. INJECT specialized argument types into slots
|
||||
let arg_tuple_ty = if arg_types.is_empty() {
|
||||
StaticType::Any // No specialized info
|
||||
StaticType::Any
|
||||
} else {
|
||||
StaticType::Tuple(arg_types.to_vec())
|
||||
};
|
||||
@@ -117,11 +125,8 @@ impl TypeChecker {
|
||||
diag,
|
||||
);
|
||||
|
||||
// 4. Check body with the new types
|
||||
let body_typed = self.check_node(body, &mut lambda_ctx, diag);
|
||||
let ret_ty = body_typed.ty.clone();
|
||||
|
||||
// 5. Construct specialized function type
|
||||
let final_params_ty = params_typed.ty.clone();
|
||||
|
||||
let fn_ty = StaticType::Function(Box::new(crate::ast::types::Signature {
|
||||
@@ -147,9 +152,9 @@ impl TypeChecker {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_params(
|
||||
fn check_params<P: CompilerPhase<LocalAddress = VirtualId>>(
|
||||
&self,
|
||||
node: &Node,
|
||||
node: &Node<P>,
|
||||
specialized_ty: &StaticType,
|
||||
ctx: &mut TypeContext,
|
||||
diag: &mut Diagnostics,
|
||||
@@ -182,10 +187,6 @@ impl TypeChecker {
|
||||
addr,
|
||||
value: _value,
|
||||
} => {
|
||||
// In an assignment pattern, 'value' is just a Nop placeholder from the Binder.
|
||||
// We update the type of the address (if it's a local or global) to match the specialized type.
|
||||
// Note: For now, we assume assignments are compatible if types were already inferred,
|
||||
// or we could add a check here against existing type.
|
||||
(
|
||||
BoundKind::Set {
|
||||
addr: *addr,
|
||||
@@ -240,7 +241,7 @@ impl TypeChecker {
|
||||
StaticType::Error => StaticType::Error,
|
||||
_ => StaticType::Any,
|
||||
};
|
||||
let t = self.check_params(el, &sub_ty, ctx, diag);
|
||||
let t = self.check_params(el.as_ref(), &sub_ty, ctx, diag);
|
||||
elem_types.push(t.ty.clone());
|
||||
typed_elements.push(Rc::new(t));
|
||||
}
|
||||
@@ -268,9 +269,9 @@ impl TypeChecker {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_node(
|
||||
fn check_node<P: CompilerPhase<LocalAddress = VirtualId>>(
|
||||
&self,
|
||||
node: &Node,
|
||||
node: &Node<P>,
|
||||
ctx: &mut TypeContext,
|
||||
diag: &mut Diagnostics,
|
||||
) -> TypedNode {
|
||||
@@ -373,7 +374,7 @@ impl TypeChecker {
|
||||
|
||||
BoundKind::Destructure { pattern, value } => {
|
||||
let val_typed = self.check_node(value, ctx, diag);
|
||||
let pat_typed = self.check_params(pattern, &val_typed.ty, ctx, diag);
|
||||
let pat_typed = self.check_params(pattern.as_ref(), &val_typed.ty, ctx, diag);
|
||||
let ty = val_typed.ty.clone();
|
||||
(
|
||||
BoundKind::Destructure {
|
||||
@@ -397,13 +398,11 @@ impl TypeChecker {
|
||||
|
||||
if let Some(e) = else_br {
|
||||
let et = self.check_node(e, ctx, diag);
|
||||
// Basic type promotion: if types differ, fall back to Any
|
||||
if et.ty != final_ty {
|
||||
final_ty = StaticType::Any;
|
||||
}
|
||||
else_typed = Some(Rc::new(et));
|
||||
} else {
|
||||
// If without else returns Optional(Then) (T | Void)
|
||||
final_ty = StaticType::Optional(Box::new(then_typed.ty.clone()));
|
||||
}
|
||||
|
||||
@@ -422,7 +421,6 @@ impl TypeChecker {
|
||||
let mut arg_types = Vec::with_capacity(inputs.len());
|
||||
for input in inputs {
|
||||
let typed_input = self.check_node(input, ctx, diag);
|
||||
// Unwrap Series(T) or Stream(T) to T for the lambda argument
|
||||
let arg_ty = if let StaticType::Series(inner) = &typed_input.ty {
|
||||
*inner.clone()
|
||||
} else if let StaticType::Stream(inner) = &typed_input.ty {
|
||||
@@ -434,11 +432,9 @@ impl TypeChecker {
|
||||
typed_inputs.push(Rc::new(typed_input));
|
||||
}
|
||||
|
||||
// Specialized check for the lambda using the input types!
|
||||
let typed_lambda = self.check(lambda, &arg_types, diag);
|
||||
let typed_lambda = self.check_node_as_bound(lambda.as_ref(), &arg_types, diag);
|
||||
|
||||
let ret_ty = if let StaticType::Function(sig) = &typed_lambda.ty {
|
||||
// If the lambda returns an Optional(T), the pipeline filters Void and stores T!
|
||||
if let StaticType::Optional(inner) = &sig.ret {
|
||||
*inner.clone()
|
||||
} else {
|
||||
@@ -476,17 +472,14 @@ impl TypeChecker {
|
||||
body,
|
||||
positional_count,
|
||||
} => {
|
||||
// 1. Determine types of captured variables
|
||||
let mut upvalue_types = Vec::with_capacity(upvalues.len());
|
||||
for &addr in upvalues {
|
||||
upvalue_types.push(ctx.get_type(addr));
|
||||
}
|
||||
|
||||
// 2. Create nested context for lambda body
|
||||
let mut lambda_ctx =
|
||||
TypeContext::new(64, upvalue_types, ctx.root_types, Some(ctx));
|
||||
|
||||
// 3. Check parameters and body
|
||||
let params_typed = self.check_params(
|
||||
params.as_ref(),
|
||||
&StaticType::Any,
|
||||
@@ -494,13 +487,11 @@ impl TypeChecker {
|
||||
diag,
|
||||
);
|
||||
|
||||
// Set current params type for 'again' validation
|
||||
lambda_ctx.current_params_ty = Some(params_typed.ty.clone());
|
||||
|
||||
let body_typed = self.check_node(body, &mut lambda_ctx, diag);
|
||||
let ret_ty = body_typed.ty.clone();
|
||||
|
||||
// 4. Construct function type
|
||||
let fn_ty = StaticType::Function(Box::new(crate::ast::types::Signature {
|
||||
params: params_typed.ty.clone(),
|
||||
ret: ret_ty,
|
||||
@@ -520,7 +511,6 @@ impl TypeChecker {
|
||||
BoundKind::Call { callee, args } => {
|
||||
let callee_typed = self.check_node(callee, ctx, diag);
|
||||
|
||||
// Manually check args (Tuple) to prevent Vector/Matrix promotion
|
||||
let args_typed = if let BoundKind::Tuple { elements } = &args.kind {
|
||||
let mut typed_elements = Vec::new();
|
||||
let mut elem_types = Vec::new();
|
||||
@@ -537,7 +527,6 @@ impl TypeChecker {
|
||||
ty: StaticType::Tuple(elem_types),
|
||||
}
|
||||
} else {
|
||||
// Should not happen as parser wraps args in Tuple, but fallback safely
|
||||
self.check_node(args, ctx, diag)
|
||||
};
|
||||
|
||||
@@ -565,7 +554,6 @@ impl TypeChecker {
|
||||
}
|
||||
|
||||
BoundKind::Again { args } => {
|
||||
// Manually check args (Tuple) to prevent Vector/Matrix promotion
|
||||
let args_typed = if let BoundKind::Tuple { elements } = &args.kind {
|
||||
let mut typed_elements = Vec::new();
|
||||
let mut elem_types = Vec::new();
|
||||
|
||||
+31
-24
@@ -10,12 +10,12 @@ use std::path::{Path, PathBuf};
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::ast::compiler::bound_nodes::{
|
||||
Address, AnalyzedNode, BoundKind, GlobalAnalyzedRegistry, GlobalFunctionRegistry, GlobalIdx,
|
||||
Node,
|
||||
Address, AnalyzedNode, BoundKind, ExecNode, GlobalAnalyzedRegistry, GlobalFunctionRegistry, GlobalIdx,
|
||||
Node, VirtualId,
|
||||
};
|
||||
use crate::ast::compiler::dumper::Dumper;
|
||||
use crate::ast::compiler::lambda_collector::LambdaCollector;
|
||||
use crate::ast::compiler::lowering::{Lowering, ExecNode};
|
||||
use crate::ast::compiler::lowering::Lowering;
|
||||
use crate::ast::compiler::macros::{MacroEvaluator, MacroExpander, MacroRegistry};
|
||||
use crate::ast::compiler::optimizer::Optimizer;
|
||||
use crate::ast::compiler::specializer::{FunctionRegistry, MonoCache, Specializer};
|
||||
@@ -89,19 +89,11 @@ pub struct Environment {
|
||||
}
|
||||
|
||||
struct EnvFunctionRegistry {
|
||||
registry: Rc<RefCell<GlobalFunctionRegistry>>,
|
||||
analyzed_registry: Rc<RefCell<GlobalAnalyzedRegistry>>,
|
||||
}
|
||||
|
||||
impl FunctionRegistry for EnvFunctionRegistry {
|
||||
fn resolve(&self, addr: Address) -> Option<Rc<Node>> {
|
||||
if let Address::Global(idx) = addr {
|
||||
self.registry.borrow().get(&idx).cloned()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
fn resolve_analyzed(&self, addr: Address) -> Option<Rc<AnalyzedNode>> {
|
||||
fn resolve(&self, addr: Address<VirtualId>) -> Option<Rc<Node<crate::ast::compiler::bound_nodes::AnalyzedPhase>>> {
|
||||
if let Address::Global(idx) = addr {
|
||||
self.analyzed_registry.borrow().get(&idx).cloned()
|
||||
} else {
|
||||
@@ -406,7 +398,7 @@ impl Environment {
|
||||
|
||||
if !current_scope.locals.contains_key(sym) {
|
||||
let mut slot_count = self.root_slot_count.borrow_mut();
|
||||
let slot = crate::ast::compiler::bound_nodes::LocalSlot(*slot_count);
|
||||
let slot = VirtualId(*slot_count);
|
||||
current_scope.locals.insert(
|
||||
sym.clone(),
|
||||
crate::ast::compiler::binder::LocalInfo {
|
||||
@@ -484,15 +476,15 @@ impl Environment {
|
||||
LambdaCollector::collect(&bound_ast, &mut self.function_registry.borrow_mut());
|
||||
|
||||
let checker = TypeChecker::new(self.root_types.clone());
|
||||
let wrapped_ast = if let crate::ast::compiler::bound_nodes::BoundKind::Lambda { .. } = bound_ast.kind {
|
||||
let wrapped_ast = if let BoundKind::Lambda { .. } = bound_ast.kind {
|
||||
bound_ast
|
||||
} else {
|
||||
crate::ast::compiler::bound_nodes::Node {
|
||||
Node {
|
||||
identity: bound_ast.identity.clone(),
|
||||
kind: crate::ast::compiler::bound_nodes::BoundKind::Lambda {
|
||||
params: std::rc::Rc::new(crate::ast::compiler::bound_nodes::Node {
|
||||
kind: BoundKind::Lambda {
|
||||
params: std::rc::Rc::new(Node {
|
||||
identity: bound_ast.identity.clone(),
|
||||
kind: crate::ast::compiler::bound_nodes::BoundKind::Tuple { elements: vec![] },
|
||||
kind: BoundKind::Tuple { elements: vec![] },
|
||||
ty: (),
|
||||
}),
|
||||
upvalues: vec![],
|
||||
@@ -668,7 +660,7 @@ impl Environment {
|
||||
{
|
||||
let closure = Rc::new(crate::ast::vm::Closure::new(
|
||||
params.clone(),
|
||||
body.ty.original.clone(),
|
||||
body.ty.original.clone(),
|
||||
body.clone(),
|
||||
Vec::new(),
|
||||
*positional_count,
|
||||
@@ -717,13 +709,11 @@ impl Environment {
|
||||
|
||||
fn specialize_node(&self, node: AnalyzedNode) -> AnalyzedNode {
|
||||
let registry = Rc::new(EnvFunctionRegistry {
|
||||
registry: self.function_registry.clone(),
|
||||
analyzed_registry: self.typed_function_registry.clone(),
|
||||
});
|
||||
|
||||
let rtl_lookup = Rc::new(|name: &str, args: &[StaticType]| intrinsics::lookup(name, args));
|
||||
let typed_reg = self.typed_function_registry.clone();
|
||||
let syntax_reg = self.function_registry.clone();
|
||||
let mono_cache = self.monomorph_cache.clone();
|
||||
let root_values = self.root_values.clone();
|
||||
let root_types = self.root_types.clone();
|
||||
@@ -731,12 +721,30 @@ impl Environment {
|
||||
let optimization = self.optimization;
|
||||
|
||||
let compiler = Rc::new(
|
||||
move |func_template: Rc<Node>,
|
||||
move |func_template: Rc<Node<crate::ast::compiler::bound_nodes::AnalyzedPhase>>,
|
||||
arg_types: &[StaticType]|
|
||||
-> Result<(Value, StaticType), String> {
|
||||
let mut diag = Diagnostics::new();
|
||||
let checker = TypeChecker::new(root_types.clone());
|
||||
let retyped_ast = checker.check(func_template.as_ref(), arg_types, &mut diag);
|
||||
|
||||
// For specialization, we re-type-check the analyzed node.
|
||||
// Note: AnalyzedNode.ty.original is the TypedNode.
|
||||
// But TypeChecker expects Node<BoundPhase>. We need to go from TypedNode -> BoundNode.
|
||||
// However, since TypedNode is just Node<TypedPhase>, we can't easily "un-type" it.
|
||||
// Instead, we access the original AST from the binder if we had it,
|
||||
// OR we make the TypeChecker generic.
|
||||
// For now, we assume the TypedNode can be used where a BoundNode is expected
|
||||
// if we strip the metadata.
|
||||
// A better way is to store the BoundNode in NodeMetrics as well.
|
||||
|
||||
// Temporary fix: Re-binding from source would be too expensive.
|
||||
// Let's assume for now that we can specialize directly on the TypedNode
|
||||
// or that we need a small helper to transform TypedNode -> BoundNode.
|
||||
|
||||
// Realizing that TypedNode (StaticType) is very similar to BoundNode (()),
|
||||
// we can just use the internal transform.
|
||||
|
||||
let retyped_ast = checker.check_node_as_bound(func_template.ty.original.as_ref(), arg_types, &mut diag);
|
||||
|
||||
if diag.has_errors() {
|
||||
return Err(diag
|
||||
@@ -750,7 +758,6 @@ impl Environment {
|
||||
let analyzed = Analyzer::analyze(&retyped_ast, &root_purity.borrow());
|
||||
|
||||
let sub_registry = Rc::new(EnvFunctionRegistry {
|
||||
registry: syntax_reg.clone(),
|
||||
analyzed_registry: typed_reg.clone(),
|
||||
});
|
||||
let sub_rtl_lookup =
|
||||
|
||||
+7
-8
@@ -1,5 +1,4 @@
|
||||
use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind};
|
||||
use crate::ast::compiler::lowering::ExecNode;
|
||||
use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, ExecNode, StackOffset};
|
||||
use crate::ast::types::{Object, Value};
|
||||
use std::any::Any;
|
||||
use std::cell::RefCell;
|
||||
@@ -335,7 +334,7 @@ impl VM {
|
||||
val.clone()
|
||||
};
|
||||
|
||||
self.set_value(addr.clone(), store_val)?;
|
||||
self.set_value(*addr, store_val)?;
|
||||
// Define always evaluates to the unwrapped value for immediate use.
|
||||
Ok(val)
|
||||
}
|
||||
@@ -351,7 +350,7 @@ impl VM {
|
||||
}
|
||||
Ok(val)
|
||||
}
|
||||
BoundKind::Get { addr, .. } => self.get_value(addr.clone()),
|
||||
BoundKind::Get { addr, .. } => self.get_value(*addr),
|
||||
|
||||
BoundKind::FieldAccessor(k) => Ok(Value::FieldAccessor(k.clone())),
|
||||
|
||||
@@ -399,7 +398,7 @@ impl VM {
|
||||
|
||||
BoundKind::Set { addr, value } => {
|
||||
let val = self.eval_internal(obs, value)?;
|
||||
self.set_value(addr.clone(), val.clone())?;
|
||||
self.set_value(*addr, val.clone())?;
|
||||
Ok(val)
|
||||
}
|
||||
BoundKind::If {
|
||||
@@ -882,7 +881,7 @@ impl VM {
|
||||
}
|
||||
}
|
||||
|
||||
fn capture_upvalue(&mut self, addr: Address) -> Result<Rc<RefCell<Value>>, String> {
|
||||
fn capture_upvalue(&mut self, addr: Address<StackOffset>) -> Result<Rc<RefCell<Value>>, String> {
|
||||
match addr {
|
||||
Address::Local(slot) => {
|
||||
let frame = self.frames.last().ok_or("No call frame")?;
|
||||
@@ -918,7 +917,7 @@ impl VM {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_value(&self, addr: Address) -> Result<Value, String> {
|
||||
fn get_value(&self, addr: Address<StackOffset>) -> Result<Value, String> {
|
||||
match addr {
|
||||
Address::Local(slot) => {
|
||||
let frame = self.frames.last().ok_or("No call frame")?;
|
||||
@@ -958,7 +957,7 @@ impl VM {
|
||||
}
|
||||
}
|
||||
|
||||
fn set_value(&mut self, addr: Address, value: Value) -> Result<(), String> {
|
||||
fn set_value(&mut self, addr: Address<StackOffset>, value: Value) -> Result<(), String> {
|
||||
match addr {
|
||||
Address::Local(slot) => {
|
||||
let frame = self.frames.last().ok_or("No call frame")?;
|
||||
|
||||
Reference in New Issue
Block a user