7d72a99fa1
Simplified the AST architecture by removing the overly complex Node<K,
T> structure and replacing it with specialized types for
each phase:
- Introduced UntypedNode in nodes.rs for the Parser and Macro system,
eliminating redundant ty: () initializations.
- Defined a new generic Node<T> in bound_nodes.rs for all compiler
stages, where T represents the phase-specific metadata.
- Updated the Binder to act as the bridge between UntypedNode and
Node<()>.
- Simplified type aliases: TypedNode, AnalyzedNode, and ExecNode now
leverage the streamlined Node<T> structure.
- Updated Parser, Macros, Type-Checker, Optimizer, and VM to reflect
the architectural changes.
- Fixed import chains and resolved needless borrows via clippy.
This change improves type safety, reduces boilerplate code in the
parser, and makes the compiler stages more idiomatic and
readable.
265 lines
8.7 KiB
Rust
265 lines
8.7 KiB
Rust
use crate::ast::compiler::bound_nodes::{BoundKind, Node};
|
|
use crate::ast::types::Value;
|
|
use crate::ast::vm::Closure;
|
|
use std::fmt::Debug;
|
|
|
|
/// Human-readable AST dumper for the bound AST.
|
|
pub struct Dumper {
|
|
output: String,
|
|
indent: usize,
|
|
}
|
|
|
|
impl Dumper {
|
|
/// Produces a formatted string representation of the given bound AST node and its children.
|
|
pub fn dump<T: Debug>(node: &Node<T>) -> String {
|
|
let mut dumper = Self {
|
|
output: String::new(),
|
|
indent: 0,
|
|
};
|
|
dumper.visit(node);
|
|
dumper.output
|
|
}
|
|
|
|
fn write_indent(&mut self) {
|
|
for _ in 0..self.indent {
|
|
self.output.push_str(" ");
|
|
}
|
|
}
|
|
|
|
fn log<T: Debug>(&mut self, label: &str, node: &Node<T>) {
|
|
self.write_indent();
|
|
self.output.push_str(label);
|
|
self.output
|
|
.push_str(&format!(" <Metadata: {:?}>\n", node.ty));
|
|
}
|
|
|
|
fn visit<T: Debug>(&mut self, node: &Node<T>) {
|
|
match &node.kind {
|
|
BoundKind::Nop => self.log("Nop", node),
|
|
BoundKind::Constant(v) => {
|
|
self.log(&format!("Constant: {}", v), node);
|
|
// Introspect Closure AST if possible
|
|
if let Value::Object(obj) = v
|
|
&& let Some(closure) = obj.as_any().downcast_ref::<Closure>()
|
|
{
|
|
self.indent += 1;
|
|
self.write_indent();
|
|
self.output.push_str("--- Specialized Body ---\n");
|
|
// We need to cast the inner TypedNode to the generic T required by visit.
|
|
// Since Dumper is generic over T, but Closure stores TypedNode (where T = StaticType),
|
|
// we can only fully dump if T is StaticType.
|
|
// However, we can hack it by creating a new Dumper for the inner AST string.
|
|
|
|
// We can't call self.visit because types mismatch if T != StaticType.
|
|
// So we just recursively dump to string and append.
|
|
let inner_dump = Dumper::dump(&closure.function_node);
|
|
for line in inner_dump.lines() {
|
|
self.write_indent();
|
|
self.output.push_str(line);
|
|
self.output.push('\n');
|
|
}
|
|
self.indent -= 1;
|
|
}
|
|
}
|
|
BoundKind::Get { addr, name } => {
|
|
self.log(&format!("Get: {} ({:?})", name.name, addr), node)
|
|
}
|
|
|
|
BoundKind::FieldAccessor(k) => self.log(&format!("FieldAccessor: .{}", k.name()), node),
|
|
|
|
BoundKind::GetField { rec, field } => {
|
|
self.log(&format!("GetField: .{}", field.name()), node);
|
|
self.indent += 1;
|
|
self.visit(rec);
|
|
self.indent -= 1;
|
|
}
|
|
|
|
BoundKind::Set { addr, value } => {
|
|
self.log(&format!("Set: {:?}", addr), node);
|
|
self.indent += 1;
|
|
self.visit(value);
|
|
self.indent -= 1;
|
|
}
|
|
|
|
BoundKind::Define {
|
|
name,
|
|
addr,
|
|
kind,
|
|
value,
|
|
captured_by,
|
|
} => {
|
|
let k_str = match kind {
|
|
crate::ast::compiler::bound_nodes::DeclarationKind::Variable => "Variable",
|
|
crate::ast::compiler::bound_nodes::DeclarationKind::Parameter => "Parameter",
|
|
};
|
|
let capture_info = if captured_by.is_empty() {
|
|
String::from("not captured")
|
|
} else {
|
|
format!("captured by {} lambdas", captured_by.len())
|
|
};
|
|
self.log(
|
|
&format!(
|
|
"Define {} (Name: '{}', Address: {:?}, {})",
|
|
k_str, name.name, addr, capture_info
|
|
),
|
|
node,
|
|
);
|
|
|
|
self.indent += 1;
|
|
if !captured_by.is_empty() {
|
|
for capturer in captured_by {
|
|
self.write_indent();
|
|
let loc = capturer
|
|
.location
|
|
.unwrap_or(crate::ast::types::SourceLocation { line: 0, col: 0 });
|
|
self.output.push_str(&format!(
|
|
"- Capturer: Lambda at line {}, col {}\n",
|
|
loc.line, loc.col
|
|
));
|
|
}
|
|
}
|
|
self.visit(value);
|
|
self.indent -= 1;
|
|
}
|
|
|
|
BoundKind::Destructure { pattern, value } => {
|
|
self.log("Destructure", node);
|
|
self.indent += 1;
|
|
self.write_indent();
|
|
self.output.push_str("Pattern:\n");
|
|
self.visit(pattern);
|
|
self.write_indent();
|
|
self.output.push_str("Value:\n");
|
|
self.visit(value);
|
|
self.indent -= 1;
|
|
}
|
|
|
|
BoundKind::If {
|
|
cond,
|
|
then_br,
|
|
else_br,
|
|
} => {
|
|
self.log("If", node);
|
|
self.indent += 1;
|
|
|
|
self.write_indent();
|
|
self.output.push_str("Condition:\n");
|
|
self.visit(cond);
|
|
|
|
self.write_indent();
|
|
self.output.push_str("Then:\n");
|
|
self.visit(then_br);
|
|
|
|
if let Some(e) = else_br {
|
|
self.write_indent();
|
|
self.output.push_str("Else:\n");
|
|
self.visit(e);
|
|
}
|
|
self.indent -= 1;
|
|
}
|
|
|
|
BoundKind::Lambda {
|
|
params,
|
|
upvalues,
|
|
body,
|
|
..
|
|
} => {
|
|
self.log(&format!("Lambda (Upvalues: {})", upvalues.len()), node);
|
|
self.indent += 1;
|
|
|
|
self.write_indent();
|
|
self.output.push_str("Parameters:\n");
|
|
self.visit(params);
|
|
|
|
if !upvalues.is_empty() {
|
|
self.write_indent();
|
|
self.output.push_str(&format!("Upvalues: {:?}\n", upvalues));
|
|
}
|
|
self.visit(body);
|
|
self.indent -= 1;
|
|
}
|
|
|
|
BoundKind::Call { callee, args } => {
|
|
self.log("Call", node);
|
|
self.indent += 1;
|
|
|
|
self.write_indent();
|
|
self.output.push_str("Callee:\n");
|
|
self.visit(callee);
|
|
|
|
self.write_indent();
|
|
self.output.push_str("Arguments:\n");
|
|
self.visit(args);
|
|
|
|
self.indent -= 1;
|
|
}
|
|
|
|
BoundKind::Again { args } => {
|
|
self.log("Again", node);
|
|
self.indent += 1;
|
|
self.visit(args);
|
|
self.indent -= 1;
|
|
}
|
|
BoundKind::Pipe { inputs, lambda, .. } => {
|
|
self.log("Pipe", node);
|
|
self.indent += 1;
|
|
for input in inputs {
|
|
self.visit(input);
|
|
}
|
|
self.visit(lambda);
|
|
self.indent -= 1;
|
|
}
|
|
|
|
BoundKind::Block { exprs } => {
|
|
self.log("Block", node);
|
|
self.indent += 1;
|
|
for expr in exprs {
|
|
self.visit(expr);
|
|
}
|
|
self.indent -= 1;
|
|
}
|
|
|
|
BoundKind::Tuple { elements } => {
|
|
self.log("Tuple", node);
|
|
self.indent += 1;
|
|
for el in elements {
|
|
self.visit(el);
|
|
}
|
|
self.indent -= 1;
|
|
}
|
|
|
|
BoundKind::Record { layout, values } => {
|
|
self.log(
|
|
&format!("Record (Layout: {} fields)", layout.fields.len()),
|
|
node,
|
|
);
|
|
self.indent += 1;
|
|
for v in values {
|
|
self.visit(v);
|
|
}
|
|
self.indent -= 1;
|
|
}
|
|
|
|
BoundKind::Expansion {
|
|
original_call,
|
|
bound_expanded,
|
|
} => {
|
|
self.log(
|
|
&format!("Expansion (Original: {:?})", original_call.kind),
|
|
node,
|
|
);
|
|
self.indent += 1;
|
|
self.visit(bound_expanded);
|
|
self.indent -= 1;
|
|
}
|
|
|
|
BoundKind::Extension(ext) => {
|
|
self.log(&ext.display_name(), node);
|
|
}
|
|
BoundKind::Error => {
|
|
self.log("ERROR_NODE", node);
|
|
}
|
|
}
|
|
}
|
|
}
|