Refactor: Move compiler node definitions to src/ast/nodes
The `bound_nodes.rs` file has been removed and its contents have been moved to `src/ast/nodes.rs`. This consolidates all AST node definitions into a single module, improving organization and maintainability. The `compiler` modules now import these definitions from `crate::ast::nodes` instead of `crate::ast::compiler::bound_nodes`.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
use crate::ast::compiler::bound_nodes::{
|
||||
use crate::ast::nodes::{
|
||||
Address, AnalyzedNode, GlobalIdx, IdentifierBinding, Node,
|
||||
NodeKind, NodeMetrics, TypedNode, TypedPhase,
|
||||
};
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use crate::ast::compiler::bound_nodes::{
|
||||
use crate::ast::nodes::{
|
||||
Address, AssignBinding, BoundPhase, DeclarationKind, DefBinding, GlobalIdx,
|
||||
IdentifierBinding, LambdaBinding, Node, NodeKind, UpvalueIdx, VirtualId,
|
||||
IdentifierBinding, LambdaBinding, Node, NodeKind, SyntaxKind, SyntaxNode, Symbol,
|
||||
UpvalueIdx, VirtualId,
|
||||
};
|
||||
use crate::ast::diagnostics::Diagnostics;
|
||||
use crate::ast::nodes::{SyntaxKind, SyntaxNode, Symbol};
|
||||
use crate::ast::types::{Identity, Purity, StaticType};
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
@@ -1,524 +0,0 @@
|
||||
use crate::ast::nodes::Symbol;
|
||||
use crate::ast::types::{Identity, StaticType, Value};
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct VirtualId(pub u32);
|
||||
|
||||
impl std::fmt::Display for VirtualId {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct UpvalueIdx(pub u32);
|
||||
|
||||
impl std::fmt::Display for UpvalueIdx {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "U{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct GlobalIdx(pub u32);
|
||||
|
||||
impl std::fmt::Display for GlobalIdx {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "G{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[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,
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/// Semantic role and address of an identifier (reference vs. declaration).
|
||||
type Binding: std::fmt::Debug + Clone + PartialEq;
|
||||
/// Binding metadata for `def` nodes (captured_by info).
|
||||
type DefInfo: std::fmt::Debug + Clone + PartialEq;
|
||||
/// Target address for `assign` nodes.
|
||||
type AssignInfo: std::fmt::Debug + Clone + PartialEq;
|
||||
/// Closure metadata for `lambda` nodes (upvalues, positional count).
|
||||
type LambdaInfo: std::fmt::Debug + Clone + PartialEq;
|
||||
/// Record field layout for O(1) access.
|
||||
type RecordLayout: std::fmt::Debug + Clone + PartialEq;
|
||||
}
|
||||
|
||||
// ── Phase-specific binding info types ──────────────────────────────────────────
|
||||
|
||||
/// Semantic role of an identifier after binding.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum IdentifierBinding<L = VirtualId> {
|
||||
/// A variable reference (read access).
|
||||
Reference(Address<L>),
|
||||
/// A variable or parameter declaration (write/bind target).
|
||||
Declaration {
|
||||
addr: Address<L>,
|
||||
kind: DeclarationKind,
|
||||
},
|
||||
}
|
||||
|
||||
/// Binding metadata attached to `def` nodes.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct DefBinding {
|
||||
/// Identities of lambdas that capture this definition's variable.
|
||||
pub captured_by: Vec<Identity>,
|
||||
}
|
||||
|
||||
/// Target address attached to `assign` nodes.
|
||||
/// `Some(addr)` for simple assignment, `None` for destructuring assignment
|
||||
/// (where addresses live on the individual `Identifier` nodes in the target pattern).
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct AssignBinding<L = VirtualId> {
|
||||
pub addr: Option<Address<L>>,
|
||||
}
|
||||
|
||||
/// Closure metadata attached to `lambda` nodes.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct LambdaBinding<L = VirtualId> {
|
||||
/// Addresses of captured variables from enclosing scopes.
|
||||
pub upvalues: Vec<Address<L>>,
|
||||
/// Number of positional parameters (None = variadic).
|
||||
pub positional_count: Option<u32>,
|
||||
}
|
||||
|
||||
// ── SyntaxPhase ────────────────────────────────────────────────────────────────
|
||||
|
||||
/// The initial phase produced by the parser. All annotation slots are `()`.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct SyntaxPhase;
|
||||
impl CompilerPhase for SyntaxPhase {
|
||||
type Metadata = ();
|
||||
type LocalAddress = ();
|
||||
type Binding = ();
|
||||
type DefInfo = ();
|
||||
type AssignInfo = ();
|
||||
type LambdaInfo = ();
|
||||
type RecordLayout = ();
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct BoundPhase;
|
||||
impl CompilerPhase for BoundPhase {
|
||||
type Metadata = ();
|
||||
type LocalAddress = VirtualId;
|
||||
type Binding = IdentifierBinding<VirtualId>;
|
||||
type DefInfo = DefBinding;
|
||||
type AssignInfo = AssignBinding<VirtualId>;
|
||||
type LambdaInfo = LambdaBinding<VirtualId>;
|
||||
type RecordLayout = Arc<crate::ast::types::RecordLayout>;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct TypedPhase;
|
||||
impl CompilerPhase for TypedPhase {
|
||||
type Metadata = StaticType;
|
||||
type LocalAddress = VirtualId;
|
||||
type Binding = IdentifierBinding<VirtualId>;
|
||||
type DefInfo = DefBinding;
|
||||
type AssignInfo = AssignBinding<VirtualId>;
|
||||
type LambdaInfo = LambdaBinding<VirtualId>;
|
||||
type RecordLayout = Arc<crate::ast::types::RecordLayout>;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct AnalyzedPhase;
|
||||
impl CompilerPhase for AnalyzedPhase {
|
||||
type Metadata = NodeMetrics;
|
||||
type LocalAddress = VirtualId;
|
||||
type Binding = IdentifierBinding<VirtualId>;
|
||||
type DefInfo = DefBinding;
|
||||
type AssignInfo = AssignBinding<VirtualId>;
|
||||
type LambdaInfo = LambdaBinding<VirtualId>;
|
||||
type RecordLayout = Arc<crate::ast::types::RecordLayout>;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct RuntimePhase;
|
||||
impl CompilerPhase for RuntimePhase {
|
||||
type Metadata = RuntimeMetadata;
|
||||
type LocalAddress = StackOffset;
|
||||
type Binding = IdentifierBinding<StackOffset>;
|
||||
type DefInfo = DefBinding;
|
||||
type AssignInfo = AssignBinding<StackOffset>;
|
||||
type LambdaInfo = LambdaBinding<StackOffset>;
|
||||
type RecordLayout = Arc<crate::ast::types::RecordLayout>;
|
||||
}
|
||||
|
||||
/// Convenience alias for all post-binding phases that share the same
|
||||
/// concrete binding, def, assign, lambda, and record-layout types.
|
||||
/// Covers `BoundPhase`, `TypedPhase`, `AnalyzedPhase` (but not `SyntaxPhase` or `RuntimePhase`).
|
||||
pub trait BoundLike:
|
||||
CompilerPhase<
|
||||
LocalAddress = VirtualId,
|
||||
Binding = IdentifierBinding<VirtualId>,
|
||||
DefInfo = DefBinding,
|
||||
AssignInfo = AssignBinding<VirtualId>,
|
||||
LambdaInfo = LambdaBinding<VirtualId>,
|
||||
RecordLayout = Arc<crate::ast::types::RecordLayout>,
|
||||
>
|
||||
{
|
||||
}
|
||||
|
||||
impl<P> BoundLike for P where
|
||||
P: CompilerPhase<
|
||||
LocalAddress = VirtualId,
|
||||
Binding = IdentifierBinding<VirtualId>,
|
||||
DefInfo = DefBinding,
|
||||
AssignInfo = AssignBinding<VirtualId>,
|
||||
LambdaInfo = LambdaBinding<VirtualId>,
|
||||
RecordLayout = Arc<crate::ast::types::RecordLayout>,
|
||||
>
|
||||
{
|
||||
}
|
||||
|
||||
/// A unified AST node, decorated with phase-specific information P.
|
||||
/// Replaces both `SyntaxNode` (parser output) and the old bound AST node.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct Node<P: CompilerPhase = BoundPhase> {
|
||||
pub identity: Identity,
|
||||
pub kind: NodeKind<P>,
|
||||
pub ty: P::Metadata,
|
||||
}
|
||||
|
||||
impl<P: CompilerPhase> Clone for Node<P> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
identity: self.identity.clone(),
|
||||
kind: self.kind.clone(),
|
||||
ty: self.ty.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type TypedNode = Node<TypedPhase>;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct NodeMetrics {
|
||||
pub original: Rc<TypedNode>,
|
||||
pub purity: crate::ast::types::Purity,
|
||||
pub is_recursive: bool,
|
||||
}
|
||||
|
||||
pub type AnalyzedNode = Node<AnalyzedPhase>;
|
||||
|
||||
#[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 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()
|
||||
}
|
||||
}
|
||||
|
||||
// ── NodeKind<P>: Unified AST node kind ─────────────────────────────────────────
|
||||
|
||||
/// A key-value pair in a record literal: `(key_node, value_node)`.
|
||||
pub type RecordFieldPair<P> = (Rc<Node<P>>, Rc<Node<P>>);
|
||||
|
||||
/// Unified AST node kind, replacing both `SyntaxKind` and `BoundKind<P>`.
|
||||
///
|
||||
/// The structure is always consistent with the syntax the user wrote.
|
||||
/// Phase-specific information is carried in `P::*` annotation slots,
|
||||
/// which are `()` in `SyntaxPhase` and concrete types in later phases.
|
||||
#[derive(Debug)]
|
||||
pub enum NodeKind<P: CompilerPhase = SyntaxPhase> {
|
||||
Nop,
|
||||
Constant(Value),
|
||||
Identifier {
|
||||
symbol: Symbol,
|
||||
binding: P::Binding,
|
||||
},
|
||||
FieldAccessor(crate::ast::types::Keyword),
|
||||
If {
|
||||
cond: Rc<Node<P>>,
|
||||
then_br: Rc<Node<P>>,
|
||||
else_br: Option<Rc<Node<P>>>,
|
||||
},
|
||||
Def {
|
||||
pattern: Rc<Node<P>>,
|
||||
value: Rc<Node<P>>,
|
||||
info: P::DefInfo,
|
||||
},
|
||||
Assign {
|
||||
target: Rc<Node<P>>,
|
||||
value: Rc<Node<P>>,
|
||||
info: P::AssignInfo,
|
||||
},
|
||||
Lambda {
|
||||
params: Rc<Node<P>>,
|
||||
body: Rc<Node<P>>,
|
||||
info: P::LambdaInfo,
|
||||
},
|
||||
Call {
|
||||
callee: Rc<Node<P>>,
|
||||
args: Rc<Node<P>>,
|
||||
},
|
||||
Again {
|
||||
args: Rc<Node<P>>,
|
||||
},
|
||||
Pipe {
|
||||
inputs: Vec<Rc<Node<P>>>,
|
||||
lambda: Rc<Node<P>>,
|
||||
},
|
||||
Block {
|
||||
exprs: Vec<Rc<Node<P>>>,
|
||||
},
|
||||
Tuple {
|
||||
elements: Vec<Rc<Node<P>>>,
|
||||
},
|
||||
Record {
|
||||
fields: Vec<RecordFieldPair<P>>,
|
||||
layout: P::RecordLayout,
|
||||
},
|
||||
/// Macro declaration (only valid in `SyntaxPhase`).
|
||||
MacroDecl {
|
||||
name: Symbol,
|
||||
params: Rc<Node<P>>,
|
||||
body: Rc<Node<P>>,
|
||||
},
|
||||
/// Quasiquote template (only valid in `SyntaxPhase`).
|
||||
Template(Rc<Node<P>>),
|
||||
/// Unquote placeholder inside a template (only valid in `SyntaxPhase`).
|
||||
Placeholder(Rc<Node<P>>),
|
||||
/// Splice placeholder inside a template (only valid in `SyntaxPhase`).
|
||||
Splice(Rc<Node<P>>),
|
||||
/// Optimized field access (only created during lowering for `RuntimePhase`).
|
||||
GetField {
|
||||
rec: Rc<Node<P>>,
|
||||
field: crate::ast::types::Keyword,
|
||||
},
|
||||
/// Expanded macro call, preserving the original call for debugging.
|
||||
Expansion {
|
||||
original_call: Rc<Node<SyntaxPhase>>,
|
||||
expanded: Rc<Node<P>>,
|
||||
},
|
||||
Error,
|
||||
Extension(Box<dyn BoundExtension<P>>),
|
||||
}
|
||||
|
||||
impl<P: CompilerPhase> Clone for NodeKind<P> {
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
NodeKind::Nop => NodeKind::Nop,
|
||||
NodeKind::Constant(v) => NodeKind::Constant(v.clone()),
|
||||
NodeKind::Identifier { symbol, binding } => NodeKind::Identifier {
|
||||
symbol: symbol.clone(),
|
||||
binding: binding.clone(),
|
||||
},
|
||||
NodeKind::FieldAccessor(k) => NodeKind::FieldAccessor(*k),
|
||||
NodeKind::If { cond, then_br, else_br } => NodeKind::If {
|
||||
cond: cond.clone(),
|
||||
then_br: then_br.clone(),
|
||||
else_br: else_br.clone(),
|
||||
},
|
||||
NodeKind::Def { pattern, value, info } => NodeKind::Def {
|
||||
pattern: pattern.clone(),
|
||||
value: value.clone(),
|
||||
info: info.clone(),
|
||||
},
|
||||
NodeKind::Assign { target, value, info } => NodeKind::Assign {
|
||||
target: target.clone(),
|
||||
value: value.clone(),
|
||||
info: info.clone(),
|
||||
},
|
||||
NodeKind::Lambda { params, body, info } => NodeKind::Lambda {
|
||||
params: params.clone(),
|
||||
body: body.clone(),
|
||||
info: info.clone(),
|
||||
},
|
||||
NodeKind::Call { callee, args } => NodeKind::Call {
|
||||
callee: callee.clone(),
|
||||
args: args.clone(),
|
||||
},
|
||||
NodeKind::Again { args } => NodeKind::Again { args: args.clone() },
|
||||
NodeKind::Pipe { inputs, lambda } => NodeKind::Pipe {
|
||||
inputs: inputs.clone(),
|
||||
lambda: lambda.clone(),
|
||||
},
|
||||
NodeKind::Block { exprs } => NodeKind::Block { exprs: exprs.clone() },
|
||||
NodeKind::Tuple { elements } => NodeKind::Tuple { elements: elements.clone() },
|
||||
NodeKind::Record { fields, layout } => NodeKind::Record {
|
||||
fields: fields.clone(),
|
||||
layout: layout.clone(),
|
||||
},
|
||||
NodeKind::GetField { rec, field } => NodeKind::GetField {
|
||||
rec: rec.clone(),
|
||||
field: *field,
|
||||
},
|
||||
NodeKind::MacroDecl { name, params, body } => NodeKind::MacroDecl {
|
||||
name: name.clone(),
|
||||
params: params.clone(),
|
||||
body: body.clone(),
|
||||
},
|
||||
NodeKind::Template(inner) => NodeKind::Template(inner.clone()),
|
||||
NodeKind::Placeholder(inner) => NodeKind::Placeholder(inner.clone()),
|
||||
NodeKind::Splice(inner) => NodeKind::Splice(inner.clone()),
|
||||
NodeKind::Expansion { original_call, expanded } => NodeKind::Expansion {
|
||||
original_call: original_call.clone(),
|
||||
expanded: expanded.clone(),
|
||||
},
|
||||
NodeKind::Error => NodeKind::Error,
|
||||
NodeKind::Extension(ext) => NodeKind::Extension(ext.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: CompilerPhase> PartialEq for NodeKind<P> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
match (self, other) {
|
||||
(NodeKind::Nop, NodeKind::Nop) => true,
|
||||
(NodeKind::Constant(a), NodeKind::Constant(b)) => a == b,
|
||||
(NodeKind::Identifier { symbol: sa, binding: ba }, NodeKind::Identifier { symbol: sb, binding: bb }) => {
|
||||
sa == sb && ba == bb
|
||||
}
|
||||
(NodeKind::FieldAccessor(a), NodeKind::FieldAccessor(b)) => a == b,
|
||||
(NodeKind::If { cond: ca, then_br: ta, else_br: ea }, NodeKind::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,
|
||||
}
|
||||
}
|
||||
(NodeKind::Def { pattern: pa, value: va, info: ia }, NodeKind::Def { pattern: pb, value: vb, info: ib }) => {
|
||||
Rc::ptr_eq(pa, pb) && Rc::ptr_eq(va, vb) && ia == ib
|
||||
}
|
||||
(NodeKind::Assign { target: ta, value: va, info: ia }, NodeKind::Assign { target: tb, value: vb, info: ib }) => {
|
||||
Rc::ptr_eq(ta, tb) && Rc::ptr_eq(va, vb) && ia == ib
|
||||
}
|
||||
(NodeKind::Lambda { params: pa, body: ba, info: ia }, NodeKind::Lambda { params: pb, body: bb, info: ib }) => {
|
||||
Rc::ptr_eq(pa, pb) && Rc::ptr_eq(ba, bb) && ia == ib
|
||||
}
|
||||
(NodeKind::Call { callee: ca, args: aa }, NodeKind::Call { callee: cb, args: ab }) => {
|
||||
Rc::ptr_eq(ca, cb) && Rc::ptr_eq(aa, ab)
|
||||
}
|
||||
(NodeKind::Again { args: aa }, NodeKind::Again { args: ab }) => Rc::ptr_eq(aa, ab),
|
||||
(NodeKind::Pipe { inputs: ia, lambda: la }, NodeKind::Pipe { inputs: ib, lambda: lb }) => {
|
||||
ia.len() == ib.len()
|
||||
&& ia.iter().zip(ib.iter()).all(|(a, b)| Rc::ptr_eq(a, b))
|
||||
&& Rc::ptr_eq(la, lb)
|
||||
}
|
||||
(NodeKind::Block { exprs: ea }, NodeKind::Block { exprs: eb }) => {
|
||||
ea.len() == eb.len() && ea.iter().zip(eb.iter()).all(|(a, b)| Rc::ptr_eq(a, b))
|
||||
}
|
||||
(NodeKind::Tuple { elements: ea }, NodeKind::Tuple { elements: eb }) => {
|
||||
ea.len() == eb.len() && ea.iter().zip(eb.iter()).all(|(a, b)| Rc::ptr_eq(a, b))
|
||||
}
|
||||
(NodeKind::Record { fields: fa, layout: la }, NodeKind::Record { fields: fb, layout: lb }) => {
|
||||
la == lb
|
||||
&& fa.len() == fb.len()
|
||||
&& fa.iter().zip(fb.iter()).all(|((ka, va), (kb, vb))| Rc::ptr_eq(ka, kb) && Rc::ptr_eq(va, vb))
|
||||
}
|
||||
(NodeKind::GetField { rec: ra, field: fa }, NodeKind::GetField { rec: rb, field: fb }) => {
|
||||
Rc::ptr_eq(ra, rb) && fa == fb
|
||||
}
|
||||
(NodeKind::MacroDecl { name: na, params: pa, body: ba }, NodeKind::MacroDecl { name: nb, params: pb, body: bb }) => {
|
||||
na == nb && Rc::ptr_eq(pa, pb) && Rc::ptr_eq(ba, bb)
|
||||
}
|
||||
(NodeKind::Template(a), NodeKind::Template(b)) => Rc::ptr_eq(a, b),
|
||||
(NodeKind::Placeholder(a), NodeKind::Placeholder(b)) => Rc::ptr_eq(a, b),
|
||||
(NodeKind::Splice(a), NodeKind::Splice(b)) => Rc::ptr_eq(a, b),
|
||||
(NodeKind::Expansion { original_call: ca, expanded: ea }, NodeKind::Expansion { original_call: cb, expanded: eb }) => {
|
||||
Rc::ptr_eq(ca, cb) && Rc::ptr_eq(ea, eb)
|
||||
}
|
||||
(NodeKind::Error, NodeKind::Error) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: CompilerPhase> NodeKind<P> {
|
||||
pub fn display_name(&self) -> String {
|
||||
match self {
|
||||
NodeKind::Nop => "NOP".to_string(),
|
||||
NodeKind::Constant(v) => format!("CONST({})", v),
|
||||
NodeKind::Identifier { symbol, .. } => format!("ID({})", symbol.name),
|
||||
NodeKind::FieldAccessor(k) => format!("FIELD_ACCESSOR(.{})", k.name()),
|
||||
NodeKind::If { .. } => "IF".to_string(),
|
||||
NodeKind::Def { .. } => "DEF".to_string(),
|
||||
NodeKind::Assign { .. } => "ASSIGN".to_string(),
|
||||
NodeKind::Lambda { .. } => "LAMBDA".to_string(),
|
||||
NodeKind::Call { .. } => "CALL".to_string(),
|
||||
NodeKind::Again { .. } => "AGAIN".to_string(),
|
||||
NodeKind::Pipe { .. } => "PIPE".to_string(),
|
||||
NodeKind::Block { .. } => "BLOCK".to_string(),
|
||||
NodeKind::Tuple { elements } => format!("TUPLE({})", elements.len()),
|
||||
NodeKind::Record { fields, .. } => format!("RECORD({})", fields.len()),
|
||||
NodeKind::GetField { field, .. } => format!("GET_FIELD(.{})", field.name()),
|
||||
NodeKind::MacroDecl { name, .. } => format!("MACRO({})", name.name),
|
||||
NodeKind::Template(_) => "TEMPLATE".to_string(),
|
||||
NodeKind::Placeholder(_) => "PLACEHOLDER".to_string(),
|
||||
NodeKind::Splice(_) => "SPLICE".to_string(),
|
||||
NodeKind::Expansion { .. } => "EXPANSION".to_string(),
|
||||
NodeKind::Extension(ext) => ext.display_name(),
|
||||
NodeKind::Error => "ERROR".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::ast::compiler::bound_nodes::{CompilerPhase, DefBinding, Node, NodeKind};
|
||||
use crate::ast::nodes::{CompilerPhase, DefBinding, Node, NodeKind};
|
||||
use crate::ast::types::Identity;
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::ast::compiler::bound_nodes::{CompilerPhase, Node, NodeKind};
|
||||
use crate::ast::nodes::{CompilerPhase, Node, NodeKind};
|
||||
|
||||
/// Human-readable AST dumper for the bound AST.
|
||||
pub struct Dumper {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::ast::compiler::bound_nodes::{
|
||||
use crate::ast::nodes::{
|
||||
Address, BoundLike, GlobalIdx, IdentifierBinding, Node, NodeKind,
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::ast::compiler::bound_nodes::{
|
||||
use crate::ast::nodes::{
|
||||
Address, AnalyzedNode, AssignBinding, ExecNode,
|
||||
IdentifierBinding, LambdaBinding, Node, NodeKind, RuntimeMetadata,
|
||||
StackOffset, VirtualId,
|
||||
|
||||
@@ -642,7 +642,7 @@ impl<E: MacroEvaluator> MacroExpander<E> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::ast::compiler::bound_nodes::Address;
|
||||
use crate::ast::nodes::Address;
|
||||
use crate::ast::compiler::Binder;
|
||||
use crate::ast::parser::Parser;
|
||||
use crate::ast::types::{Object, Value};
|
||||
@@ -788,7 +788,7 @@ mod tests {
|
||||
locals.insert(
|
||||
Symbol::from("*"),
|
||||
crate::ast::compiler::binder::LocalInfo {
|
||||
addr: Address::Local(crate::ast::compiler::bound_nodes::VirtualId(0)),
|
||||
addr: Address::Local(crate::ast::nodes::VirtualId(0)),
|
||||
identity: crate::ast::types::NodeIdentity::new(crate::ast::types::SourceLocation {
|
||||
line: 0,
|
||||
col: 0,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
pub mod analyzer;
|
||||
pub mod binder;
|
||||
pub mod bound_nodes;
|
||||
pub mod captures;
|
||||
pub mod dumper;
|
||||
pub mod lambda_collector;
|
||||
@@ -10,8 +9,8 @@ pub mod specializer;
|
||||
pub mod lowering;
|
||||
pub mod type_checker;
|
||||
|
||||
pub use crate::ast::nodes::*;
|
||||
pub use binder::*;
|
||||
pub use bound_nodes::*;
|
||||
pub use captures::*;
|
||||
pub use dumper::*;
|
||||
pub use macros::*;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::ast::compiler::bound_nodes::{
|
||||
use crate::ast::nodes::{
|
||||
Address, AnalyzedNode, AssignBinding, GlobalAnalyzedRegistry,
|
||||
IdentifierBinding, LambdaBinding, Node, NodeKind, UpvalueIdx,
|
||||
};
|
||||
@@ -756,7 +756,7 @@ impl Optimizer {
|
||||
}
|
||||
|
||||
/// Extracts the address from a Def pattern node (when it's a simple Identifier with Declaration binding).
|
||||
fn extract_def_addr(pattern: &AnalyzedNode) -> Option<Address<crate::ast::compiler::bound_nodes::VirtualId>> {
|
||||
fn extract_def_addr(pattern: &AnalyzedNode) -> Option<Address<crate::ast::nodes::VirtualId>> {
|
||||
if let NodeKind::Identifier {
|
||||
binding: IdentifierBinding::Declaration { addr, .. },
|
||||
..
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::ast::compiler::bound_nodes::{
|
||||
use crate::ast::nodes::{
|
||||
Address, AnalyzedNode, IdentifierBinding, Node, NodeKind, NodeMetrics,
|
||||
};
|
||||
use crate::ast::types::{Purity, RecordLayout, StaticType, Value};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::ast::compiler::bound_nodes::{
|
||||
use crate::ast::nodes::{
|
||||
Address, AnalyzedNode, IdentifierBinding, NodeKind, VirtualId,
|
||||
};
|
||||
use crate::ast::types::{Purity, Value};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::ast::compiler::bound_nodes::{
|
||||
use crate::ast::nodes::{
|
||||
Address, AnalyzedNode, AssignBinding, IdentifierBinding,
|
||||
LambdaBinding, Node, NodeKind, UpvalueIdx, VirtualId,
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::ast::compiler::bound_nodes::{
|
||||
use crate::ast::nodes::{
|
||||
Address, AnalyzedNode, GlobalIdx, IdentifierBinding,
|
||||
NodeKind, VirtualId,
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::ast::compiler::bound_nodes::{
|
||||
use crate::ast::nodes::{
|
||||
Address, AnalyzedNode, AnalyzedPhase, IdentifierBinding, Node, NodeKind, NodeMetrics, VirtualId,
|
||||
};
|
||||
use crate::ast::types::{Purity, Signature, StaticType, Value};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::ast::compiler::bound_nodes::{
|
||||
use crate::ast::nodes::{
|
||||
Address, AssignBinding, BoundLike, DefBinding, IdentifierBinding, LambdaBinding, Node,
|
||||
NodeKind, TypedNode, TypedPhase, VirtualId,
|
||||
};
|
||||
@@ -84,7 +84,7 @@ impl TypeChecker {
|
||||
|
||||
pub fn check(
|
||||
&self,
|
||||
node: &Node<crate::ast::compiler::bound_nodes::BoundPhase>,
|
||||
node: &Node<crate::ast::nodes::BoundPhase>,
|
||||
arg_types: &[StaticType],
|
||||
diag: &mut Diagnostics,
|
||||
) -> TypedNode {
|
||||
|
||||
@@ -9,7 +9,7 @@ use std::collections::{HashMap, HashSet};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::ast::compiler::bound_nodes::{
|
||||
use crate::ast::nodes::{
|
||||
Address, AnalyzedNode, ExecNode, GlobalAnalyzedRegistry, GlobalFunctionRegistry, GlobalIdx,
|
||||
LambdaBinding, Node, NodeKind, VirtualId,
|
||||
};
|
||||
@@ -93,7 +93,7 @@ struct EnvFunctionRegistry {
|
||||
}
|
||||
|
||||
impl FunctionRegistry for EnvFunctionRegistry {
|
||||
fn resolve(&self, addr: Address<VirtualId>) -> Option<Rc<Node<crate::ast::compiler::bound_nodes::AnalyzedPhase>>> {
|
||||
fn resolve(&self, addr: Address<VirtualId>) -> Option<Rc<Node<crate::ast::nodes::AnalyzedPhase>>> {
|
||||
if let Address::Global(idx) = addr {
|
||||
self.analyzed_registry.borrow().get(&idx).cloned()
|
||||
} else {
|
||||
@@ -722,7 +722,7 @@ impl Environment {
|
||||
let optimization = self.optimization;
|
||||
|
||||
let compiler = Rc::new(
|
||||
move |func_template: Rc<Node<crate::ast::compiler::bound_nodes::AnalyzedPhase>>,
|
||||
move |func_template: Rc<Node<crate::ast::nodes::AnalyzedPhase>>,
|
||||
arg_types: &[StaticType]|
|
||||
-> Result<(Value, StaticType), String> {
|
||||
let mut diag = Diagnostics::new();
|
||||
|
||||
+536
-3
@@ -1,8 +1,10 @@
|
||||
use crate::ast::compiler::bound_nodes::{Node, NodeKind, SyntaxPhase};
|
||||
use crate::ast::types::{Identity, Object};
|
||||
use crate::ast::types::{Identity, Object, StaticType, Value};
|
||||
use std::any::Any;
|
||||
use std::fmt::Debug;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
// ── Symbol ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
/// A name with an optional context for macro hygiene.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
@@ -31,6 +33,236 @@ impl From<&str> for Symbol {
|
||||
}
|
||||
}
|
||||
|
||||
// ── Address types ──────────────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct VirtualId(pub u32);
|
||||
|
||||
impl std::fmt::Display for VirtualId {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct UpvalueIdx(pub u32);
|
||||
|
||||
impl std::fmt::Display for UpvalueIdx {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "U{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct GlobalIdx(pub u32);
|
||||
|
||||
impl std::fmt::Display for GlobalIdx {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "G{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[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,
|
||||
}
|
||||
|
||||
// ── CompilerPhase trait ────────────────────────────────────────────────────────
|
||||
|
||||
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;
|
||||
|
||||
/// Semantic role and address of an identifier (reference vs. declaration).
|
||||
type Binding: std::fmt::Debug + Clone + PartialEq;
|
||||
/// Binding metadata for `def` nodes (captured_by info).
|
||||
type DefInfo: std::fmt::Debug + Clone + PartialEq;
|
||||
/// Target address for `assign` nodes.
|
||||
type AssignInfo: std::fmt::Debug + Clone + PartialEq;
|
||||
/// Closure metadata for `lambda` nodes (upvalues, positional count).
|
||||
type LambdaInfo: std::fmt::Debug + Clone + PartialEq;
|
||||
/// Record field layout for O(1) access.
|
||||
type RecordLayout: std::fmt::Debug + Clone + PartialEq;
|
||||
}
|
||||
|
||||
// ── Phase-specific binding info types ──────────────────────────────────────────
|
||||
|
||||
/// Semantic role of an identifier after binding.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum IdentifierBinding<L = VirtualId> {
|
||||
/// A variable reference (read access).
|
||||
Reference(Address<L>),
|
||||
/// A variable or parameter declaration (write/bind target).
|
||||
Declaration {
|
||||
addr: Address<L>,
|
||||
kind: DeclarationKind,
|
||||
},
|
||||
}
|
||||
|
||||
/// Binding metadata attached to `def` nodes.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct DefBinding {
|
||||
/// Identities of lambdas that capture this definition's variable.
|
||||
pub captured_by: Vec<Identity>,
|
||||
}
|
||||
|
||||
/// Target address attached to `assign` nodes.
|
||||
/// `Some(addr)` for simple assignment, `None` for destructuring assignment
|
||||
/// (where addresses live on the individual `Identifier` nodes in the target pattern).
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct AssignBinding<L = VirtualId> {
|
||||
pub addr: Option<Address<L>>,
|
||||
}
|
||||
|
||||
/// Closure metadata attached to `lambda` nodes.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct LambdaBinding<L = VirtualId> {
|
||||
/// Addresses of captured variables from enclosing scopes.
|
||||
pub upvalues: Vec<Address<L>>,
|
||||
/// Number of positional parameters (None = variadic).
|
||||
pub positional_count: Option<u32>,
|
||||
}
|
||||
|
||||
// ── Compiler phases ────────────────────────────────────────────────────────────
|
||||
|
||||
/// The initial phase produced by the parser. All annotation slots are `()`.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct SyntaxPhase;
|
||||
impl CompilerPhase for SyntaxPhase {
|
||||
type Metadata = ();
|
||||
type LocalAddress = ();
|
||||
type Binding = ();
|
||||
type DefInfo = ();
|
||||
type AssignInfo = ();
|
||||
type LambdaInfo = ();
|
||||
type RecordLayout = ();
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct BoundPhase;
|
||||
impl CompilerPhase for BoundPhase {
|
||||
type Metadata = ();
|
||||
type LocalAddress = VirtualId;
|
||||
type Binding = IdentifierBinding<VirtualId>;
|
||||
type DefInfo = DefBinding;
|
||||
type AssignInfo = AssignBinding<VirtualId>;
|
||||
type LambdaInfo = LambdaBinding<VirtualId>;
|
||||
type RecordLayout = Arc<crate::ast::types::RecordLayout>;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct TypedPhase;
|
||||
impl CompilerPhase for TypedPhase {
|
||||
type Metadata = StaticType;
|
||||
type LocalAddress = VirtualId;
|
||||
type Binding = IdentifierBinding<VirtualId>;
|
||||
type DefInfo = DefBinding;
|
||||
type AssignInfo = AssignBinding<VirtualId>;
|
||||
type LambdaInfo = LambdaBinding<VirtualId>;
|
||||
type RecordLayout = Arc<crate::ast::types::RecordLayout>;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct AnalyzedPhase;
|
||||
impl CompilerPhase for AnalyzedPhase {
|
||||
type Metadata = NodeMetrics;
|
||||
type LocalAddress = VirtualId;
|
||||
type Binding = IdentifierBinding<VirtualId>;
|
||||
type DefInfo = DefBinding;
|
||||
type AssignInfo = AssignBinding<VirtualId>;
|
||||
type LambdaInfo = LambdaBinding<VirtualId>;
|
||||
type RecordLayout = Arc<crate::ast::types::RecordLayout>;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct RuntimePhase;
|
||||
impl CompilerPhase for RuntimePhase {
|
||||
type Metadata = RuntimeMetadata;
|
||||
type LocalAddress = StackOffset;
|
||||
type Binding = IdentifierBinding<StackOffset>;
|
||||
type DefInfo = DefBinding;
|
||||
type AssignInfo = AssignBinding<StackOffset>;
|
||||
type LambdaInfo = LambdaBinding<StackOffset>;
|
||||
type RecordLayout = Arc<crate::ast::types::RecordLayout>;
|
||||
}
|
||||
|
||||
/// Convenience alias for all post-binding phases that share the same
|
||||
/// concrete binding, def, assign, lambda, and record-layout types.
|
||||
/// Covers `BoundPhase`, `TypedPhase`, `AnalyzedPhase` (but not `SyntaxPhase` or `RuntimePhase`).
|
||||
pub trait BoundLike:
|
||||
CompilerPhase<
|
||||
LocalAddress = VirtualId,
|
||||
Binding = IdentifierBinding<VirtualId>,
|
||||
DefInfo = DefBinding,
|
||||
AssignInfo = AssignBinding<VirtualId>,
|
||||
LambdaInfo = LambdaBinding<VirtualId>,
|
||||
RecordLayout = Arc<crate::ast::types::RecordLayout>,
|
||||
>
|
||||
{
|
||||
}
|
||||
|
||||
impl<P> BoundLike for P where
|
||||
P: CompilerPhase<
|
||||
LocalAddress = VirtualId,
|
||||
Binding = IdentifierBinding<VirtualId>,
|
||||
DefInfo = DefBinding,
|
||||
AssignInfo = AssignBinding<VirtualId>,
|
||||
LambdaInfo = LambdaBinding<VirtualId>,
|
||||
RecordLayout = Arc<crate::ast::types::RecordLayout>,
|
||||
>
|
||||
{
|
||||
}
|
||||
|
||||
// ── Node<P> ────────────────────────────────────────────────────────────────────
|
||||
|
||||
/// A unified AST node, decorated with phase-specific information P.
|
||||
/// Replaces both `SyntaxNode` (parser output) and the old bound AST node.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct Node<P: CompilerPhase = BoundPhase> {
|
||||
pub identity: Identity,
|
||||
pub kind: NodeKind<P>,
|
||||
pub ty: P::Metadata,
|
||||
}
|
||||
|
||||
impl<P: CompilerPhase> Clone for Node<P> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
identity: self.identity.clone(),
|
||||
kind: self.kind.clone(),
|
||||
ty: self.ty.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Type alias: the parser AST is now `Node<SyntaxPhase>`.
|
||||
pub type SyntaxNode = Node<SyntaxPhase>;
|
||||
|
||||
@@ -46,7 +278,63 @@ impl Object for Node<SyntaxPhase> {
|
||||
}
|
||||
}
|
||||
|
||||
/// The base for custom node types (extensions)
|
||||
pub type TypedNode = Node<TypedPhase>;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct NodeMetrics {
|
||||
pub original: Rc<TypedNode>,
|
||||
pub purity: crate::ast::types::Purity,
|
||||
pub is_recursive: bool,
|
||||
}
|
||||
|
||||
pub type AnalyzedNode = Node<AnalyzedPhase>;
|
||||
|
||||
#[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 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()
|
||||
}
|
||||
}
|
||||
|
||||
// ── CustomNode ─────────────────────────────────────────────────────────────────
|
||||
|
||||
/// The base for custom node types (extensions).
|
||||
pub trait CustomNode: Debug {
|
||||
fn display_name(&self) -> &'static str;
|
||||
fn clone_box(&self) -> Box<dyn CustomNode>;
|
||||
@@ -63,3 +351,248 @@ impl PartialEq for Box<dyn CustomNode> {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// ── NodeKind<P> ────────────────────────────────────────────────────────────────
|
||||
|
||||
/// A key-value pair in a record literal: `(key_node, value_node)`.
|
||||
pub type RecordFieldPair<P> = (Rc<Node<P>>, Rc<Node<P>>);
|
||||
|
||||
/// Unified AST node kind, replacing both `SyntaxKind` and `BoundKind<P>`.
|
||||
///
|
||||
/// The structure is always consistent with the syntax the user wrote.
|
||||
/// Phase-specific information is carried in `P::*` annotation slots,
|
||||
/// which are `()` in `SyntaxPhase` and concrete types in later phases.
|
||||
#[derive(Debug)]
|
||||
pub enum NodeKind<P: CompilerPhase = SyntaxPhase> {
|
||||
Nop,
|
||||
Constant(Value),
|
||||
Identifier {
|
||||
symbol: Symbol,
|
||||
binding: P::Binding,
|
||||
},
|
||||
FieldAccessor(crate::ast::types::Keyword),
|
||||
If {
|
||||
cond: Rc<Node<P>>,
|
||||
then_br: Rc<Node<P>>,
|
||||
else_br: Option<Rc<Node<P>>>,
|
||||
},
|
||||
Def {
|
||||
pattern: Rc<Node<P>>,
|
||||
value: Rc<Node<P>>,
|
||||
info: P::DefInfo,
|
||||
},
|
||||
Assign {
|
||||
target: Rc<Node<P>>,
|
||||
value: Rc<Node<P>>,
|
||||
info: P::AssignInfo,
|
||||
},
|
||||
Lambda {
|
||||
params: Rc<Node<P>>,
|
||||
body: Rc<Node<P>>,
|
||||
info: P::LambdaInfo,
|
||||
},
|
||||
Call {
|
||||
callee: Rc<Node<P>>,
|
||||
args: Rc<Node<P>>,
|
||||
},
|
||||
Again {
|
||||
args: Rc<Node<P>>,
|
||||
},
|
||||
Pipe {
|
||||
inputs: Vec<Rc<Node<P>>>,
|
||||
lambda: Rc<Node<P>>,
|
||||
},
|
||||
Block {
|
||||
exprs: Vec<Rc<Node<P>>>,
|
||||
},
|
||||
Tuple {
|
||||
elements: Vec<Rc<Node<P>>>,
|
||||
},
|
||||
Record {
|
||||
fields: Vec<RecordFieldPair<P>>,
|
||||
layout: P::RecordLayout,
|
||||
},
|
||||
/// Macro declaration (only valid in `SyntaxPhase`).
|
||||
MacroDecl {
|
||||
name: Symbol,
|
||||
params: Rc<Node<P>>,
|
||||
body: Rc<Node<P>>,
|
||||
},
|
||||
/// Quasiquote template (only valid in `SyntaxPhase`).
|
||||
Template(Rc<Node<P>>),
|
||||
/// Unquote placeholder inside a template (only valid in `SyntaxPhase`).
|
||||
Placeholder(Rc<Node<P>>),
|
||||
/// Splice placeholder inside a template (only valid in `SyntaxPhase`).
|
||||
Splice(Rc<Node<P>>),
|
||||
/// Optimized field access (only created during lowering for `RuntimePhase`).
|
||||
GetField {
|
||||
rec: Rc<Node<P>>,
|
||||
field: crate::ast::types::Keyword,
|
||||
},
|
||||
/// Expanded macro call, preserving the original call for debugging.
|
||||
Expansion {
|
||||
original_call: Rc<Node<SyntaxPhase>>,
|
||||
expanded: Rc<Node<P>>,
|
||||
},
|
||||
Error,
|
||||
Extension(Box<dyn BoundExtension<P>>),
|
||||
}
|
||||
|
||||
impl<P: CompilerPhase> Clone for NodeKind<P> {
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
NodeKind::Nop => NodeKind::Nop,
|
||||
NodeKind::Constant(v) => NodeKind::Constant(v.clone()),
|
||||
NodeKind::Identifier { symbol, binding } => NodeKind::Identifier {
|
||||
symbol: symbol.clone(),
|
||||
binding: binding.clone(),
|
||||
},
|
||||
NodeKind::FieldAccessor(k) => NodeKind::FieldAccessor(*k),
|
||||
NodeKind::If { cond, then_br, else_br } => NodeKind::If {
|
||||
cond: cond.clone(),
|
||||
then_br: then_br.clone(),
|
||||
else_br: else_br.clone(),
|
||||
},
|
||||
NodeKind::Def { pattern, value, info } => NodeKind::Def {
|
||||
pattern: pattern.clone(),
|
||||
value: value.clone(),
|
||||
info: info.clone(),
|
||||
},
|
||||
NodeKind::Assign { target, value, info } => NodeKind::Assign {
|
||||
target: target.clone(),
|
||||
value: value.clone(),
|
||||
info: info.clone(),
|
||||
},
|
||||
NodeKind::Lambda { params, body, info } => NodeKind::Lambda {
|
||||
params: params.clone(),
|
||||
body: body.clone(),
|
||||
info: info.clone(),
|
||||
},
|
||||
NodeKind::Call { callee, args } => NodeKind::Call {
|
||||
callee: callee.clone(),
|
||||
args: args.clone(),
|
||||
},
|
||||
NodeKind::Again { args } => NodeKind::Again { args: args.clone() },
|
||||
NodeKind::Pipe { inputs, lambda } => NodeKind::Pipe {
|
||||
inputs: inputs.clone(),
|
||||
lambda: lambda.clone(),
|
||||
},
|
||||
NodeKind::Block { exprs } => NodeKind::Block { exprs: exprs.clone() },
|
||||
NodeKind::Tuple { elements } => NodeKind::Tuple { elements: elements.clone() },
|
||||
NodeKind::Record { fields, layout } => NodeKind::Record {
|
||||
fields: fields.clone(),
|
||||
layout: layout.clone(),
|
||||
},
|
||||
NodeKind::GetField { rec, field } => NodeKind::GetField {
|
||||
rec: rec.clone(),
|
||||
field: *field,
|
||||
},
|
||||
NodeKind::MacroDecl { name, params, body } => NodeKind::MacroDecl {
|
||||
name: name.clone(),
|
||||
params: params.clone(),
|
||||
body: body.clone(),
|
||||
},
|
||||
NodeKind::Template(inner) => NodeKind::Template(inner.clone()),
|
||||
NodeKind::Placeholder(inner) => NodeKind::Placeholder(inner.clone()),
|
||||
NodeKind::Splice(inner) => NodeKind::Splice(inner.clone()),
|
||||
NodeKind::Expansion { original_call, expanded } => NodeKind::Expansion {
|
||||
original_call: original_call.clone(),
|
||||
expanded: expanded.clone(),
|
||||
},
|
||||
NodeKind::Error => NodeKind::Error,
|
||||
NodeKind::Extension(ext) => NodeKind::Extension(ext.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: CompilerPhase> PartialEq for NodeKind<P> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
match (self, other) {
|
||||
(NodeKind::Nop, NodeKind::Nop) => true,
|
||||
(NodeKind::Constant(a), NodeKind::Constant(b)) => a == b,
|
||||
(NodeKind::Identifier { symbol: sa, binding: ba }, NodeKind::Identifier { symbol: sb, binding: bb }) => {
|
||||
sa == sb && ba == bb
|
||||
}
|
||||
(NodeKind::FieldAccessor(a), NodeKind::FieldAccessor(b)) => a == b,
|
||||
(NodeKind::If { cond: ca, then_br: ta, else_br: ea }, NodeKind::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,
|
||||
}
|
||||
}
|
||||
(NodeKind::Def { pattern: pa, value: va, info: ia }, NodeKind::Def { pattern: pb, value: vb, info: ib }) => {
|
||||
Rc::ptr_eq(pa, pb) && Rc::ptr_eq(va, vb) && ia == ib
|
||||
}
|
||||
(NodeKind::Assign { target: ta, value: va, info: ia }, NodeKind::Assign { target: tb, value: vb, info: ib }) => {
|
||||
Rc::ptr_eq(ta, tb) && Rc::ptr_eq(va, vb) && ia == ib
|
||||
}
|
||||
(NodeKind::Lambda { params: pa, body: ba, info: ia }, NodeKind::Lambda { params: pb, body: bb, info: ib }) => {
|
||||
Rc::ptr_eq(pa, pb) && Rc::ptr_eq(ba, bb) && ia == ib
|
||||
}
|
||||
(NodeKind::Call { callee: ca, args: aa }, NodeKind::Call { callee: cb, args: ab }) => {
|
||||
Rc::ptr_eq(ca, cb) && Rc::ptr_eq(aa, ab)
|
||||
}
|
||||
(NodeKind::Again { args: aa }, NodeKind::Again { args: ab }) => Rc::ptr_eq(aa, ab),
|
||||
(NodeKind::Pipe { inputs: ia, lambda: la }, NodeKind::Pipe { inputs: ib, lambda: lb }) => {
|
||||
ia.len() == ib.len()
|
||||
&& ia.iter().zip(ib.iter()).all(|(a, b)| Rc::ptr_eq(a, b))
|
||||
&& Rc::ptr_eq(la, lb)
|
||||
}
|
||||
(NodeKind::Block { exprs: ea }, NodeKind::Block { exprs: eb }) => {
|
||||
ea.len() == eb.len() && ea.iter().zip(eb.iter()).all(|(a, b)| Rc::ptr_eq(a, b))
|
||||
}
|
||||
(NodeKind::Tuple { elements: ea }, NodeKind::Tuple { elements: eb }) => {
|
||||
ea.len() == eb.len() && ea.iter().zip(eb.iter()).all(|(a, b)| Rc::ptr_eq(a, b))
|
||||
}
|
||||
(NodeKind::Record { fields: fa, layout: la }, NodeKind::Record { fields: fb, layout: lb }) => {
|
||||
la == lb
|
||||
&& fa.len() == fb.len()
|
||||
&& fa.iter().zip(fb.iter()).all(|((ka, va), (kb, vb))| Rc::ptr_eq(ka, kb) && Rc::ptr_eq(va, vb))
|
||||
}
|
||||
(NodeKind::GetField { rec: ra, field: fa }, NodeKind::GetField { rec: rb, field: fb }) => {
|
||||
Rc::ptr_eq(ra, rb) && fa == fb
|
||||
}
|
||||
(NodeKind::MacroDecl { name: na, params: pa, body: ba }, NodeKind::MacroDecl { name: nb, params: pb, body: bb }) => {
|
||||
na == nb && Rc::ptr_eq(pa, pb) && Rc::ptr_eq(ba, bb)
|
||||
}
|
||||
(NodeKind::Template(a), NodeKind::Template(b)) => Rc::ptr_eq(a, b),
|
||||
(NodeKind::Placeholder(a), NodeKind::Placeholder(b)) => Rc::ptr_eq(a, b),
|
||||
(NodeKind::Splice(a), NodeKind::Splice(b)) => Rc::ptr_eq(a, b),
|
||||
(NodeKind::Expansion { original_call: ca, expanded: ea }, NodeKind::Expansion { original_call: cb, expanded: eb }) => {
|
||||
Rc::ptr_eq(ca, cb) && Rc::ptr_eq(ea, eb)
|
||||
}
|
||||
(NodeKind::Error, NodeKind::Error) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: CompilerPhase> NodeKind<P> {
|
||||
pub fn display_name(&self) -> String {
|
||||
match self {
|
||||
NodeKind::Nop => "NOP".to_string(),
|
||||
NodeKind::Constant(v) => format!("CONST({})", v),
|
||||
NodeKind::Identifier { symbol, .. } => format!("ID({})", symbol.name),
|
||||
NodeKind::FieldAccessor(k) => format!("FIELD_ACCESSOR(.{})", k.name()),
|
||||
NodeKind::If { .. } => "IF".to_string(),
|
||||
NodeKind::Def { .. } => "DEF".to_string(),
|
||||
NodeKind::Assign { .. } => "ASSIGN".to_string(),
|
||||
NodeKind::Lambda { .. } => "LAMBDA".to_string(),
|
||||
NodeKind::Call { .. } => "CALL".to_string(),
|
||||
NodeKind::Again { .. } => "AGAIN".to_string(),
|
||||
NodeKind::Pipe { .. } => "PIPE".to_string(),
|
||||
NodeKind::Block { .. } => "BLOCK".to_string(),
|
||||
NodeKind::Tuple { elements } => format!("TUPLE({})", elements.len()),
|
||||
NodeKind::Record { fields, .. } => format!("RECORD({})", fields.len()),
|
||||
NodeKind::GetField { field, .. } => format!("GET_FIELD(.{})", field.name()),
|
||||
NodeKind::MacroDecl { name, .. } => format!("MACRO({})", name.name),
|
||||
NodeKind::Template(_) => "TEMPLATE".to_string(),
|
||||
NodeKind::Placeholder(_) => "PLACEHOLDER".to_string(),
|
||||
NodeKind::Splice(_) => "SPLICE".to_string(),
|
||||
NodeKind::Expansion { .. } => "EXPANSION".to_string(),
|
||||
NodeKind::Extension(ext) => ext.display_name(),
|
||||
NodeKind::Error => "ERROR".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
use crate::ast::compiler::bound_nodes::{Address, AnalyzedNode, ExecNode, IdentifierBinding, NodeKind, StackOffset};
|
||||
use crate::ast::nodes::{Address, AnalyzedNode, ExecNode, IdentifierBinding, NodeKind, StackOffset};
|
||||
use crate::ast::types::{Object, Value};
|
||||
use std::any::Any;
|
||||
use std::cell::RefCell;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use myc::ast::compiler::bound_nodes::TypedNode;
|
||||
use myc::ast::nodes::TypedNode;
|
||||
use myc::ast::environment::Environment;
|
||||
use myc::ast::types::StaticType;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user