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:
@@ -0,0 +1,219 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
use crate::ast::nodes::{Node, UntypedKind};
|
||||||
|
use crate::ast::compiler::bound_nodes::{BoundKind, Address};
|
||||||
|
use crate::ast::types::{Value, Identity};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct CompilerScope {
|
||||||
|
locals: HashMap<String, u32>,
|
||||||
|
slot_count: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CompilerScope {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
locals: HashMap::new(),
|
||||||
|
slot_count: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn define(&mut self, name: &str) -> u32 {
|
||||||
|
let slot = self.slot_count;
|
||||||
|
self.locals.insert(name.to_string(), slot);
|
||||||
|
self.slot_count += 1;
|
||||||
|
slot
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve(&self, name: &str) -> Option<u32> {
|
||||||
|
self.locals.get(name).copied()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FunctionCompiler {
|
||||||
|
scope: CompilerScope,
|
||||||
|
// Upvalues capture variables from the *immediate* enclosing function.
|
||||||
|
// The address stored here refers to a Local/Upvalue in the Parent Scope.
|
||||||
|
upvalues: Vec<Address>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FunctionCompiler {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
scope: CompilerScope::new(),
|
||||||
|
upvalues: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_upvalue(&mut self, addr: Address) -> u32 {
|
||||||
|
if let Some(idx) = self.upvalues.iter().position(|&a| a == addr) {
|
||||||
|
return idx as u32;
|
||||||
|
}
|
||||||
|
let idx = self.upvalues.len() as u32;
|
||||||
|
self.upvalues.push(addr);
|
||||||
|
idx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Binder {
|
||||||
|
functions: Vec<FunctionCompiler>,
|
||||||
|
globals: Arc<Mutex<HashMap<String, u32>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Binder {
|
||||||
|
pub fn new(globals: Arc<Mutex<HashMap<String, u32>>>) -> Self {
|
||||||
|
let mut binder = Self {
|
||||||
|
functions: Vec::new(),
|
||||||
|
globals,
|
||||||
|
};
|
||||||
|
binder.functions.push(FunctionCompiler::new());
|
||||||
|
binder
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bind(&mut self, node: &Node<UntypedKind>) -> Result<Node<BoundKind>, String> {
|
||||||
|
match &node.kind {
|
||||||
|
UntypedKind::Nop => Ok(self.make_node(node.identity.clone(), BoundKind::Nop)),
|
||||||
|
UntypedKind::Constant(v) => Ok(self.make_node(node.identity.clone(), BoundKind::Constant(v.clone()))),
|
||||||
|
|
||||||
|
UntypedKind::Identifier(name) => {
|
||||||
|
let addr = self.resolve_variable(name)?;
|
||||||
|
Ok(self.make_node(node.identity.clone(), BoundKind::Get(addr)))
|
||||||
|
},
|
||||||
|
|
||||||
|
UntypedKind::If { cond, then_br, else_br } => {
|
||||||
|
let cond = Box::new(self.bind(cond)?);
|
||||||
|
let then_br = Box::new(self.bind(then_br)?);
|
||||||
|
let else_br = match else_br {
|
||||||
|
Some(e) => Some(Box::new(self.bind(e)?)),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
Ok(self.make_node(node.identity.clone(), BoundKind::If { cond, then_br, else_br }))
|
||||||
|
},
|
||||||
|
|
||||||
|
UntypedKind::Def { name, value } => {
|
||||||
|
// Determine scope: Global (root) or Local (inside fn)?
|
||||||
|
if self.functions.len() == 1 {
|
||||||
|
// Global Definition
|
||||||
|
let val_node = self.bind(value)?;
|
||||||
|
let mut globals = self.globals.lock().unwrap();
|
||||||
|
let idx = if let Some(&idx) = globals.get(name.as_ref()) {
|
||||||
|
idx
|
||||||
|
} else {
|
||||||
|
let idx = globals.len() as u32;
|
||||||
|
globals.insert(name.to_string(), idx);
|
||||||
|
idx
|
||||||
|
};
|
||||||
|
Ok(self.make_node(node.identity.clone(), BoundKind::DefGlobal { global_index: idx, value: Box::new(val_node) }))
|
||||||
|
} else {
|
||||||
|
// Local Variable Definition
|
||||||
|
// We bind the value *before* defining the name to avoid self-reference in init?
|
||||||
|
// Usually yes: (def x 10) -> bind(10), define(x).
|
||||||
|
let val_node = self.bind(value)?;
|
||||||
|
let current_fn = self.functions.last_mut().unwrap();
|
||||||
|
let slot = current_fn.scope.define(name);
|
||||||
|
|
||||||
|
// We treat local 'def' as a Set to the new slot
|
||||||
|
Ok(self.make_node(node.identity.clone(), BoundKind::Set {
|
||||||
|
addr: Address::Local(slot),
|
||||||
|
value: Box::new(val_node)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
UntypedKind::Assign { target, value } => {
|
||||||
|
let val_node = self.bind(value)?;
|
||||||
|
|
||||||
|
if let UntypedKind::Identifier(name) = &target.kind {
|
||||||
|
let addr = self.resolve_variable(name)?;
|
||||||
|
Ok(self.make_node(node.identity.clone(), BoundKind::Set { addr, value: Box::new(val_node) }))
|
||||||
|
} else {
|
||||||
|
Err("Assignment target must be an identifier".to_string())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
UntypedKind::Lambda { params, body } => {
|
||||||
|
self.functions.push(FunctionCompiler::new());
|
||||||
|
|
||||||
|
{
|
||||||
|
let current_fn = self.functions.last_mut().unwrap();
|
||||||
|
for param in params {
|
||||||
|
current_fn.scope.define(param);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let body_bound = self.bind(body)?;
|
||||||
|
|
||||||
|
let compiled_fn = self.functions.pop().unwrap();
|
||||||
|
let upvalues = compiled_fn.upvalues;
|
||||||
|
|
||||||
|
Ok(self.make_node(node.identity.clone(), BoundKind::Lambda {
|
||||||
|
param_count: params.len() as u32,
|
||||||
|
upvalues,
|
||||||
|
body: Arc::new(body_bound),
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
|
||||||
|
UntypedKind::Call { callee, args } => {
|
||||||
|
let callee = Box::new(self.bind(callee)?);
|
||||||
|
let mut bound_args = Vec::new();
|
||||||
|
for arg in args {
|
||||||
|
bound_args.push(self.bind(arg)?);
|
||||||
|
}
|
||||||
|
Ok(self.make_node(node.identity.clone(), BoundKind::Call { callee, args: bound_args }))
|
||||||
|
},
|
||||||
|
|
||||||
|
UntypedKind::Block { exprs } => {
|
||||||
|
let mut bound_exprs = Vec::new();
|
||||||
|
for expr in exprs {
|
||||||
|
bound_exprs.push(self.bind(expr)?);
|
||||||
|
}
|
||||||
|
Ok(self.make_node(node.identity.clone(), BoundKind::Block { exprs: bound_exprs }))
|
||||||
|
},
|
||||||
|
|
||||||
|
UntypedKind::Extension(_) => {
|
||||||
|
Err("Custom extensions not supported in Binder yet".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_variable(&mut self, name: &str) -> Result<Address, String> {
|
||||||
|
let current_fn_idx = self.functions.len() - 1;
|
||||||
|
|
||||||
|
// 1. Try local in current function
|
||||||
|
if let Some(slot) = self.functions[current_fn_idx].scope.resolve(name) {
|
||||||
|
return Ok(Address::Local(slot));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Try enclosing scopes (capture chain)
|
||||||
|
let mut captured_addr: Option<Address> = None;
|
||||||
|
|
||||||
|
// Walk backwards from parent of current function up to root (0)
|
||||||
|
// We look for where the variable is DEFINED.
|
||||||
|
for i in (0..current_fn_idx).rev() {
|
||||||
|
let func = &self.functions[i];
|
||||||
|
if let Some(slot) = func.scope.resolve(name) {
|
||||||
|
// Found definition! It's a Local variable in function 'i'.
|
||||||
|
captured_addr = Some(Address::Local(slot));
|
||||||
|
|
||||||
|
// Now we must propagate this capture down through all functions from i+1 to current.
|
||||||
|
let mut addr = captured_addr.unwrap();
|
||||||
|
for k in (i + 1)..=current_fn_idx {
|
||||||
|
addr = Address::Upvalue(self.functions[k].add_upvalue(addr));
|
||||||
|
}
|
||||||
|
return Ok(addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Try Global
|
||||||
|
let globals = self.globals.lock().unwrap();
|
||||||
|
if let Some(&idx) = globals.get(name) {
|
||||||
|
return Ok(Address::Global(idx));
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(format!("Undefined variable '{}'", name))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_node(&self, identity: Identity, kind: BoundKind) -> Node<BoundKind> {
|
||||||
|
Node { identity, kind }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
use crate::ast::types::Value;
|
||||||
|
use crate::ast::nodes::{Node, CustomNode};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
pub enum Address {
|
||||||
|
Local(u32), // Stack-Slot index (relative to frame base)
|
||||||
|
Upvalue(u32), // Index in the closure's upvalue array
|
||||||
|
Global(u32), // Index in the global environment vector
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum BoundKind {
|
||||||
|
Nop,
|
||||||
|
Constant(Value),
|
||||||
|
|
||||||
|
// Variable Access (Resolved)
|
||||||
|
Get(Address),
|
||||||
|
|
||||||
|
// Variable Update (Resolved)
|
||||||
|
Set {
|
||||||
|
addr: Address,
|
||||||
|
value: Box<Node<BoundKind>>,
|
||||||
|
},
|
||||||
|
|
||||||
|
If {
|
||||||
|
cond: Box<Node<BoundKind>>,
|
||||||
|
then_br: Box<Node<BoundKind>>,
|
||||||
|
else_br: Option<Box<Node<BoundKind>>>,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Global Definition (Locals are implicit by stack position)
|
||||||
|
DefGlobal {
|
||||||
|
global_index: u32,
|
||||||
|
value: Box<Node<BoundKind>>,
|
||||||
|
},
|
||||||
|
|
||||||
|
Lambda {
|
||||||
|
param_count: u32,
|
||||||
|
// The list of variables captured from enclosing scopes
|
||||||
|
upvalues: Vec<Address>,
|
||||||
|
body: Arc<Node<BoundKind>>,
|
||||||
|
},
|
||||||
|
|
||||||
|
Call {
|
||||||
|
callee: Box<Node<BoundKind>>,
|
||||||
|
args: Vec<Node<BoundKind>>,
|
||||||
|
},
|
||||||
|
|
||||||
|
Block {
|
||||||
|
exprs: Vec<Node<BoundKind>>,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Extension points (need to be adapted for bound nodes if they use variables)
|
||||||
|
// For now, we assume extensions are self-contained or handled dynamically.
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
pub mod binder;
|
||||||
|
pub mod bound_nodes;
|
||||||
|
|
||||||
|
pub use binder::*;
|
||||||
|
pub use bound_nodes::*;
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use crate::ast::types::Value;
|
||||||
|
use crate::ast::parser::Parser;
|
||||||
|
use crate::ast::compiler::binder::Binder;
|
||||||
|
use crate::ast::vm::VM;
|
||||||
|
|
||||||
|
pub struct Environment {
|
||||||
|
pub global_names: Arc<Mutex<HashMap<String, u32>>>,
|
||||||
|
pub global_values: Arc<Mutex<Vec<Value>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Environment {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let env = Self {
|
||||||
|
global_names: Arc::new(Mutex::new(HashMap::new())),
|
||||||
|
global_values: Arc::new(Mutex::new(Vec::new())),
|
||||||
|
};
|
||||||
|
env.register_stdlib();
|
||||||
|
env
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn register_native(&self, name: &str, func: fn(Vec<Value>) -> Value) {
|
||||||
|
let mut names = self.global_names.lock().unwrap();
|
||||||
|
let mut values = self.global_values.lock().unwrap();
|
||||||
|
|
||||||
|
let idx = values.len() as u32;
|
||||||
|
names.insert(name.to_string(), idx);
|
||||||
|
values.push(Value::Function(Arc::new(move |args| func(args))));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register_stdlib(&self) {
|
||||||
|
// Simple math for testing
|
||||||
|
self.register_native("+", |args| {
|
||||||
|
let mut acc = 0.0;
|
||||||
|
for arg in args {
|
||||||
|
if let Value::Int(i) = arg { acc += i as f64; }
|
||||||
|
else if let Value::Float(f) = arg { acc += f; }
|
||||||
|
}
|
||||||
|
if acc.fract() == 0.0 { Value::Int(acc as i64) } else { Value::Float(acc) }
|
||||||
|
});
|
||||||
|
|
||||||
|
self.register_native(">", |args| {
|
||||||
|
if args.len() != 2 { return Value::Void; }
|
||||||
|
match (&args[0], &args[1]) {
|
||||||
|
(Value::Int(a), Value::Int(b)) => Value::Bool(a > b),
|
||||||
|
_ => Value::Bool(false),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run_script(&self, source: &str) -> Result<Value, String> {
|
||||||
|
// 1. Parse
|
||||||
|
let mut parser = Parser::new(source)?;
|
||||||
|
let untyped_ast = parser.parse_expression()?; // Or loop for script
|
||||||
|
|
||||||
|
// 2. Bind
|
||||||
|
let mut binder = Binder::new(self.global_names.clone());
|
||||||
|
let bound_ast = binder.bind(&untyped_ast)?;
|
||||||
|
|
||||||
|
// 3. Execute
|
||||||
|
// Note: VM needs to borrow globals, not own them exclusively?
|
||||||
|
// My VM impl takes Vec<Value>, but here we have Arc<Mutex<Vec>>.
|
||||||
|
// I need to update VM to accept shared globals.
|
||||||
|
|
||||||
|
let mut vm = VM::new(self.global_values.clone());
|
||||||
|
vm.run(&bound_ast)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,8 +2,14 @@ pub mod types;
|
|||||||
pub mod nodes;
|
pub mod nodes;
|
||||||
pub mod lexer;
|
pub mod lexer;
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
|
pub mod compiler;
|
||||||
|
pub mod vm;
|
||||||
|
pub mod environment;
|
||||||
|
|
||||||
pub use types::*;
|
pub use types::*;
|
||||||
pub use nodes::*;
|
pub use nodes::*;
|
||||||
pub use lexer::*;
|
pub use lexer::*;
|
||||||
pub use parser::*;
|
pub use parser::*;
|
||||||
|
pub use compiler::*;
|
||||||
|
pub use vm::*;
|
||||||
|
pub use environment::*;
|
||||||
|
|||||||
+11
-2
@@ -3,6 +3,7 @@ use std::collections::HashMap;
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
use std::any::Any;
|
||||||
|
|
||||||
/// Simple source location
|
/// Simple source location
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
@@ -48,10 +49,16 @@ impl Keyword {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Interface for custom objects (Closures, Series, Streams)
|
||||||
|
pub trait Object: fmt::Debug + Send + Sync {
|
||||||
|
fn type_name(&self) -> &'static str;
|
||||||
|
fn as_any(&self) -> &dyn Any;
|
||||||
|
}
|
||||||
|
|
||||||
/// Core data value in Myc Script (similar to TDataValue)
|
/// Core data value in Myc Script (similar to TDataValue)
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum Value {
|
pub enum Value {
|
||||||
Void, // Maps to vkVoid
|
Void,
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
Int(i64),
|
Int(i64),
|
||||||
Float(f64),
|
Float(f64),
|
||||||
@@ -60,6 +67,7 @@ pub enum Value {
|
|||||||
List(Arc<Vec<Value>>),
|
List(Arc<Vec<Value>>),
|
||||||
Record(Arc<HashMap<Keyword, Value>>),
|
Record(Arc<HashMap<Keyword, Value>>),
|
||||||
Function(Arc<dyn Fn(Vec<Value>) -> Value + Send + Sync>),
|
Function(Arc<dyn Fn(Vec<Value>) -> Value + Send + Sync>),
|
||||||
|
Object(Arc<dyn Object>), // For compiled Closures and other opaque types
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Value {
|
impl Value {
|
||||||
@@ -83,7 +91,8 @@ impl fmt::Display for Value {
|
|||||||
Value::Keyword(k) => write!(f, ":{}", k.name()),
|
Value::Keyword(k) => write!(f, ":{}", k.name()),
|
||||||
Value::List(l) => write!(f, "<list[{}]>", l.len()),
|
Value::List(l) => write!(f, "<list[{}]>", l.len()),
|
||||||
Value::Record(r) => write!(f, "<record[{}]>", r.len()),
|
Value::Record(r) => write!(f, "<record[{}]>", r.len()),
|
||||||
Value::Function(_) => write!(f, "<fn>"),
|
Value::Function(_) => write!(f, "<native fn>"),
|
||||||
|
Value::Object(o) => write!(f, "<{}>", o.type_name()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+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()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+11
-27
@@ -1,4 +1,5 @@
|
|||||||
use eframe::egui;
|
use eframe::egui;
|
||||||
|
use crate::ast::environment::Environment;
|
||||||
|
|
||||||
pub mod ast;
|
pub mod ast;
|
||||||
|
|
||||||
@@ -17,6 +18,7 @@ fn main() -> eframe::Result {
|
|||||||
struct CompilerApp {
|
struct CompilerApp {
|
||||||
source_code: String,
|
source_code: String,
|
||||||
output_log: String,
|
output_log: String,
|
||||||
|
env: Environment,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for CompilerApp {
|
impl Default for CompilerApp {
|
||||||
@@ -24,6 +26,7 @@ impl Default for CompilerApp {
|
|||||||
Self {
|
Self {
|
||||||
source_code: String::new(),
|
source_code: String::new(),
|
||||||
output_log: String::from("Ready to compile..."),
|
output_log: String::from("Ready to compile..."),
|
||||||
|
env: Environment::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -40,35 +43,16 @@ impl eframe::App for CompilerApp {
|
|||||||
|
|
||||||
// Compile Button (at the top of the bottom panel)
|
// Compile Button (at the top of the bottom panel)
|
||||||
if ui.button("Compile").clicked() {
|
if ui.button("Compile").clicked() {
|
||||||
use crate::ast::parser::Parser;
|
match self.env.run_script(&self.source_code) {
|
||||||
use crate::ast::nodes::Context;
|
Ok(result) => {
|
||||||
|
self.output_log = format!(
|
||||||
let mut parser = match Parser::new(&self.source_code) {
|
"Execution Successful.\nResult: {}\n\nFinished at {:?}",
|
||||||
Ok(p) => p,
|
result,
|
||||||
Err(e) => {
|
std::time::SystemTime::now(),
|
||||||
self.output_log = format!("Lexer Error: {}", e);
|
);
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
match parser.parse_expression() {
|
|
||||||
Ok(ast) => {
|
|
||||||
let mut context = Context::new(); // Use new() which registers stdlib
|
|
||||||
match ast.eval(&mut context) {
|
|
||||||
Ok(result) => {
|
|
||||||
self.output_log = format!(
|
|
||||||
"AST Parsed & Evaluated Successfully.\nResult: {}\n\nFinished at {:?}",
|
|
||||||
result,
|
|
||||||
std::time::SystemTime::now(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
self.output_log = format!("Runtime Error: {}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
self.output_log = format!("Parser Error: {}", e);
|
self.output_log = format!("Error: {}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user