feat: Implement AST binder and VM
Adds a new `Binder` struct that traverses the AST and resolves variable references to concrete `Address` types (Local, Upvalue, Global). This information is crucial for the Virtual Machine's execution phase. Introduces the `BoundKind` enum to represent the AST after binding. The `VM` is updated to handle `BoundKind` nodes and execute the bound AST. It now manages a call stack, local variables, and closures for function calls and upvalue capturing. The `Environment` struct is enhanced to manage global variables and provide a unified interface for parsing, binding, and executing scripts. It also includes basic standard library functions.
This commit is contained in:
+218
@@ -0,0 +1,218 @@
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::any::Any;
|
||||
use crate::ast::compiler::bound_nodes::{Address, BoundKind};
|
||||
use crate::ast::nodes::Node;
|
||||
use crate::ast::types::{Value, Object};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Closure {
|
||||
pub function_node: Arc<Node<BoundKind>>,
|
||||
pub upvalues: Vec<Value>,
|
||||
}
|
||||
|
||||
impl Object for Closure {
|
||||
fn type_name(&self) -> &'static str {
|
||||
"closure"
|
||||
}
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct CallFrame {
|
||||
stack_base: usize,
|
||||
closure: Option<Arc<Closure>>,
|
||||
}
|
||||
|
||||
pub struct VM {
|
||||
stack: Vec<Value>,
|
||||
globals: Arc<Mutex<Vec<Value>>>,
|
||||
frames: Vec<CallFrame>,
|
||||
}
|
||||
|
||||
impl VM {
|
||||
pub fn new(globals: Arc<Mutex<Vec<Value>>>) -> Self {
|
||||
Self {
|
||||
stack: Vec::new(),
|
||||
globals,
|
||||
frames: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(&mut self, root: &Node<BoundKind>) -> Result<Value, String> {
|
||||
self.stack.clear();
|
||||
self.frames.clear();
|
||||
|
||||
// Push global script frame
|
||||
self.frames.push(CallFrame {
|
||||
stack_base: 0,
|
||||
closure: None,
|
||||
});
|
||||
|
||||
let result = self.eval(root);
|
||||
|
||||
self.frames.pop();
|
||||
result
|
||||
}
|
||||
|
||||
fn eval(&mut self, node: &Node<BoundKind>) -> Result<Value, String> {
|
||||
match &node.kind {
|
||||
BoundKind::Nop => Ok(Value::Void),
|
||||
BoundKind::Constant(v) => Ok(v.clone()),
|
||||
|
||||
BoundKind::DefGlobal { global_index, value } => {
|
||||
let val = self.eval(value)?;
|
||||
let idx = *global_index as usize;
|
||||
let mut globals = self.globals.lock().unwrap();
|
||||
if idx >= globals.len() {
|
||||
globals.resize(idx + 1, Value::Void);
|
||||
}
|
||||
globals[idx] = val.clone();
|
||||
Ok(val)
|
||||
},
|
||||
|
||||
BoundKind::Get(addr) => self.get_value(*addr),
|
||||
|
||||
BoundKind::Set { addr, value } => {
|
||||
let val = self.eval(value)?;
|
||||
self.set_value(*addr, val.clone())?;
|
||||
Ok(val)
|
||||
},
|
||||
|
||||
BoundKind::If { cond, then_br, else_br } => {
|
||||
let c = self.eval(cond)?;
|
||||
if c.is_truthy() {
|
||||
self.eval(then_br)
|
||||
} else if let Some(e) = else_br {
|
||||
self.eval(e)
|
||||
} else {
|
||||
Ok(Value::Void)
|
||||
}
|
||||
},
|
||||
|
||||
BoundKind::Block { exprs } => {
|
||||
let mut last = Value::Void;
|
||||
for e in exprs {
|
||||
last = self.eval(e)?;
|
||||
}
|
||||
Ok(last)
|
||||
},
|
||||
|
||||
BoundKind::Lambda { param_count: _, upvalues, body } => {
|
||||
let mut captured = Vec::with_capacity(upvalues.len());
|
||||
for addr in upvalues {
|
||||
captured.push(self.get_value(*addr)?);
|
||||
}
|
||||
|
||||
let closure = Closure {
|
||||
function_node: body.clone(),
|
||||
upvalues: captured,
|
||||
};
|
||||
|
||||
Ok(Value::Object(Arc::new(closure)))
|
||||
},
|
||||
|
||||
BoundKind::Call { callee, args } => {
|
||||
let func_val = self.eval(callee)?;
|
||||
|
||||
let mut arg_vals = Vec::with_capacity(args.len());
|
||||
for arg in args {
|
||||
arg_vals.push(self.eval(arg)?);
|
||||
}
|
||||
|
||||
match func_val {
|
||||
Value::Function(f) => Ok(f(arg_vals)),
|
||||
Value::Object(obj) => {
|
||||
if let Some(closure) = obj.as_any().downcast_ref::<Closure>() {
|
||||
let old_stack_top = self.stack.len();
|
||||
let closure_arc = Arc::new(closure.clone());
|
||||
|
||||
self.stack.extend(arg_vals);
|
||||
|
||||
self.frames.push(CallFrame {
|
||||
stack_base: old_stack_top,
|
||||
closure: Some(closure_arc.clone()),
|
||||
});
|
||||
|
||||
let result = self.eval(&closure.function_node);
|
||||
|
||||
self.frames.pop();
|
||||
self.stack.truncate(old_stack_top);
|
||||
|
||||
result
|
||||
} else {
|
||||
Err(format!("Object is not a closure: {}", obj.type_name()))
|
||||
}
|
||||
},
|
||||
_ => Err(format!("Attempt to call non-function: {}", func_val)),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_value(&self, addr: Address) -> Result<Value, String> {
|
||||
match addr {
|
||||
Address::Local(idx) => {
|
||||
let frame = self.frames.last().ok_or("No call frame")?;
|
||||
let abs_index = frame.stack_base + (idx as usize);
|
||||
if abs_index < self.stack.len() {
|
||||
Ok(self.stack[abs_index].clone())
|
||||
} else {
|
||||
Err(format!("Stack underflow access local {}", idx))
|
||||
}
|
||||
},
|
||||
Address::Global(idx) => {
|
||||
let idx = idx as usize;
|
||||
let globals = self.globals.lock().unwrap();
|
||||
if idx < globals.len() {
|
||||
Ok(globals[idx].clone())
|
||||
} else {
|
||||
Err(format!("Global access out of bounds {}", idx))
|
||||
}
|
||||
},
|
||||
Address::Upvalue(idx) => {
|
||||
let frame = self.frames.last().ok_or("No call frame")?;
|
||||
if let Some(closure) = &frame.closure {
|
||||
let idx = idx as usize;
|
||||
if idx < closure.upvalues.len() {
|
||||
Ok(closure.upvalues[idx].clone())
|
||||
} else {
|
||||
Err(format!("Upvalue access out of bounds {}", idx))
|
||||
}
|
||||
} else {
|
||||
Err("Current frame has no closure (cannot access upvalues)".to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn set_value(&mut self, addr: Address, value: Value) -> Result<(), String> {
|
||||
match addr {
|
||||
Address::Local(idx) => {
|
||||
let frame = self.frames.last().ok_or("No call frame")?;
|
||||
let abs_index = frame.stack_base + (idx as usize);
|
||||
if abs_index >= self.stack.len() {
|
||||
if abs_index == self.stack.len() {
|
||||
self.stack.push(value);
|
||||
} else {
|
||||
return Err(format!("Stack gap write local {}", idx));
|
||||
}
|
||||
} else {
|
||||
self.stack[abs_index] = value;
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
Address::Global(idx) => {
|
||||
let idx = idx as usize;
|
||||
let mut globals = self.globals.lock().unwrap();
|
||||
if idx >= globals.len() {
|
||||
globals.resize(idx + 1, Value::Void);
|
||||
}
|
||||
globals[idx] = value;
|
||||
Ok(())
|
||||
},
|
||||
Address::Upvalue(_) => Err("Cannot assign to upvalue (Closure Upvalues are immutable)".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user