diff --git a/src/ast/compiler/bound_nodes.rs b/src/ast/compiler/bound_nodes.rs index dbbb81c..defa27b 100644 --- a/src/ast/compiler/bound_nodes.rs +++ b/src/ast/compiler/bound_nodes.rs @@ -1,6 +1,7 @@ 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); @@ -66,6 +67,68 @@ pub enum DeclarationKind { 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 { + /// A variable reference (read access). + Reference(Address), + /// A variable or parameter declaration (write/bind target). + Declaration { + addr: Address, + 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, +} + +/// Target address attached to `assign` nodes. +#[derive(Debug, Clone, PartialEq)] +pub struct AssignBinding { + pub addr: Address, +} + +/// Closure metadata attached to `lambda` nodes. +#[derive(Debug, Clone, PartialEq)] +pub struct LambdaBinding { + /// Addresses of captured variables from enclosing scopes. + pub upvalues: Vec>, + /// Number of positional parameters (None = variadic). + pub positional_count: Option, +} + +// ── 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)] @@ -73,6 +136,11 @@ pub struct BoundPhase; impl CompilerPhase for BoundPhase { type Metadata = (); type LocalAddress = VirtualId; + type Binding = IdentifierBinding; + type DefInfo = DefBinding; + type AssignInfo = AssignBinding; + type LambdaInfo = LambdaBinding; + type RecordLayout = Arc; } #[derive(Debug, Clone, PartialEq, Eq)] @@ -80,6 +148,11 @@ pub struct TypedPhase; impl CompilerPhase for TypedPhase { type Metadata = StaticType; type LocalAddress = VirtualId; + type Binding = IdentifierBinding; + type DefInfo = DefBinding; + type AssignInfo = AssignBinding; + type LambdaInfo = LambdaBinding; + type RecordLayout = Arc; } #[derive(Debug, Clone, PartialEq, Eq)] @@ -87,6 +160,11 @@ pub struct AnalyzedPhase; impl CompilerPhase for AnalyzedPhase { type Metadata = NodeMetrics; type LocalAddress = VirtualId; + type Binding = IdentifierBinding; + type DefInfo = DefBinding; + type AssignInfo = AssignBinding; + type LambdaInfo = LambdaBinding; + type RecordLayout = Arc; } #[derive(Debug, Clone, PartialEq, Eq)] @@ -94,6 +172,11 @@ pub struct RuntimePhase; impl CompilerPhase for RuntimePhase { type Metadata = RuntimeMetadata; type LocalAddress = StackOffset; + type Binding = IdentifierBinding; + type DefInfo = DefBinding; + type AssignInfo = AssignBinding; + type LambdaInfo = LambdaBinding; + type RecordLayout = Arc; } /// A bound AST node, decorated with phase-specific information P. @@ -350,3 +433,235 @@ impl BoundKind

{ } } } + +// ── NodeKind

: Unified AST node kind ───────────────────────────────────────── + +/// A key-value pair in a record literal: `(key_node, value_node)`. +pub type RecordFieldPair

= (Rc>, Rc>); + +/// Unified AST node kind, replacing both `SyntaxKind` and `BoundKind

`. +/// +/// 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 { + Nop, + Constant(Value), + Identifier { + symbol: Symbol, + binding: P::Binding, + }, + FieldAccessor(crate::ast::types::Keyword), + If { + cond: Rc>, + then_br: Rc>, + else_br: Option>>, + }, + Def { + pattern: Rc>, + value: Rc>, + info: P::DefInfo, + }, + Assign { + target: Rc>, + value: Rc>, + info: P::AssignInfo, + }, + Lambda { + params: Rc>, + body: Rc>, + info: P::LambdaInfo, + }, + Call { + callee: Rc>, + args: Rc>, + }, + Again { + args: Rc>, + }, + Pipe { + inputs: Vec>>, + lambda: Rc>, + }, + Block { + exprs: Vec>>, + }, + Tuple { + elements: Vec>>, + }, + Record { + fields: Vec>, + layout: P::RecordLayout, + }, + /// Macro declaration (only valid in `SyntaxPhase`). + MacroDecl { + name: Symbol, + params: Rc>, + body: Rc>, + }, + /// Quasiquote template (only valid in `SyntaxPhase`). + Template(Rc>), + /// Unquote placeholder inside a template (only valid in `SyntaxPhase`). + Placeholder(Rc>), + /// Splice placeholder inside a template (only valid in `SyntaxPhase`). + Splice(Rc>), + /// Expanded macro call, preserving the original call for debugging. + Expansion { + original_call: Rc>, + expanded: Rc>, + }, + Error, + Extension(Box>), +} + +impl Clone for NodeKind

{ + 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::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 PartialEq for NodeKind

{ + 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::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 NodeKind

{ + 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::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(), + } + } +}