diff --git a/src/ast/compiler/binder.rs b/src/ast/compiler/binder.rs new file mode 100644 index 0000000..d0157ea --- /dev/null +++ b/src/ast/compiler/binder.rs @@ -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, + 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 { + 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
, +} + +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, + globals: Arc>>, +} + +impl Binder { + pub fn new(globals: Arc>>) -> Self { + let mut binder = Self { + functions: Vec::new(), + globals, + }; + binder.functions.push(FunctionCompiler::new()); + binder + } + + pub fn bind(&mut self, node: &Node) -> Result, 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 { + 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
= 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 { + Node { identity, kind } + } +} diff --git a/src/ast/compiler/bound_nodes.rs b/src/ast/compiler/bound_nodes.rs new file mode 100644 index 0000000..fab2b70 --- /dev/null +++ b/src/ast/compiler/bound_nodes.rs @@ -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>, + }, + + If { + cond: Box>, + then_br: Box>, + else_br: Option>>, + }, + + // Global Definition (Locals are implicit by stack position) + DefGlobal { + global_index: u32, + value: Box>, + }, + + Lambda { + param_count: u32, + // The list of variables captured from enclosing scopes + upvalues: Vec
, + body: Arc>, + }, + + Call { + callee: Box>, + args: Vec>, + }, + + Block { + exprs: Vec>, + }, + + // Extension points (need to be adapted for bound nodes if they use variables) + // For now, we assume extensions are self-contained or handled dynamically. +} diff --git a/src/ast/compiler/mod.rs b/src/ast/compiler/mod.rs new file mode 100644 index 0000000..95b4160 --- /dev/null +++ b/src/ast/compiler/mod.rs @@ -0,0 +1,5 @@ +pub mod binder; +pub mod bound_nodes; + +pub use binder::*; +pub use bound_nodes::*; diff --git a/src/ast/environment.rs b/src/ast/environment.rs new file mode 100644 index 0000000..961fd97 --- /dev/null +++ b/src/ast/environment.rs @@ -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>>, + pub global_values: Arc>>, +} + +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) { + 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 { + // 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, but here we have Arc>. + // I need to update VM to accept shared globals. + + let mut vm = VM::new(self.global_values.clone()); + vm.run(&bound_ast) + } +} diff --git a/src/ast/mod.rs b/src/ast/mod.rs index d4f61b9..3f46f68 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -2,8 +2,14 @@ pub mod types; pub mod nodes; pub mod lexer; pub mod parser; +pub mod compiler; +pub mod vm; +pub mod environment; pub use types::*; pub use nodes::*; pub use lexer::*; pub use parser::*; +pub use compiler::*; +pub use vm::*; +pub use environment::*; diff --git a/src/ast/types.rs b/src/ast/types.rs index cae0732..9366c2a 100644 --- a/src/ast/types.rs +++ b/src/ast/types.rs @@ -3,6 +3,7 @@ use std::collections::HashMap; use std::fmt; use std::sync::Mutex; use lazy_static::lazy_static; +use std::any::Any; /// Simple source location #[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) #[derive(Clone)] pub enum Value { - Void, // Maps to vkVoid + Void, Bool(bool), Int(i64), Float(f64), @@ -60,6 +67,7 @@ pub enum Value { List(Arc>), Record(Arc>), Function(Arc) -> Value + Send + Sync>), + Object(Arc), // For compiled Closures and other opaque types } impl Value { @@ -83,7 +91,8 @@ impl fmt::Display for Value { Value::Keyword(k) => write!(f, ":{}", k.name()), Value::List(l) => write!(f, "", l.len()), Value::Record(r) => write!(f, "", r.len()), - Value::Function(_) => write!(f, ""), + Value::Function(_) => write!(f, ""), + Value::Object(o) => write!(f, "<{}>", o.type_name()), } } } diff --git a/src/ast/vm.rs b/src/ast/vm.rs new file mode 100644 index 0000000..fc887e0 --- /dev/null +++ b/src/ast/vm.rs @@ -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>, + pub upvalues: Vec, +} + +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>, +} + +pub struct VM { + stack: Vec, + globals: Arc>>, + frames: Vec, +} + +impl VM { + pub fn new(globals: Arc>>) -> Self { + Self { + stack: Vec::new(), + globals, + frames: Vec::new(), + } + } + + pub fn run(&mut self, root: &Node) -> Result { + 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) -> Result { + 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::() { + 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 { + 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()), + } + } +} diff --git a/src/main.rs b/src/main.rs index 35925d0..41fa359 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ use eframe::egui; +use crate::ast::environment::Environment; pub mod ast; @@ -17,6 +18,7 @@ fn main() -> eframe::Result { struct CompilerApp { source_code: String, output_log: String, + env: Environment, } impl Default for CompilerApp { @@ -24,6 +26,7 @@ impl Default for CompilerApp { Self { source_code: String::new(), 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) if ui.button("Compile").clicked() { - use crate::ast::parser::Parser; - use crate::ast::nodes::Context; - - let mut parser = match Parser::new(&self.source_code) { - Ok(p) => p, - Err(e) => { - 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); - } - } + match self.env.run_script(&self.source_code) { + Ok(result) => { + self.output_log = format!( + "Execution Successful.\nResult: {}\n\nFinished at {:?}", + result, + std::time::SystemTime::now(), + ); } Err(e) => { - self.output_log = format!("Parser Error: {}", e); + self.output_log = format!("Error: {}", e); } } }