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:
Michael Schimmel
2026-03-13 19:20:44 +01:00
parent d035da9d91
commit e5d82ee2b6
15 changed files with 403 additions and 458 deletions
+3 -3
View File
@@ -1,5 +1,5 @@
use crate::ast::compiler::bound_nodes::{ 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 crate::ast::types::Purity;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
@@ -318,7 +318,7 @@ impl<'a> Analyzer<'a> {
BoundKind::Error => (BoundKind::Error, Purity::Impure), BoundKind::Error => (BoundKind::Error, Purity::Impure),
}; };
crate::ast::compiler::bound_nodes::Node { Node {
identity: node.identity.clone(), identity: node.identity.clone(),
kind: new_kind, kind: new_kind,
ty: NodeMetrics { ty: NodeMetrics {
@@ -334,7 +334,7 @@ trait NodeExt {
fn for_each_child<F: FnMut(&TypedNode)>(&self, f: F); 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) { fn for_each_child<F: FnMut(&TypedNode)>(&self, mut f: F) {
match self { match self {
BoundKind::If { BoundKind::If {
+16 -16
View File
@@ -1,5 +1,5 @@
use crate::ast::compiler::bound_nodes::{ 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::diagnostics::Diagnostics;
use crate::ast::nodes::{Symbol, SyntaxKind, SyntaxNode}; use crate::ast::nodes::{Symbol, SyntaxKind, SyntaxNode};
@@ -9,7 +9,7 @@ use std::rc::Rc;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct LocalInfo { pub struct LocalInfo {
pub addr: Address, pub addr: Address<VirtualId>,
pub identity: Identity, pub identity: Identity,
pub _ty: StaticType, pub _ty: StaticType,
pub purity: Purity, pub purity: Purity,
@@ -38,7 +38,7 @@ struct FunctionCompiler {
identity: Identity, identity: Identity,
scopes: Vec<CompilerScope>, scopes: Vec<CompilerScope>,
slot_count: u32, slot_count: u32,
upvalues: Vec<Address>, upvalues: Vec<Address<VirtualId>>,
} }
impl FunctionCompiler { 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(); let current_scope = self.scopes.last_mut().unwrap();
if current_scope.locals.contains_key(name) { if current_scope.locals.contains_key(name) {
return Err(format!( 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( current_scope.locals.insert(
name.clone(), name.clone(),
LocalInfo { LocalInfo {
@@ -97,7 +97,7 @@ impl FunctionCompiler {
None 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) { if let Some(idx) = self.upvalues.iter().position(|&a| a == addr) {
return UpvalueIdx(idx as u32); return UpvalueIdx(idx as u32);
} }
@@ -114,7 +114,7 @@ pub enum ExprContext {
} }
pub type BindingResult = ( pub type BindingResult = (
Node, Node<BoundPhase>,
HashMap<Identity, Vec<Identity>>, HashMap<Identity, Vec<Identity>>,
Vec<CompilerScope>, Vec<CompilerScope>,
u32, u32,
@@ -179,7 +179,7 @@ impl Binder {
identity: Identity, identity: Identity,
_kind: crate::ast::compiler::bound_nodes::DeclarationKind, _kind: crate::ast::compiler::bound_nodes::DeclarationKind,
diag: &mut Diagnostics, diag: &mut Diagnostics,
) -> Option<Address> { ) -> Option<Address<VirtualId>> {
let current_fn_idx = self.functions.len() - 1; let current_fn_idx = self.functions.len() - 1;
if current_fn_idx == 0 { if current_fn_idx == 0 {
@@ -211,7 +211,7 @@ impl Binder {
node: &SyntaxNode, node: &SyntaxNode,
ctx: ExprContext, ctx: ExprContext,
diag: &mut Diagnostics, diag: &mut Diagnostics,
) -> Node { ) -> Node<BoundPhase> {
match &node.kind { match &node.kind {
SyntaxKind::Nop => self.make_node(node.identity.clone(), BoundKind::Nop), SyntaxKind::Nop => self.make_node(node.identity.clone(), BoundKind::Nop),
SyntaxKind::Constant(v) => { SyntaxKind::Constant(v) => {
@@ -361,7 +361,7 @@ impl Binder {
let body_bound = self.bind(body.as_ref(), ExprContext::Expression, diag); let body_bound = self.bind(body.as_ref(), ExprContext::Expression, diag);
let compiled_fn = self.functions.pop().unwrap(); 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 { match &node.kind {
BoundKind::Define { BoundKind::Define {
kind: DeclarationKind::Parameter, kind: DeclarationKind::Parameter,
@@ -520,9 +520,9 @@ impl Binder {
); );
self.make_node(node.identity.clone(), BoundKind::Error) self.make_node(node.identity.clone(), BoundKind::Error)
} }
SyntaxKind::Error => crate::ast::compiler::bound_nodes::Node { SyntaxKind::Error => Node {
identity: node.identity.clone(), identity: node.identity.clone(),
kind: crate::ast::compiler::bound_nodes::BoundKind::Error, kind: BoundKind::Error,
ty: (), ty: (),
}, },
} }
@@ -533,7 +533,7 @@ impl Binder {
sym: &Symbol, sym: &Symbol,
diag: &mut Diagnostics, diag: &mut Diagnostics,
identity: &Identity, identity: &Identity,
) -> Option<Address> { ) -> Option<Address<VirtualId>> {
let current_fn_idx = self.functions.len() - 1; let current_fn_idx = self.functions.len() - 1;
// 1. Try local in current function // 1. Try local in current function
@@ -603,7 +603,7 @@ impl Binder {
node: &SyntaxNode, node: &SyntaxNode,
kind: DeclarationKind, kind: DeclarationKind,
diag: &mut Diagnostics, diag: &mut Diagnostics,
) -> Node { ) -> Node<BoundPhase> {
match &node.kind { match &node.kind {
SyntaxKind::Identifier(sym) => { SyntaxKind::Identifier(sym) => {
if let Some(addr) = self.declare_variable(sym, node.identity.clone(), kind, diag) { if let Some(addr) = self.declare_variable(sym, node.identity.clone(), kind, diag) {
@@ -647,7 +647,7 @@ impl Binder {
&mut self, &mut self,
node: &SyntaxNode, node: &SyntaxNode,
diag: &mut Diagnostics, diag: &mut Diagnostics,
) -> Node { ) -> Node<BoundPhase> {
match &node.kind { match &node.kind {
SyntaxKind::Identifier(sym) => { SyntaxKind::Identifier(sym) => {
if let Some(addr) = self.resolve_variable(sym, diag, &node.identity) { 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 { Node {
identity, identity,
kind, kind,
+199 -199
View File
@@ -3,11 +3,20 @@ use crate::ast::types::{Identity, StaticType, Value};
use std::rc::Rc; use std::rc::Rc;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] #[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 { 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)] #[derive(Debug, PartialEq, Eq, Hash)]
pub enum Address { pub enum Address<L> {
Local(LocalSlot), // Stack-Slot index (relative to frame base) Local(L), // Local address (VirtualId or StackOffset)
Upvalue(UpvalueIdx), // Index in the closure's upvalue array Upvalue(UpvalueIdx), // Index in the closure's upvalue array
Global(GlobalIdx), // Index in the global environment vector 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)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum DeclarationKind { pub enum DeclarationKind {
Variable, Variable,
Parameter, Parameter,
} }
/// Trait for DSL-specific AST extensions (like Pipe, Series-Access, etc.) pub trait CompilerPhase: std::fmt::Debug + 'static {
pub trait BoundExtension<T>: std::fmt::Debug { type Metadata: std::fmt::Debug + Clone + PartialEq;
fn clone_box(&self) -> Box<dyn BoundExtension<T>>; type LocalAddress: std::fmt::Debug + Clone + Copy + PartialEq + Eq + std::hash::Hash;
fn display_name(&self) -> String;
} }
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 { 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. pub type TypedNode = Node<TypedPhase>;
#[derive(Debug, Clone, PartialEq)]
pub struct Node<T = ()> {
pub identity: Identity,
pub kind: BoundKind<T>,
pub ty: T,
}
/// 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)] #[derive(Debug, Clone, PartialEq)]
pub struct NodeMetrics { pub struct NodeMetrics {
pub original: Rc<TypedNode>, pub original: Rc<TypedNode>,
@@ -76,114 +123,144 @@ pub struct NodeMetrics {
pub is_recursive: bool, pub is_recursive: bool,
} }
/// Type alias for a node that has been analyzed. pub type AnalyzedNode = Node<AnalyzedPhase>;
pub type AnalyzedNode = Node<NodeMetrics>;
/// 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>>; pub type GlobalAnalyzedRegistry = std::collections::HashMap<GlobalIdx, Rc<AnalyzedNode>>;
#[derive(Debug, Clone)] pub trait BoundExtension<P: CompilerPhase>: std::fmt::Debug {
pub enum BoundKind<T = ()> { 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, Nop,
Constant(Value), Constant(Value),
// Variable Access (Resolved)
Get { Get {
addr: Address, addr: Address<P::LocalAddress>,
name: Symbol, name: Symbol,
}, },
// Variable Update (Assignment)
Set { Set {
addr: Address, addr: Address<P::LocalAddress>,
value: Rc<Node<T>>, value: Rc<Node<P>>,
}, },
// Variable Declaration (Unified for Local/Global/Parameter)
Define { Define {
name: Symbol, name: Symbol,
addr: Address, addr: Address<P::LocalAddress>,
kind: DeclarationKind, kind: DeclarationKind,
value: Rc<Node<T>>, value: Rc<Node<P>>,
captured_by: Vec<Identity>, captured_by: Vec<Identity>,
}, },
/// A first-class field accessor (e.g. .name)
FieldAccessor(crate::ast::types::Keyword), FieldAccessor(crate::ast::types::Keyword),
/// Specialized field access (O(1) via RecordLayout)
GetField { GetField {
rec: Rc<Node<T>>, rec: Rc<Node<P>>,
field: crate::ast::types::Keyword, field: crate::ast::types::Keyword,
}, },
If { If {
cond: Rc<Node<T>>, cond: Rc<Node<P>>,
then_br: Rc<Node<T>>, then_br: Rc<Node<P>>,
else_br: Option<Rc<Node<T>>>, else_br: Option<Rc<Node<P>>>,
}, },
/// A destructuring operation (can be a definition or an assignment depending on the pattern)
Destructure { Destructure {
pattern: Rc<Node<T>>, pattern: Rc<Node<P>>,
value: Rc<Node<T>>, value: Rc<Node<P>>,
}, },
Lambda { Lambda {
params: Rc<Node<T>>, params: Rc<Node<P>>,
// The list of variables captured from enclosing scopes upvalues: Vec<Address<P::LocalAddress>>,
upvalues: Vec<Address>, body: Rc<Node<P>>,
body: Rc<Node<T>>,
/// Static optimization: number of positional parameters if the pattern is flat.
positional_count: Option<u32>, positional_count: Option<u32>,
}, },
Call { Call {
callee: Rc<Node<T>>, callee: Rc<Node<P>>,
args: Rc<Node<T>>, args: Rc<Node<P>>,
}, },
Again { Again {
args: Rc<Node<T>>, args: Rc<Node<P>>,
}, },
Pipe { Pipe {
inputs: Vec<Rc<Node<T>>>, inputs: Vec<Rc<Node<P>>>,
lambda: Rc<Node<T>>, lambda: Rc<Node<P>>,
out_type: crate::ast::types::StaticType, out_type: crate::ast::types::StaticType,
}, },
Block { Block {
exprs: Vec<Rc<Node<T>>>, exprs: Vec<Rc<Node<P>>>,
}, },
Tuple { Tuple {
elements: Vec<Rc<Node<T>>>, elements: Vec<Rc<Node<P>>>,
}, },
Record { Record {
layout: std::sync::Arc<crate::ast::types::RecordLayout>, 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 { Expansion {
/// The original call from the syntax AST.
original_call: Rc<crate::ast::nodes::SyntaxNode>, original_call: Rc<crate::ast::nodes::SyntaxNode>,
/// The result of binding the expanded AST. bound_expanded: Rc<Node<P>>,
bound_expanded: Rc<Node<T>>,
}, },
/// A diagnostic poison node, allowing compilation to continue after an error.
Error, Error,
Extension(Box<dyn BoundExtension<P>>),
/// DSL-specific extension slot
Extension(Box<dyn BoundExtension<T>>),
} }
impl<T> PartialEq for BoundKind<T> impl<P: CompilerPhase> Clone for BoundKind<P> {
where fn clone(&self) -> Self {
T: PartialEq, 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 { fn eq(&self, other: &Self) -> bool {
match (self, other) { match (self, other) {
(BoundKind::Nop, BoundKind::Nop) => true, (BoundKind::Nop, BoundKind::Nop) => true,
@@ -191,87 +268,32 @@ where
(BoundKind::Get { addr: aa, name: na }, BoundKind::Get { addr: ab, name: nb }) => { (BoundKind::Get { addr: aa, name: na }, BoundKind::Get { addr: ab, name: nb }) => {
aa == ab && na == nb aa == ab && na == nb
} }
( (BoundKind::Set { addr: aa, value: va }, BoundKind::Set { addr: ab, value: vb }) => {
BoundKind::Set { aa == ab && Rc::ptr_eq(va, vb)
addr: aa, }
value: va, (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: 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::FieldAccessor(a), BoundKind::FieldAccessor(b)) => a == b,
( (BoundKind::GetField { rec: ra, field: fa }, BoundKind::GetField { rec: rb, field: fb }) => {
BoundKind::GetField { rec: ra, field: fa }, Rc::ptr_eq(ra, rb) && fa == fb
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) {
BoundKind::If { (Some(a), Some(b)) => Rc::ptr_eq(a, b),
cond: ca, (None, None) => true,
then_br: ta, _ => false,
else_br: ea, }
}, }
BoundKind::If { (BoundKind::Destructure { pattern: pa, value: va }, BoundKind::Destructure { pattern: pb, value: vb }) => {
cond: cb, Rc::ptr_eq(pa, pb) && Rc::ptr_eq(va, vb)
then_br: tb, }
else_br: eb, (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
) => Rc::ptr_eq(ca, cb) && Rc::ptr_eq(ta, tb) && match (ea, eb) { }
(Some(a), Some(b)) => Rc::ptr_eq(a, b), (BoundKind::Call { callee: ca, args: aa }, BoundKind::Call { callee: cb, args: ab }) => {
(None, None) => true, Rc::ptr_eq(ca, cb) && Rc::ptr_eq(aa, ab)
_ => 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::Again { args: aa }, BoundKind::Again { args: ab }) => Rc::ptr_eq(aa, ab),
(BoundKind::Block { exprs: ea }, BoundKind::Block { exprs: eb }) => { (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)) 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 }) => { (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)) 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 }) => {
BoundKind::Record { std::sync::Arc::ptr_eq(la, lb) && va.len() == vb.len() && va.iter().zip(vb.iter()).all(|(a, b)| Rc::ptr_eq(a, b))
layout: la, }
values: va, (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: 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::Error, BoundKind::Error) => true,
(BoundKind::Extension(_), BoundKind::Extension(_)) => false,
_ => false, _ => false,
} }
} }
} }
/// A single field in a Record literal (Key-Value pair) impl<P: CompilerPhase> BoundKind<P> {
pub type RecordField<T> = (Node<T>, Node<T>);
impl<T> BoundKind<T> {
pub fn display_name(&self) -> String { pub fn display_name(&self) -> String {
match self { match self {
BoundKind::Nop => "NOP".to_string(), BoundKind::Nop => "NOP".to_string(),
BoundKind::Constant(v) => format!("CONST({})", v), BoundKind::Constant(v) => format!("CONST({})", v),
BoundKind::Get { addr, name } => format!("GET({}, {:?})", name.name, addr), BoundKind::Get { addr, name } => format!("GET({}, {:?})", name.name, addr),
BoundKind::Set { addr, .. } => format!("SET({:?})", addr), BoundKind::Set { addr, .. } => format!("SET({:?})", addr),
BoundKind::Define { BoundKind::Define { name, addr, kind, .. } => {
name, addr, kind, ..
} => {
let k_str = match kind { let k_str = match kind {
DeclarationKind::Variable => "VAR", DeclarationKind::Variable => "VAR",
DeclarationKind::Parameter => "PARAM", DeclarationKind::Parameter => "PARAM",
@@ -329,9 +331,7 @@ impl<T> BoundKind<T> {
BoundKind::GetField { field, .. } => format!("GET_FIELD(.{})", field.name()), BoundKind::GetField { field, .. } => format!("GET_FIELD(.{})", field.name()),
BoundKind::If { .. } => "IF".to_string(), BoundKind::If { .. } => "IF".to_string(),
BoundKind::Destructure { .. } => "DESTRUCTURE".to_string(), BoundKind::Destructure { .. } => "DESTRUCTURE".to_string(),
BoundKind::Lambda { BoundKind::Lambda { params, upvalues, .. } => {
params, upvalues, ..
} => {
let p_str = match &params.kind { let p_str = match &params.kind {
BoundKind::Tuple { elements } => format!("p:{}", elements.len()), BoundKind::Tuple { elements } => format!("p:{}", elements.len()),
_ => "p:1".to_string(), _ => "p:1".to_string(),
+78 -103
View File
@@ -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 crate::ast::types::Identity;
use std::collections::HashMap; use std::collections::HashMap;
use std::rc::Rc;
pub struct CapturePass; pub struct CapturePass;
impl 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) Self::transform(node, capture_map)
} }
fn transform<T: Clone>(mut node: Node<T>, capture_map: &HashMap<Identity, Vec<Identity>>) -> Node<T> { fn transform<P: CompilerPhase>(mut node: Node<P>, capture_map: &HashMap<Identity, Vec<Identity>>) -> Node<P> {
use std::rc::Rc; node.kind = match node.kind {
match node.kind {
BoundKind::Define { BoundKind::Define {
name, name,
addr, addr,
kind, kind,
value, value,
.. mut captured_by,
} => { } => {
let captured_by = capture_map.get(&node.identity).cloned().unwrap_or_default(); if let Some(capturers) = capture_map.get(&node.identity) {
node.kind = BoundKind::Define { captured_by.extend(capturers.iter().cloned());
}
BoundKind::Define {
name, name,
addr, addr,
kind, kind,
value: Rc::new(Self::transform(value.as_ref().clone(), capture_map)), value: Rc::new(Self::transform(value.as_ref().clone(), capture_map)),
captured_by, 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 { BoundKind::Lambda {
@@ -69,27 +36,38 @@ impl CapturePass {
upvalues, upvalues,
body, body,
positional_count, positional_count,
} => { } => BoundKind::Lambda {
node.kind = BoundKind::Lambda { params: Rc::new(Self::transform(params.as_ref().clone(), capture_map)),
params: Rc::new(Self::transform(params.as_ref().clone(), capture_map)), upvalues,
upvalues, body: Rc::new(Self::transform(body.as_ref().clone(), capture_map)),
body: Rc::new(Self::transform(body.as_ref().clone(), capture_map)), positional_count,
positional_count, },
};
}
BoundKind::Call { callee, args } => { BoundKind::Call { callee, args } => BoundKind::Call {
node.kind = BoundKind::Call { callee: Rc::new(Self::transform(callee.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)),
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 { BoundKind::Pipe {
inputs, inputs,
lambda, lambda,
@@ -99,56 +77,53 @@ impl CapturePass {
for input in inputs { for input in inputs {
t_inputs.push(Rc::new(Self::transform(input.as_ref().clone(), capture_map))); t_inputs.push(Rc::new(Self::transform(input.as_ref().clone(), capture_map)));
} }
node.kind = BoundKind::Pipe { BoundKind::Pipe {
inputs: t_inputs, inputs: t_inputs,
lambda: Rc::new(Self::transform(lambda.as_ref().clone(), capture_map)), lambda: Rc::new(Self::transform(lambda.as_ref().clone(), capture_map)),
out_type: out_type.clone(), out_type,
}; }
}
BoundKind::Block { exprs } => {
node.kind = BoundKind::Block {
exprs: exprs
.into_iter()
.map(|e| Rc::new(Self::transform(e.as_ref().clone(), capture_map)))
.collect(),
};
} }
BoundKind::Tuple { elements } => { BoundKind::Block { exprs } => BoundKind::Block {
node.kind = BoundKind::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::Record { layout, values } => { BoundKind::Tuple { elements } => BoundKind::Tuple {
node.kind = BoundKind::Record { elements: elements
layout, .into_iter()
values: values .map(|e| Rc::new(Self::transform(e.as_ref().clone(), capture_map)))
.into_iter() .collect(),
.map(|v| Rc::new(Self::transform(v.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 { BoundKind::Expansion {
original_call, original_call,
bound_expanded, bound_expanded,
} => { } => BoundKind::Expansion {
node.kind = BoundKind::Expansion { original_call,
original_call, bound_expanded: Rc::new(Self::transform(
bound_expanded: Rc::new(Self::transform(bound_expanded.as_ref().clone(), capture_map)), bound_expanded.as_ref().clone(),
}; capture_map,
} )),
},
BoundKind::Nop BoundKind::GetField { rec, field } => BoundKind::GetField {
| BoundKind::Constant(_) rec: Rc::new(Self::transform(rec.as_ref().clone(), capture_map)),
| BoundKind::Get { .. } field,
| BoundKind::Extension(_) },
| BoundKind::Error => {}
} other => other,
};
node node
} }
} }
+4 -5
View File
@@ -1,5 +1,4 @@
use crate::ast::compiler::bound_nodes::{BoundKind, Node}; use crate::ast::compiler::bound_nodes::{BoundKind, CompilerPhase, Node};
use std::fmt::Debug;
/// Human-readable AST dumper for the bound AST. /// Human-readable AST dumper for the bound AST.
pub struct Dumper { pub struct Dumper {
@@ -9,7 +8,7 @@ pub struct Dumper {
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<T: Debug>(node: &Node<T>) -> 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,
@@ -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.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<T: Debug>(&mut self, node: &Node<T>) { fn visit<P: CompilerPhase>(&mut self, node: &Node<P>) {
match &node.kind { match &node.kind {
BoundKind::Nop => self.log("Nop", node), BoundKind::Nop => self.log("Nop", node),
BoundKind::Constant(v) => { BoundKind::Constant(v) => {
+6 -6
View File
@@ -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::collections::HashMap;
use std::rc::Rc; use std::rc::Rc;
/// A pass that collects all global function definitions (lambdas) into a registry. /// 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. /// This allows the Specializer to retrieve the original AST of a function for monomorphization.
pub struct LambdaCollector<'a, T> { pub struct LambdaCollector<'a, P: CompilerPhase> {
registry: &'a mut HashMap<GlobalIdx, Rc<Node<T>>>, 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. /// 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 }; let mut collector = Self { registry };
collector.visit(node); collector.visit(node);
} }
fn visit(&mut self, node: &Node<T>) { fn visit(&mut self, node: &Node<P>) {
match &node.kind { match &node.kind {
BoundKind::Block { exprs } => { BoundKind::Block { exprs } => {
for expr in exprs { for expr in exprs {
+6 -29
View File
@@ -1,31 +1,7 @@
use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, LocalSlot, Node}; use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, ExecNode, Node, RuntimeMetadata, StackOffset, VirtualId};
use crate::ast::types::StaticType;
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::Debug;
use std::rc::Rc; 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 { struct StackAllocator {
mapping: HashMap<u32, u32>, mapping: HashMap<u32, u32>,
next_slot: 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 entry = self.mapping.entry(slot.0).or_insert_with(|| {
let s = self.next_slot; let s = self.next_slot;
self.next_slot += 1; self.next_slot += 1;
s 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 { match addr {
Address::Local(slot) => Address::Local(self.map_slot(slot)), Address::Local(slot) => Address::Local(self.map_slot(slot)),
other => other, Address::Upvalue(idx) => Address::Upvalue(idx),
Address::Global(idx) => Address::Global(idx),
} }
} }
} }
+1 -1
View File
@@ -769,7 +769,7 @@ mod tests {
locals.insert( locals.insert(
Symbol::from("*"), Symbol::from("*"),
crate::ast::compiler::binder::LocalInfo { 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 { identity: crate::ast::types::NodeIdentity::new(crate::ast::types::SourceLocation {
line: 0, line: 0,
col: 0, col: 0,
+3 -3
View File
@@ -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::types::{Purity, Value};
use crate::ast::vm::Closure; use crate::ast::vm::Closure;
use std::cell::RefCell; 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 { let type_ok = match val {
Value::Int(_) Value::Int(_)
| Value::Float(_) | 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 { match &node.kind {
BoundKind::Define { BoundKind::Define {
addr: Address::Local(slot), addr: Address::Local(slot),
+15 -15
View File
@@ -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 crate::ast::types::Value;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::rc::Rc; use std::rc::Rc;
#[derive(Default)] #[derive(Default)]
pub struct SubstitutionMap { pub struct SubstitutionMap {
pub values: HashMap<Address, Value>, pub values: HashMap<Address<VirtualId>, Value>,
pub ast_substitutions: HashMap<Address, Rc<AnalyzedNode>>, pub ast_substitutions: HashMap<Address<VirtualId>, Rc<AnalyzedNode>>,
pub slot_mapping: HashMap<LocalSlot, LocalSlot>, pub slot_mapping: HashMap<VirtualId, VirtualId>,
pub assigned: HashSet<Address>, pub assigned: HashSet<Address<VirtualId>>,
pub next_slot: u32, pub next_slot: u32,
pub used: HashSet<Address>, pub used: HashSet<Address<VirtualId>>,
pub captured_slots: HashSet<LocalSlot>, pub captured_slots: HashSet<VirtualId>,
} }
impl SubstitutionMap { impl SubstitutionMap {
@@ -43,40 +43,40 @@ impl SubstitutionMap {
inner 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)); 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) { if let Some(&new_slot) = self.slot_mapping.get(&old_slot) {
return new_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.slot_mapping.insert(old_slot, new_slot);
self.next_slot += 1; self.next_slot += 1;
new_slot new_slot
} }
pub fn map_address(&mut self, addr: Address) -> Address { pub fn map_address(&mut self, addr: Address<VirtualId>) -> Address<VirtualId> {
match addr { match addr {
Address::Local(slot) => Address::Local(self.map_slot(slot)), Address::Local(slot) => Address::Local(self.map_slot(slot)),
other => other, 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); 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) 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); 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 if let Address::Upvalue(idx) = addr
&& let Some(res) = mapping.get(idx.0 as usize) && let Some(res) = mapping.get(idx.0 as usize)
&& let Some(new_idx) = res && let Some(new_idx) = res
+5 -5
View File
@@ -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::types::{Identity, Value};
use crate::ast::vm::Closure; use crate::ast::vm::Closure;
use std::collections::HashSet; use std::collections::HashSet;
@@ -32,17 +32,17 @@ impl PathTracker {
// --- UsageInfo --- // --- UsageInfo ---
#[derive(Default)] #[derive(Default)]
pub struct UsageInfo { pub struct UsageInfo {
pub used: HashSet<Address>, pub used: HashSet<Address<VirtualId>>,
pub assigned: HashSet<Address>, pub assigned: HashSet<Address<VirtualId>>,
pub used_identities: HashSet<Identity>, pub used_identities: HashSet<Identity>,
} }
impl UsageInfo { impl UsageInfo {
pub fn is_assigned(&self, addr: &Address) -> bool { pub fn is_assigned(&self, addr: &Address<VirtualId>) -> bool {
self.assigned.contains(addr) 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) self.used.contains(addr)
} }
+5 -5
View File
@@ -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 crate::ast::types::{Purity, Signature, StaticType, Value};
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
@@ -6,16 +6,16 @@ use std::rc::Rc;
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct MonoCacheKey { pub struct MonoCacheKey {
pub address: Address, pub address: Address<VirtualId>,
pub arg_types: Vec<StaticType>, 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 type RtlLookupFunc = Rc<dyn Fn(&str, &[StaticType]) -> Option<(Value, StaticType)>>;
pub trait FunctionRegistry { pub trait FunctionRegistry {
fn resolve(&self, addr: Address) -> Option<Rc<Node>>; fn resolve(&self, addr: Address<VirtualId>) -> Option<Rc<Node<AnalyzedPhase>>>;
fn resolve_analyzed(&self, _addr: Address) -> Option<Rc<AnalyzedNode>> { fn resolve_analyzed(&self, _addr: Address<VirtualId>) -> Option<Rc<AnalyzedNode>> {
None None
} }
} }
+24 -36
View File
@@ -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::diagnostics::Diagnostics;
use crate::ast::types::StaticType; use crate::ast::types::StaticType;
use std::collections::HashMap; 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 { match addr {
Address::Local(slot) => self Address::Local(slot) => self
.slots .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 { match addr {
Address::Local(slot) => { Address::Local(slot) => {
self.slots.insert(slot.0, ty); self.slots.insert(slot.0, ty);
@@ -81,7 +81,18 @@ impl TypeChecker {
pub fn check( pub fn check(
&self, &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], arg_types: &[StaticType],
diag: &mut Diagnostics, diag: &mut Diagnostics,
) -> TypedNode { ) -> TypedNode {
@@ -92,20 +103,17 @@ impl TypeChecker {
body, body,
positional_count, positional_count,
} => { } => {
// 1. Determine types of captured variables (Root lambdas have none)
let mut upvalue_types = Vec::with_capacity(upvalues.len()); let mut upvalue_types = Vec::with_capacity(upvalues.len());
for _ in upvalues { for &_addr in upvalues {
upvalue_types.push(StaticType::Any); upvalue_types.push(StaticType::Any);
} }
// 2. Create the specialized context
let root_ctx = TypeContext::new(0, vec![], &self.root_types, None); let root_ctx = TypeContext::new(0, vec![], &self.root_types, None);
let mut lambda_ctx = let mut lambda_ctx =
TypeContext::new(64, upvalue_types, &self.root_types, Some(&root_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() { let arg_tuple_ty = if arg_types.is_empty() {
StaticType::Any // No specialized info StaticType::Any
} else { } else {
StaticType::Tuple(arg_types.to_vec()) StaticType::Tuple(arg_types.to_vec())
}; };
@@ -117,11 +125,8 @@ impl TypeChecker {
diag, diag,
); );
// 4. Check body with the new types
let body_typed = self.check_node(body, &mut lambda_ctx, diag); let body_typed = self.check_node(body, &mut lambda_ctx, diag);
let ret_ty = body_typed.ty.clone(); let ret_ty = body_typed.ty.clone();
// 5. Construct specialized function type
let final_params_ty = params_typed.ty.clone(); let final_params_ty = params_typed.ty.clone();
let fn_ty = StaticType::Function(Box::new(crate::ast::types::Signature { 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, &self,
node: &Node, node: &Node<P>,
specialized_ty: &StaticType, specialized_ty: &StaticType,
ctx: &mut TypeContext, ctx: &mut TypeContext,
diag: &mut Diagnostics, diag: &mut Diagnostics,
@@ -182,10 +187,6 @@ impl TypeChecker {
addr, addr,
value: _value, 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 { BoundKind::Set {
addr: *addr, addr: *addr,
@@ -240,7 +241,7 @@ impl TypeChecker {
StaticType::Error => StaticType::Error, StaticType::Error => StaticType::Error,
_ => StaticType::Any, _ => 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()); elem_types.push(t.ty.clone());
typed_elements.push(Rc::new(t)); typed_elements.push(Rc::new(t));
} }
@@ -268,9 +269,9 @@ impl TypeChecker {
} }
} }
fn check_node( fn check_node<P: CompilerPhase<LocalAddress = VirtualId>>(
&self, &self,
node: &Node, node: &Node<P>,
ctx: &mut TypeContext, ctx: &mut TypeContext,
diag: &mut Diagnostics, diag: &mut Diagnostics,
) -> TypedNode { ) -> TypedNode {
@@ -373,7 +374,7 @@ impl TypeChecker {
BoundKind::Destructure { pattern, value } => { BoundKind::Destructure { pattern, value } => {
let val_typed = self.check_node(value, ctx, diag); 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(); let ty = val_typed.ty.clone();
( (
BoundKind::Destructure { BoundKind::Destructure {
@@ -397,13 +398,11 @@ impl TypeChecker {
if let Some(e) = else_br { if let Some(e) = else_br {
let et = self.check_node(e, ctx, diag); let et = self.check_node(e, ctx, diag);
// Basic type promotion: if types differ, fall back to Any
if et.ty != final_ty { if et.ty != final_ty {
final_ty = StaticType::Any; final_ty = StaticType::Any;
} }
else_typed = Some(Rc::new(et)); else_typed = Some(Rc::new(et));
} else { } else {
// If without else returns Optional(Then) (T | Void)
final_ty = StaticType::Optional(Box::new(then_typed.ty.clone())); 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()); let mut arg_types = Vec::with_capacity(inputs.len());
for input in inputs { for input in inputs {
let typed_input = self.check_node(input, ctx, diag); 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 { let arg_ty = if let StaticType::Series(inner) = &typed_input.ty {
*inner.clone() *inner.clone()
} else if let StaticType::Stream(inner) = &typed_input.ty { } else if let StaticType::Stream(inner) = &typed_input.ty {
@@ -434,11 +432,9 @@ impl TypeChecker {
typed_inputs.push(Rc::new(typed_input)); typed_inputs.push(Rc::new(typed_input));
} }
// Specialized check for the lambda using the input types! let typed_lambda = self.check_node_as_bound(lambda.as_ref(), &arg_types, diag);
let typed_lambda = self.check(lambda, &arg_types, diag);
let ret_ty = if let StaticType::Function(sig) = &typed_lambda.ty { 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 { if let StaticType::Optional(inner) = &sig.ret {
*inner.clone() *inner.clone()
} else { } else {
@@ -476,17 +472,14 @@ impl TypeChecker {
body, body,
positional_count, positional_count,
} => { } => {
// 1. Determine types of captured variables
let mut upvalue_types = Vec::with_capacity(upvalues.len()); let mut upvalue_types = Vec::with_capacity(upvalues.len());
for &addr in upvalues { for &addr in upvalues {
upvalue_types.push(ctx.get_type(addr)); upvalue_types.push(ctx.get_type(addr));
} }
// 2. Create nested context for lambda body
let mut lambda_ctx = let mut lambda_ctx =
TypeContext::new(64, upvalue_types, ctx.root_types, Some(ctx)); TypeContext::new(64, upvalue_types, ctx.root_types, Some(ctx));
// 3. Check parameters and body
let params_typed = self.check_params( let params_typed = self.check_params(
params.as_ref(), params.as_ref(),
&StaticType::Any, &StaticType::Any,
@@ -494,13 +487,11 @@ impl TypeChecker {
diag, diag,
); );
// Set current params type for 'again' validation
lambda_ctx.current_params_ty = Some(params_typed.ty.clone()); lambda_ctx.current_params_ty = Some(params_typed.ty.clone());
let body_typed = self.check_node(body, &mut lambda_ctx, diag); let body_typed = self.check_node(body, &mut lambda_ctx, diag);
let ret_ty = body_typed.ty.clone(); let ret_ty = body_typed.ty.clone();
// 4. Construct function type
let fn_ty = StaticType::Function(Box::new(crate::ast::types::Signature { let fn_ty = StaticType::Function(Box::new(crate::ast::types::Signature {
params: params_typed.ty.clone(), params: params_typed.ty.clone(),
ret: ret_ty, ret: ret_ty,
@@ -520,7 +511,6 @@ impl TypeChecker {
BoundKind::Call { callee, args } => { BoundKind::Call { callee, args } => {
let callee_typed = self.check_node(callee, ctx, diag); 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 args_typed = if let BoundKind::Tuple { elements } = &args.kind {
let mut typed_elements = Vec::new(); let mut typed_elements = Vec::new();
let mut elem_types = Vec::new(); let mut elem_types = Vec::new();
@@ -537,7 +527,6 @@ impl TypeChecker {
ty: StaticType::Tuple(elem_types), ty: StaticType::Tuple(elem_types),
} }
} else { } else {
// Should not happen as parser wraps args in Tuple, but fallback safely
self.check_node(args, ctx, diag) self.check_node(args, ctx, diag)
}; };
@@ -565,7 +554,6 @@ impl TypeChecker {
} }
BoundKind::Again { args } => { BoundKind::Again { args } => {
// Manually check args (Tuple) to prevent Vector/Matrix promotion
let args_typed = if let BoundKind::Tuple { elements } = &args.kind { let args_typed = if let BoundKind::Tuple { elements } = &args.kind {
let mut typed_elements = Vec::new(); let mut typed_elements = Vec::new();
let mut elem_types = Vec::new(); let mut elem_types = Vec::new();
+30 -23
View File
@@ -10,12 +10,12 @@ 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, GlobalAnalyzedRegistry, GlobalFunctionRegistry, GlobalIdx, Address, AnalyzedNode, BoundKind, ExecNode, GlobalAnalyzedRegistry, GlobalFunctionRegistry, GlobalIdx,
Node, Node, 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;
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::macros::{MacroEvaluator, MacroExpander, MacroRegistry};
use crate::ast::compiler::optimizer::Optimizer; use crate::ast::compiler::optimizer::Optimizer;
use crate::ast::compiler::specializer::{FunctionRegistry, MonoCache, Specializer}; use crate::ast::compiler::specializer::{FunctionRegistry, MonoCache, Specializer};
@@ -89,19 +89,11 @@ pub struct Environment {
} }
struct EnvFunctionRegistry { struct EnvFunctionRegistry {
registry: Rc<RefCell<GlobalFunctionRegistry>>,
analyzed_registry: Rc<RefCell<GlobalAnalyzedRegistry>>, analyzed_registry: Rc<RefCell<GlobalAnalyzedRegistry>>,
} }
impl FunctionRegistry for EnvFunctionRegistry { impl FunctionRegistry for EnvFunctionRegistry {
fn resolve(&self, addr: Address) -> Option<Rc<Node>> { fn resolve(&self, addr: Address<VirtualId>) -> Option<Rc<Node<crate::ast::compiler::bound_nodes::AnalyzedPhase>>> {
if let Address::Global(idx) = addr {
self.registry.borrow().get(&idx).cloned()
} else {
None
}
}
fn resolve_analyzed(&self, addr: Address) -> Option<Rc<AnalyzedNode>> {
if let Address::Global(idx) = addr { if let Address::Global(idx) = addr {
self.analyzed_registry.borrow().get(&idx).cloned() self.analyzed_registry.borrow().get(&idx).cloned()
} else { } else {
@@ -406,7 +398,7 @@ impl Environment {
if !current_scope.locals.contains_key(sym) { if !current_scope.locals.contains_key(sym) {
let mut slot_count = self.root_slot_count.borrow_mut(); 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( current_scope.locals.insert(
sym.clone(), sym.clone(),
crate::ast::compiler::binder::LocalInfo { crate::ast::compiler::binder::LocalInfo {
@@ -484,15 +476,15 @@ 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 crate::ast::compiler::bound_nodes::BoundKind::Lambda { .. } = bound_ast.kind { let wrapped_ast = if let BoundKind::Lambda { .. } = bound_ast.kind {
bound_ast bound_ast
} else { } else {
crate::ast::compiler::bound_nodes::Node { Node {
identity: bound_ast.identity.clone(), identity: bound_ast.identity.clone(),
kind: crate::ast::compiler::bound_nodes::BoundKind::Lambda { kind: BoundKind::Lambda {
params: std::rc::Rc::new(crate::ast::compiler::bound_nodes::Node { params: std::rc::Rc::new(Node {
identity: bound_ast.identity.clone(), identity: bound_ast.identity.clone(),
kind: crate::ast::compiler::bound_nodes::BoundKind::Tuple { elements: vec![] }, kind: BoundKind::Tuple { elements: vec![] },
ty: (), ty: (),
}), }),
upvalues: vec![], upvalues: vec![],
@@ -717,13 +709,11 @@ impl Environment {
fn specialize_node(&self, node: AnalyzedNode) -> AnalyzedNode { fn specialize_node(&self, node: AnalyzedNode) -> AnalyzedNode {
let registry = Rc::new(EnvFunctionRegistry { let registry = Rc::new(EnvFunctionRegistry {
registry: self.function_registry.clone(),
analyzed_registry: self.typed_function_registry.clone(), analyzed_registry: self.typed_function_registry.clone(),
}); });
let rtl_lookup = Rc::new(|name: &str, args: &[StaticType]| intrinsics::lookup(name, args)); let rtl_lookup = Rc::new(|name: &str, args: &[StaticType]| intrinsics::lookup(name, args));
let typed_reg = self.typed_function_registry.clone(); let typed_reg = self.typed_function_registry.clone();
let syntax_reg = self.function_registry.clone();
let mono_cache = self.monomorph_cache.clone(); let mono_cache = self.monomorph_cache.clone();
let root_values = self.root_values.clone(); let root_values = self.root_values.clone();
let root_types = self.root_types.clone(); let root_types = self.root_types.clone();
@@ -731,12 +721,30 @@ impl Environment {
let optimization = self.optimization; let optimization = self.optimization;
let compiler = Rc::new( let compiler = Rc::new(
move |func_template: Rc<Node>, move |func_template: Rc<Node<crate::ast::compiler::bound_nodes::AnalyzedPhase>>,
arg_types: &[StaticType]| arg_types: &[StaticType]|
-> Result<(Value, StaticType), String> { -> Result<(Value, StaticType), String> {
let mut diag = Diagnostics::new(); let mut diag = Diagnostics::new();
let checker = TypeChecker::new(root_types.clone()); 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() { if diag.has_errors() {
return Err(diag return Err(diag
@@ -750,7 +758,6 @@ impl Environment {
let analyzed = Analyzer::analyze(&retyped_ast, &root_purity.borrow()); let analyzed = Analyzer::analyze(&retyped_ast, &root_purity.borrow());
let sub_registry = Rc::new(EnvFunctionRegistry { let sub_registry = Rc::new(EnvFunctionRegistry {
registry: syntax_reg.clone(),
analyzed_registry: typed_reg.clone(), analyzed_registry: typed_reg.clone(),
}); });
let sub_rtl_lookup = let sub_rtl_lookup =
+7 -8
View File
@@ -1,5 +1,4 @@
use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind}; use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, BoundKind, ExecNode, StackOffset};
use crate::ast::compiler::lowering::ExecNode;
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;
@@ -335,7 +334,7 @@ impl VM {
val.clone() 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. // Define always evaluates to the unwrapped value for immediate use.
Ok(val) Ok(val)
} }
@@ -351,7 +350,7 @@ impl VM {
} }
Ok(val) 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())), BoundKind::FieldAccessor(k) => Ok(Value::FieldAccessor(k.clone())),
@@ -399,7 +398,7 @@ impl VM {
BoundKind::Set { addr, value } => { BoundKind::Set { addr, value } => {
let val = self.eval_internal(obs, value)?; let val = self.eval_internal(obs, value)?;
self.set_value(addr.clone(), val.clone())?; self.set_value(*addr, val.clone())?;
Ok(val) Ok(val)
} }
BoundKind::If { 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 { match addr {
Address::Local(slot) => { Address::Local(slot) => {
let frame = self.frames.last().ok_or("No call frame")?; 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 { match addr {
Address::Local(slot) => { Address::Local(slot) => {
let frame = self.frames.last().ok_or("No call frame")?; 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 { match addr {
Address::Local(slot) => { Address::Local(slot) => {
let frame = self.frames.last().ok_or("No call frame")?; let frame = self.frames.last().ok_or("No call frame")?;