Refactor Binder to use simpler types

The Binder has been refactored to simplify its internal types and reduce
redundancy.
Key changes include:
- Removed StaticType from LocalInfo, as it's not strictly needed for the
  binder.
- Simplified `define` in CompilerScope to not take a StaticType.
- Removed StaticType from upvalues in FunctionCompiler.
- Adjusted global mappings to store only the index, not the type.
- Updated BoundKind to use a generic type parameter `T` for metadata,
  defaulting to `()` for untyped bound nodes.
- Introduced `BoundNode` and `TypedNode` type aliases for clarity.
- Minor adjustments to dumper and VM to accommodate the new generic
  BoundKind.
This commit is contained in:
Michael Schimmel
2026-02-19 12:07:28 +01:00
parent c49561255e
commit 4673ea78b9
7 changed files with 130 additions and 176 deletions
+15 -10
View File
@@ -2,13 +2,13 @@ use std::rc::Rc;
use std::cell::RefCell;
use std::collections::HashMap;
use std::any::Any;
use crate::ast::compiler::bound_nodes::{Address, BoundKind};
use crate::ast::compiler::bound_nodes::{Address, BoundKind, TypedNode};
use crate::ast::nodes::Node;
use crate::ast::types::{Value, Object, StaticType};
#[derive(Debug, Clone)]
pub struct Closure {
pub function_node: Rc<Node<BoundKind, StaticType>>,
pub function_node: Rc<TypedNode>,
pub upvalues: Vec<Rc<RefCell<Value>>>,
}
@@ -30,8 +30,8 @@ struct CallFrame {
/// Hook for observing VM execution (zero-cost when O::ACTIVE is false)
pub trait VMObserver {
const ACTIVE: bool = false;
fn before_eval(&mut self, _vm: &VM, _node: &Node<BoundKind, StaticType>) {}
fn after_eval(&mut self, _vm: &VM, _node: &Node<BoundKind, StaticType>, _res: &Result<Value, String>) {}
fn before_eval(&mut self, _vm: &VM, _node: &TypedNode) {}
fn after_eval(&mut self, _vm: &VM, _node: &TypedNode, _res: &Result<Value, String>) {}
}
/// Default observer that does nothing and should be optimized away
@@ -63,13 +63,13 @@ impl Default for TracingObserver {
impl VMObserver for TracingObserver {
const ACTIVE: bool = true;
fn before_eval(&mut self, _vm: &VM, node: &Node<BoundKind, StaticType>) {
fn before_eval(&mut self, _vm: &VM, node: &TypedNode) {
let pad = self.pad();
self.logs.push(format!("{}{} [{}]: {{", pad, node.kind.display_name(), node.ty));
self.indent += 1;
}
fn after_eval(&mut self, vm: &VM, node: &Node<BoundKind, StaticType>, res: &Result<Value, String>) {
fn after_eval(&mut self, vm: &VM, node: &TypedNode, res: &Result<Value, String>) {
self.indent = self.indent.saturating_sub(1);
let pad = self.pad();
@@ -262,6 +262,11 @@ macro_rules! dispatch_eval {
BoundKind::Expansion { original_call: _, bound_expanded } => {
$self.$eval_method($($observer,)? bound_expanded)
}
BoundKind::Extension(ext) => {
// Future: Delegate execution to extension
Err(format!("Execution of extension '{}' not implemented yet", ext.display_name()))
}
}
};
}
@@ -275,7 +280,7 @@ impl VM {
}
}
pub fn run(&mut self, root: &Node<BoundKind, StaticType>) -> Result<Value, String> {
pub fn run(&mut self, root: &TypedNode) -> Result<Value, String> {
self.stack.clear();
self.frames.clear();
@@ -290,7 +295,7 @@ impl VM {
result
}
pub fn run_with_observer<O: VMObserver>(&mut self, observer: &mut O, root: &Node<BoundKind, StaticType>) -> Result<Value, String> {
pub fn run_with_observer<O: VMObserver>(&mut self, observer: &mut O, root: &TypedNode) -> Result<Value, String> {
self.stack.clear();
self.frames.clear();
@@ -307,12 +312,12 @@ impl VM {
/// The pure, original, zero-cost hot path.
#[inline(always)]
fn eval(&mut self, node: &Node<BoundKind, StaticType>) -> Result<Value, String> {
fn eval(&mut self, node: &TypedNode) -> Result<Value, String> {
dispatch_eval!(self, node, eval)
}
/// The observed path for debugging.
fn eval_observed<O: VMObserver>(&mut self, observer: &mut O, node: &Node<BoundKind, StaticType>) -> Result<Value, String> {
fn eval_observed<O: VMObserver>(&mut self, observer: &mut O, node: &TypedNode) -> Result<Value, String> {
observer.before_eval(self, node);
// Wrap in a closure to ensure after_eval is called even on early returns (e.g. from ?)