feat: Add type checking to the compiler
Introduces the `TypeChecker` struct and its associated `TypeContext` for performing static type analysis on the abstract syntax tree. This change includes: - A new `type_checker.rs` module. - Integration of `TypeChecker` into the `Environment::compile` method. - Updates to `Environment` to manage global types. - Renaming `bound_nodes.rs`'s `BoundNode` to `TypedNode` to reflect its type-checked nature. - Refinements in binder and macro expansion to accommodate type information.
This commit is contained in:
@@ -11,7 +11,7 @@ struct LocalInfo {
|
|||||||
// Note: Binder doesn't strictly need the type anymore,
|
// Note: Binder doesn't strictly need the type anymore,
|
||||||
// but it might be useful for built-ins during resolution.
|
// but it might be useful for built-ins during resolution.
|
||||||
// For now we keep it as Any or Unknown.
|
// For now we keep it as Any or Unknown.
|
||||||
ty: StaticType,
|
_ty: StaticType,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -33,7 +33,7 @@ impl CompilerScope {
|
|||||||
return Err(format!("Variable '{}' is already defined in this scope level.", sym.name));
|
return Err(format!("Variable '{}' is already defined in this scope level.", sym.name));
|
||||||
}
|
}
|
||||||
let slot = self.slot_count;
|
let slot = self.slot_count;
|
||||||
self.locals.insert(sym.clone(), LocalInfo { slot, ty: StaticType::Any });
|
self.locals.insert(sym.clone(), LocalInfo { slot, _ty: StaticType::Any });
|
||||||
self.slot_count += 1;
|
self.slot_count += 1;
|
||||||
Ok(slot)
|
Ok(slot)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ pub enum BoundKind<T = ()> {
|
|||||||
},
|
},
|
||||||
|
|
||||||
Map {
|
Map {
|
||||||
entries: Vec<(Node<BoundKind<T>, T>, Node<BoundKind<T>, T>)>,
|
entries: Vec<MapEntry<T>>,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// An expanded macro call, preserving the original call for debugging and UI.
|
/// An expanded macro call, preserving the original call for debugging and UI.
|
||||||
@@ -95,6 +95,9 @@ pub enum BoundKind<T = ()> {
|
|||||||
Extension(Box<dyn BoundExtension<T>>),
|
Extension(Box<dyn BoundExtension<T>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A single entry in a Map literal (Key-Value pair)
|
||||||
|
pub type MapEntry<T> = (Node<BoundKind<T>, T>, Node<BoundKind<T>, T>);
|
||||||
|
|
||||||
/// Type alias for a node that has been bound (addresses resolved) but not yet type-checked.
|
/// Type alias for a node that has been bound (addresses resolved) but not yet type-checked.
|
||||||
pub type BoundNode = Node<BoundKind<()>, ()>;
|
pub type BoundNode = Node<BoundKind<()>, ()>;
|
||||||
|
|
||||||
|
|||||||
@@ -440,7 +440,7 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use crate::ast::parser::Parser;
|
use crate::ast::parser::Parser;
|
||||||
use crate::ast::types::{Value, StaticType, Object};
|
use crate::ast::types::{Value, Object};
|
||||||
use crate::ast::compiler::Binder;
|
use crate::ast::compiler::Binder;
|
||||||
|
|
||||||
struct SimpleEvaluator;
|
struct SimpleEvaluator;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ pub mod tco;
|
|||||||
pub mod upvalues;
|
pub mod upvalues;
|
||||||
pub mod dumper;
|
pub mod dumper;
|
||||||
pub mod macros;
|
pub mod macros;
|
||||||
|
pub mod type_checker;
|
||||||
|
|
||||||
pub use binder::*;
|
pub use binder::*;
|
||||||
pub use bound_nodes::*;
|
pub use bound_nodes::*;
|
||||||
@@ -11,3 +12,4 @@ pub use tco::*;
|
|||||||
pub use upvalues::*;
|
pub use upvalues::*;
|
||||||
pub use dumper::*;
|
pub use dumper::*;
|
||||||
pub use macros::*;
|
pub use macros::*;
|
||||||
|
pub use type_checker::*;
|
||||||
|
|||||||
@@ -0,0 +1,227 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use crate::ast::compiler::bound_nodes::{BoundKind, Address, BoundNode, TypedNode};
|
||||||
|
use crate::ast::nodes::Node;
|
||||||
|
use crate::ast::types::StaticType;
|
||||||
|
|
||||||
|
/// Manages the types of locals and upvalues during a single type-checking pass.
|
||||||
|
struct TypeContext<'a> {
|
||||||
|
_parent: Option<&'a TypeContext<'a>>,
|
||||||
|
/// Maps slot index -> Inferred Type
|
||||||
|
slots: Vec<StaticType>,
|
||||||
|
/// Types of captured variables (passed from outer scope)
|
||||||
|
upvalue_types: Vec<StaticType>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TypeContext<'a> {
|
||||||
|
fn new(slot_count: u32, upvalue_types: Vec<StaticType>, parent: Option<&'a TypeContext<'a>>) -> Self {
|
||||||
|
Self {
|
||||||
|
_parent: parent,
|
||||||
|
slots: vec![StaticType::Any; slot_count as usize],
|
||||||
|
upvalue_types,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_type(&self, addr: Address) -> StaticType {
|
||||||
|
match addr {
|
||||||
|
Address::Local(idx) => self.slots.get(idx as usize).cloned().unwrap_or(StaticType::Any),
|
||||||
|
Address::Global(_) => StaticType::Any, // Globals are dynamic for now
|
||||||
|
Address::Upvalue(idx) => self.upvalue_types.get(idx as usize).cloned().unwrap_or(StaticType::Any),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_local_type(&mut self, idx: u32, ty: StaticType) {
|
||||||
|
if let Some(slot) = self.slots.get_mut(idx as usize) {
|
||||||
|
*slot = ty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TypeChecker {
|
||||||
|
global_types: Rc<std::cell::RefCell<HashMap<u32, StaticType>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TypeChecker {
|
||||||
|
pub fn new(global_types: Rc<std::cell::RefCell<HashMap<u32, StaticType>>>) -> Self {
|
||||||
|
Self { global_types }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check(&self, node: BoundNode) -> Result<TypedNode, String> {
|
||||||
|
// Start with a root context. Root scope has no upvalues.
|
||||||
|
// We assume 1000 slots for global script level if needed, but Binder handles it.
|
||||||
|
// Actually, Binder already assigned slot indices. We need to know the max slot.
|
||||||
|
let mut ctx = TypeContext::new(256, vec![], None);
|
||||||
|
self.check_node(node, &mut ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_node(&self, node: BoundNode, ctx: &mut TypeContext) -> Result<TypedNode, String> {
|
||||||
|
let (kind, ty) = match node.kind {
|
||||||
|
BoundKind::Nop => (BoundKind::Nop, StaticType::Void),
|
||||||
|
|
||||||
|
BoundKind::Constant(v) => {
|
||||||
|
let ty = v.static_type();
|
||||||
|
(BoundKind::Constant(v), ty)
|
||||||
|
},
|
||||||
|
|
||||||
|
BoundKind::Get(addr) => {
|
||||||
|
let ty = if let Address::Global(idx) = addr {
|
||||||
|
self.global_types.borrow().get(&idx).cloned().unwrap_or(StaticType::Any)
|
||||||
|
} else {
|
||||||
|
ctx.get_type(addr)
|
||||||
|
};
|
||||||
|
(BoundKind::Get(addr), ty)
|
||||||
|
},
|
||||||
|
|
||||||
|
BoundKind::Set { addr, value } => {
|
||||||
|
let val_typed = self.check_node(*value, ctx)?;
|
||||||
|
let ty = val_typed.ty.clone();
|
||||||
|
|
||||||
|
if let Address::Local(idx) = addr {
|
||||||
|
ctx.set_local_type(idx, ty.clone());
|
||||||
|
} else if let Address::Global(idx) = addr {
|
||||||
|
self.global_types.borrow_mut().insert(idx, ty.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
(BoundKind::Set { addr, value: Box::new(val_typed) }, ty)
|
||||||
|
},
|
||||||
|
|
||||||
|
BoundKind::DefLocal { slot, value, captured_by } => {
|
||||||
|
let val_typed = self.check_node(*value, ctx)?;
|
||||||
|
let ty = val_typed.ty.clone();
|
||||||
|
ctx.set_local_type(slot, ty.clone());
|
||||||
|
|
||||||
|
(BoundKind::DefLocal { slot, value: Box::new(val_typed), captured_by }, ty)
|
||||||
|
},
|
||||||
|
|
||||||
|
BoundKind::DefGlobal { global_index, value } => {
|
||||||
|
let val_typed = self.check_node(*value, ctx)?;
|
||||||
|
let ty = val_typed.ty.clone();
|
||||||
|
self.global_types.borrow_mut().insert(global_index, ty.clone());
|
||||||
|
|
||||||
|
(BoundKind::DefGlobal { global_index, value: Box::new(val_typed) }, ty)
|
||||||
|
},
|
||||||
|
|
||||||
|
BoundKind::If { cond, then_br, else_br } => {
|
||||||
|
let cond_typed = self.check_node(*cond, ctx)?;
|
||||||
|
let then_typed = self.check_node(*then_br, ctx)?;
|
||||||
|
|
||||||
|
let mut else_typed = None;
|
||||||
|
let mut final_ty = then_typed.ty.clone();
|
||||||
|
|
||||||
|
if let Some(e) = else_br {
|
||||||
|
let et = self.check_node(*e, ctx)?;
|
||||||
|
// Basic type promotion: if types differ, fall back to Any
|
||||||
|
if et.ty != final_ty {
|
||||||
|
final_ty = StaticType::Any;
|
||||||
|
}
|
||||||
|
else_typed = Some(Box::new(et));
|
||||||
|
} else {
|
||||||
|
// If without else returns Void or Optional(Then)
|
||||||
|
final_ty = StaticType::Any; // Delphi: MakeOptional(Then)
|
||||||
|
}
|
||||||
|
|
||||||
|
(BoundKind::If {
|
||||||
|
cond: Box::new(cond_typed),
|
||||||
|
then_br: Box::new(then_typed),
|
||||||
|
else_br: else_typed
|
||||||
|
}, final_ty)
|
||||||
|
},
|
||||||
|
|
||||||
|
BoundKind::Block { exprs } => {
|
||||||
|
let mut typed_exprs = Vec::new();
|
||||||
|
let mut last_ty = StaticType::Void;
|
||||||
|
|
||||||
|
for e in exprs {
|
||||||
|
let t = self.check_node(e, ctx)?;
|
||||||
|
last_ty = t.ty.clone();
|
||||||
|
typed_exprs.push(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
(BoundKind::Block { exprs: typed_exprs }, last_ty)
|
||||||
|
},
|
||||||
|
|
||||||
|
BoundKind::Lambda { param_count, upvalues, body } => {
|
||||||
|
// 1. Determine types of captured variables
|
||||||
|
let mut upvalue_types = Vec::with_capacity(upvalues.len());
|
||||||
|
for &addr in &upvalues {
|
||||||
|
upvalue_types.push(ctx.get_type(addr));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Create nested context for lambda body
|
||||||
|
let mut lambda_ctx = TypeContext::new(param_count + 64, upvalue_types, Some(ctx));
|
||||||
|
|
||||||
|
// 3. Check body
|
||||||
|
let body_typed = self.check_node((*body).clone(), &mut lambda_ctx)?;
|
||||||
|
let ret_ty = body_typed.ty.clone();
|
||||||
|
|
||||||
|
// 4. Construct function type: fn(any ... any) -> Ret
|
||||||
|
let fn_ty = StaticType::Function {
|
||||||
|
params: vec![StaticType::Any; param_count as usize],
|
||||||
|
ret: Box::new(ret_ty),
|
||||||
|
};
|
||||||
|
|
||||||
|
(BoundKind::Lambda {
|
||||||
|
param_count,
|
||||||
|
upvalues,
|
||||||
|
body: Rc::new(body_typed)
|
||||||
|
}, fn_ty)
|
||||||
|
},
|
||||||
|
|
||||||
|
BoundKind::Call { callee, args } => {
|
||||||
|
let callee_typed = self.check_node(*callee, ctx)?;
|
||||||
|
let mut typed_args = Vec::new();
|
||||||
|
for arg in args {
|
||||||
|
typed_args.push(self.check_node(arg, ctx)?);
|
||||||
|
}
|
||||||
|
|
||||||
|
let ret_ty = match &callee_typed.ty {
|
||||||
|
StaticType::Function { ret, .. } => *ret.clone(),
|
||||||
|
_ => StaticType::Any,
|
||||||
|
};
|
||||||
|
|
||||||
|
(BoundKind::Call {
|
||||||
|
callee: Box::new(callee_typed),
|
||||||
|
args: typed_args
|
||||||
|
}, ret_ty)
|
||||||
|
},
|
||||||
|
|
||||||
|
BoundKind::Tuple { elements } => {
|
||||||
|
let mut typed_elements = Vec::new();
|
||||||
|
for e in elements {
|
||||||
|
typed_elements.push(self.check_node(e, ctx)?);
|
||||||
|
}
|
||||||
|
// Stability: Just List(Any) for now
|
||||||
|
(BoundKind::Tuple { elements: typed_elements }, StaticType::List(Box::new(StaticType::Any)))
|
||||||
|
},
|
||||||
|
|
||||||
|
BoundKind::Map { entries } => {
|
||||||
|
let mut typed_entries = Vec::new();
|
||||||
|
for (k, v) in entries {
|
||||||
|
typed_entries.push((self.check_node(k, ctx)?, self.check_node(v, ctx)?));
|
||||||
|
}
|
||||||
|
(BoundKind::Map { entries: typed_entries }, StaticType::Record(Rc::new(std::collections::BTreeMap::new())))
|
||||||
|
},
|
||||||
|
|
||||||
|
BoundKind::Expansion { original_call, bound_expanded } => {
|
||||||
|
let expanded_typed = self.check_node(*bound_expanded, ctx)?;
|
||||||
|
let ty = expanded_typed.ty.clone();
|
||||||
|
(BoundKind::Expansion {
|
||||||
|
original_call,
|
||||||
|
bound_expanded: Box::new(expanded_typed)
|
||||||
|
}, ty)
|
||||||
|
},
|
||||||
|
|
||||||
|
BoundKind::Extension(_ext) => {
|
||||||
|
return Err(format!("TypeChecking for extension '{}' not implemented", _ext.display_name()));
|
||||||
|
},
|
||||||
|
|
||||||
|
BoundKind::TailCall { .. } => unreachable!("TailCall generated before TypeChecker"),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Node {
|
||||||
|
identity: node.identity,
|
||||||
|
kind,
|
||||||
|
ty,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
+27
-11
@@ -5,7 +5,7 @@ use crate::ast::types::{Value, StaticType, Object};
|
|||||||
use crate::ast::nodes::{Node, UntypedKind, Symbol};
|
use crate::ast::nodes::{Node, UntypedKind, Symbol};
|
||||||
use crate::ast::parser::Parser;
|
use crate::ast::parser::Parser;
|
||||||
use crate::ast::compiler::binder::Binder;
|
use crate::ast::compiler::binder::Binder;
|
||||||
use crate::ast::compiler::BoundKind;
|
use crate::ast::compiler::{TypedNode, TypeChecker};
|
||||||
use crate::ast::vm::{VM, TracingObserver};
|
use crate::ast::vm::{VM, TracingObserver};
|
||||||
|
|
||||||
use crate::ast::compiler::tco::TCO;
|
use crate::ast::compiler::tco::TCO;
|
||||||
@@ -13,14 +13,16 @@ use crate::ast::compiler::dumper::Dumper;
|
|||||||
use crate::ast::compiler::macros::{MacroExpander, MacroRegistry, MacroEvaluator};
|
use crate::ast::compiler::macros::{MacroExpander, MacroRegistry, MacroEvaluator};
|
||||||
|
|
||||||
pub struct Environment {
|
pub struct Environment {
|
||||||
pub global_names: Rc<RefCell<HashMap<Symbol, (u32, StaticType)>>>,
|
pub global_names: Rc<RefCell<HashMap<Symbol, u32>>>,
|
||||||
|
pub global_types: Rc<RefCell<HashMap<u32, StaticType>>>,
|
||||||
pub global_values: Rc<RefCell<Vec<Value>>>,
|
pub global_values: Rc<RefCell<Vec<Value>>>,
|
||||||
pub debug_mode: bool,
|
pub debug_mode: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluator used during macro expansion to allow compile-time logic.
|
/// Evaluator used during macro expansion to allow compile-time logic.
|
||||||
struct RuntimeMacroEvaluator {
|
struct RuntimeMacroEvaluator {
|
||||||
global_names: Rc<RefCell<HashMap<Symbol, (u32, StaticType)>>>,
|
global_names: Rc<RefCell<HashMap<Symbol, u32>>>,
|
||||||
|
global_types: Rc<RefCell<HashMap<u32, StaticType>>>,
|
||||||
global_values: Rc<RefCell<Vec<Value>>>,
|
global_values: Rc<RefCell<Vec<Value>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,8 +36,12 @@ impl MacroEvaluator for RuntimeMacroEvaluator {
|
|||||||
|
|
||||||
// 2. Full evaluation for complex compile-time expressions
|
// 2. Full evaluation for complex compile-time expressions
|
||||||
let bound_ast = Binder::bind_root(self.global_names.clone(), node)?;
|
let bound_ast = Binder::bind_root(self.global_names.clone(), node)?;
|
||||||
|
|
||||||
|
let checker = TypeChecker::new(self.global_types.clone());
|
||||||
|
let typed_ast = checker.check(bound_ast)?;
|
||||||
|
|
||||||
let mut vm = VM::new(self.global_values.clone());
|
let mut vm = VM::new(self.global_values.clone());
|
||||||
vm.run(&bound_ast)
|
vm.run(&typed_ast)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,6 +55,7 @@ impl Environment {
|
|||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let env = Self {
|
let env = Self {
|
||||||
global_names: Rc::new(RefCell::new(HashMap::new())),
|
global_names: Rc::new(RefCell::new(HashMap::new())),
|
||||||
|
global_types: Rc::new(RefCell::new(HashMap::new())),
|
||||||
global_values: Rc::new(RefCell::new(Vec::new())),
|
global_values: Rc::new(RefCell::new(Vec::new())),
|
||||||
debug_mode: false,
|
debug_mode: false,
|
||||||
};
|
};
|
||||||
@@ -63,6 +70,7 @@ impl Environment {
|
|||||||
fn get_expander(&self) -> MacroExpander<RuntimeMacroEvaluator> {
|
fn get_expander(&self) -> MacroExpander<RuntimeMacroEvaluator> {
|
||||||
let evaluator = RuntimeMacroEvaluator {
|
let evaluator = RuntimeMacroEvaluator {
|
||||||
global_names: self.global_names.clone(),
|
global_names: self.global_names.clone(),
|
||||||
|
global_types: self.global_types.clone(),
|
||||||
global_values: self.global_values.clone(),
|
global_values: self.global_values.clone(),
|
||||||
};
|
};
|
||||||
MacroExpander::new(MacroRegistry::new(), evaluator)
|
MacroExpander::new(MacroRegistry::new(), evaluator)
|
||||||
@@ -70,10 +78,12 @@ impl Environment {
|
|||||||
|
|
||||||
pub fn register_native(&self, name: &str, ty: StaticType, func: impl Fn(Vec<Value>) -> Value + 'static) {
|
pub fn register_native(&self, name: &str, ty: StaticType, func: impl Fn(Vec<Value>) -> Value + 'static) {
|
||||||
let mut names = self.global_names.borrow_mut();
|
let mut names = self.global_names.borrow_mut();
|
||||||
|
let mut types = self.global_types.borrow_mut();
|
||||||
let mut values = self.global_values.borrow_mut();
|
let mut values = self.global_values.borrow_mut();
|
||||||
|
|
||||||
let idx = values.len() as u32;
|
let idx = values.len() as u32;
|
||||||
names.insert(Symbol::from(name), (idx, ty));
|
names.insert(Symbol::from(name), idx);
|
||||||
|
types.insert(idx, ty);
|
||||||
values.push(Value::Function(Rc::new(func)));
|
values.push(Value::Function(Rc::new(func)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,8 +160,8 @@ impl Environment {
|
|||||||
Ok(Dumper::dump(&linked))
|
Ok(Dumper::dump(&linked))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Frontend: Parse -> Expand Macros -> Bind & Type Check
|
/// Frontend: Parse -> Expand Macros -> Bind -> Type Check
|
||||||
pub fn compile(&self, source: &str) -> Result<Node<BoundKind, StaticType>, String> {
|
pub fn compile(&self, source: &str) -> Result<TypedNode, String> {
|
||||||
// 1. Parse
|
// 1. Parse
|
||||||
let mut parser = Parser::new(source)?;
|
let mut parser = Parser::new(source)?;
|
||||||
let untyped_ast = parser.parse_expression()?;
|
let untyped_ast = parser.parse_expression()?;
|
||||||
@@ -164,17 +174,23 @@ impl Environment {
|
|||||||
// 3. Expand Macros
|
// 3. Expand Macros
|
||||||
let expanded_ast = self.get_expander().expand(untyped_ast)?;
|
let expanded_ast = self.get_expander().expand(untyped_ast)?;
|
||||||
|
|
||||||
// 4. Bind & Type Check
|
// 4. Bind
|
||||||
Binder::bind_root(self.global_names.clone(), &expanded_ast)
|
let bound_ast = Binder::bind_root(self.global_names.clone(), &expanded_ast)?;
|
||||||
|
|
||||||
|
// 5. Type Check
|
||||||
|
let checker = TypeChecker::new(self.global_types.clone());
|
||||||
|
let typed_ast = checker.check(bound_ast)?;
|
||||||
|
|
||||||
|
Ok(typed_ast)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Backend: Optimization (TCO, etc.)
|
/// Backend: Optimization (TCO, etc.)
|
||||||
pub fn link(&self, node: Node<BoundKind, StaticType>) -> Node<BoundKind, StaticType> {
|
pub fn link(&self, node: TypedNode) -> TypedNode {
|
||||||
TCO::optimize(node)
|
TCO::optimize(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Runtime: Execute the linked AST in the VM
|
/// Runtime: Execute the linked AST in the VM
|
||||||
pub fn run(&self, node: &Node<BoundKind, StaticType>) -> Result<Value, String> {
|
pub fn run(&self, node: &TypedNode) -> Result<Value, String> {
|
||||||
let mut vm = VM::new(self.global_values.clone());
|
let mut vm = VM::new(self.global_values.clone());
|
||||||
vm.run(node)
|
vm.run(node)
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -3,8 +3,7 @@ use std::cell::RefCell;
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use crate::ast::compiler::bound_nodes::{Address, BoundKind, TypedNode};
|
use crate::ast::compiler::bound_nodes::{Address, BoundKind, TypedNode};
|
||||||
use crate::ast::nodes::Node;
|
use crate::ast::types::{Value, Object};
|
||||||
use crate::ast::types::{Value, Object, StaticType};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Closure {
|
pub struct Closure {
|
||||||
@@ -455,7 +454,8 @@ impl VM {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::ast::types::{SourceLocation, NodeIdentity};
|
use crate::ast::nodes::Node;
|
||||||
|
use crate::ast::types::{SourceLocation, NodeIdentity, StaticType};
|
||||||
|
|
||||||
fn make_dummy_identity() -> Rc<NodeIdentity> {
|
fn make_dummy_identity() -> Rc<NodeIdentity> {
|
||||||
Rc::new(NodeIdentity {
|
Rc::new(NodeIdentity {
|
||||||
|
|||||||
@@ -80,8 +80,10 @@ mod tests {
|
|||||||
let mut binder = Binder::new(globals.clone());
|
let mut binder = Binder::new(globals.clone());
|
||||||
let bound_ast = binder.bind(&ast).expect("Failed to bind");
|
let bound_ast = binder.bind(&ast).expect("Failed to bind");
|
||||||
|
|
||||||
// BRIDGE: Binder -> VM requires a TypedNode
|
// Use the real TypeChecker instead of the dummy placeholder
|
||||||
let typed_ast = crate::ast::environment::dummy_type_check(bound_ast);
|
let global_types = Rc::new(RefCell::new(std::collections::HashMap::new()));
|
||||||
|
let checker = crate::ast::compiler::TypeChecker::new(global_types);
|
||||||
|
let typed_ast = checker.check(bound_ast).expect("Type check failed");
|
||||||
|
|
||||||
let vm_globals = Rc::new(RefCell::new(Vec::new()));
|
let vm_globals = Rc::new(RefCell::new(Vec::new()));
|
||||||
let mut vm = VM::new(vm_globals);
|
let mut vm = VM::new(vm_globals);
|
||||||
|
|||||||
Reference in New Issue
Block a user