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:
2026-03-22 17:58:49 +01:00
parent 49ad76e7c3
commit 1cbc656554
20 changed files with 580 additions and 572 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
use crate::ast::compiler::bound_nodes::{
use crate::ast::nodes::{
Address, AnalyzedNode, GlobalIdx, IdentifierBinding, Node,
NodeKind, NodeMetrics, TypedNode, TypedPhase,
};
+3 -3
View File
@@ -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;
-524
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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,
+2 -2
View File
@@ -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 -2
View File
@@ -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::*;
+2 -2
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -1,4 +1,4 @@
use crate::ast::compiler::bound_nodes::{
use crate::ast::nodes::{
Address, AnalyzedNode, GlobalIdx, IdentifierBinding,
NodeKind, VirtualId,
};
+1 -1
View File
@@ -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};
+2 -2
View File
@@ -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 {
+3 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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 -1
View File
@@ -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;