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:
+27
-11
@@ -5,7 +5,7 @@ use crate::ast::types::{Value, StaticType, Object};
|
||||
use crate::ast::nodes::{Node, UntypedKind, Symbol};
|
||||
use crate::ast::parser::Parser;
|
||||
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::compiler::tco::TCO;
|
||||
@@ -13,14 +13,16 @@ use crate::ast::compiler::dumper::Dumper;
|
||||
use crate::ast::compiler::macros::{MacroExpander, MacroRegistry, MacroEvaluator};
|
||||
|
||||
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 debug_mode: bool,
|
||||
}
|
||||
|
||||
/// Evaluator used during macro expansion to allow compile-time logic.
|
||||
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>>>,
|
||||
}
|
||||
|
||||
@@ -34,8 +36,12 @@ impl MacroEvaluator for RuntimeMacroEvaluator {
|
||||
|
||||
// 2. Full evaluation for complex compile-time expressions
|
||||
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());
|
||||
vm.run(&bound_ast)
|
||||
vm.run(&typed_ast)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +55,7 @@ impl Environment {
|
||||
pub fn new() -> Self {
|
||||
let env = Self {
|
||||
global_names: Rc::new(RefCell::new(HashMap::new())),
|
||||
global_types: Rc::new(RefCell::new(HashMap::new())),
|
||||
global_values: Rc::new(RefCell::new(Vec::new())),
|
||||
debug_mode: false,
|
||||
};
|
||||
@@ -63,6 +70,7 @@ impl Environment {
|
||||
fn get_expander(&self) -> MacroExpander<RuntimeMacroEvaluator> {
|
||||
let evaluator = RuntimeMacroEvaluator {
|
||||
global_names: self.global_names.clone(),
|
||||
global_types: self.global_types.clone(),
|
||||
global_values: self.global_values.clone(),
|
||||
};
|
||||
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) {
|
||||
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 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)));
|
||||
}
|
||||
|
||||
@@ -150,8 +160,8 @@ impl Environment {
|
||||
Ok(Dumper::dump(&linked))
|
||||
}
|
||||
|
||||
/// Frontend: Parse -> Expand Macros -> Bind & Type Check
|
||||
pub fn compile(&self, source: &str) -> Result<Node<BoundKind, StaticType>, String> {
|
||||
/// Frontend: Parse -> Expand Macros -> Bind -> Type Check
|
||||
pub fn compile(&self, source: &str) -> Result<TypedNode, String> {
|
||||
// 1. Parse
|
||||
let mut parser = Parser::new(source)?;
|
||||
let untyped_ast = parser.parse_expression()?;
|
||||
@@ -164,17 +174,23 @@ impl Environment {
|
||||
// 3. Expand Macros
|
||||
let expanded_ast = self.get_expander().expand(untyped_ast)?;
|
||||
|
||||
// 4. Bind & Type Check
|
||||
Binder::bind_root(self.global_names.clone(), &expanded_ast)
|
||||
// 4. Bind
|
||||
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.)
|
||||
pub fn link(&self, node: Node<BoundKind, StaticType>) -> Node<BoundKind, StaticType> {
|
||||
pub fn link(&self, node: TypedNode) -> TypedNode {
|
||||
TCO::optimize(node)
|
||||
}
|
||||
|
||||
/// 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());
|
||||
vm.run(node)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user