diff --git a/src/ast/compiler/binder.rs b/src/ast/compiler/binder.rs index 815a2ec..1e317a7 100644 --- a/src/ast/compiler/binder.rs +++ b/src/ast/compiler/binder.rs @@ -1,9 +1,9 @@ -use std::collections::HashMap; +use std::collections::{HashMap, BTreeMap}; use std::rc::Rc; use std::cell::RefCell; use crate::ast::nodes::{Node, UntypedKind}; use crate::ast::compiler::bound_nodes::{BoundKind, Address}; -use crate::ast::types::{Identity, StaticType}; +use crate::ast::types::{Identity, StaticType, Value}; #[derive(Debug, Clone)] struct LocalInfo { @@ -229,6 +229,44 @@ impl Binder { } Ok(self.make_node(node.identity.clone(), BoundKind::Block { exprs: bound_exprs }, last_ty)) }, + + UntypedKind::Tuple { elements } => { + let mut bound_elems = Vec::new(); + for e in elements { + bound_elems.push(self.bind(e)?); + } + // For now, tuple type is List(Any) + let ty = StaticType::List(Box::new(StaticType::Any)); + Ok(self.make_node(node.identity.clone(), BoundKind::Tuple { elements: bound_elems }, ty)) + }, + + UntypedKind::Map { entries } => { + let mut bound_entries = Vec::new(); + let mut key_types = BTreeMap::new(); // For Record type inference if keys are constant keywords + + for (k, v) in entries { + // Keys must be compile-time constants for now (for Record type), + // or at least we enforce keywords. + // But `bind` processes runtime expressions too. + // Let's bind both. + let bound_k = self.bind(k)?; + let bound_v = self.bind(v)?; + + // If key is a constant keyword, we can build a static record type. + if let BoundKind::Constant(Value::Keyword(kw)) = &bound_k.kind { + key_types.insert(*kw, bound_v.ty.clone()); + } + + bound_entries.push((bound_k, bound_v)); + } + + // If all keys are known, we produce a Record type. Else Map(Any, Any) which we don't have yet. + // We default to Record(Any) if keys are dynamic (not supported well yet) or known. + // Since our parser enforced keywords, we assume we have a Record. + let ty = StaticType::Record(Rc::new(key_types)); + + Ok(self.make_node(node.identity.clone(), BoundKind::Map { entries: bound_entries }, ty)) + }, UntypedKind::Extension(_) => { Err("Custom extensions not supported in Binder yet".to_string()) diff --git a/src/ast/compiler/bound_nodes.rs b/src/ast/compiler/bound_nodes.rs index ace26fb..f06014f 100644 --- a/src/ast/compiler/bound_nodes.rs +++ b/src/ast/compiler/bound_nodes.rs @@ -51,6 +51,14 @@ pub enum BoundKind { exprs: Vec>, }, + // NEW + Tuple { + elements: Vec>, + }, + Map { + entries: Vec<(Node, Node)>, + }, + // 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/nodes.rs b/src/ast/nodes.rs index bfac449..82e0628 100644 --- a/src/ast/nodes.rs +++ b/src/ast/nodes.rs @@ -1,6 +1,4 @@ use std::rc::Rc; -use std::cell::RefCell; -use std::collections::HashMap; use std::fmt::Debug; use crate::ast::types::{Identity, Value}; @@ -14,75 +12,9 @@ pub struct Node { /// The base for custom node types (extensions) pub trait CustomNode: Debug { - fn eval(&self, _ctx: &mut Context) -> Result { - Err("CustomNode eval not implemented".to_string()) - } fn display_name(&self) -> &'static str; } -/// Dynamic Scope for the simple interpreter (Legacy / Macro expansion) -#[derive(Debug, Clone)] -pub struct Scope { - values: HashMap, - parent: Option>>, -} - -impl Scope { - pub fn new(parent: Option>>) -> Self { - Self { - values: HashMap::new(), - parent, - } - } - - pub fn define(&mut self, name: &str, value: Value) { - self.values.insert(name.to_string(), value); - } - - pub fn assign(&mut self, name: &str, value: Value) -> Result<(), String> { - if self.values.contains_key(name) { - self.values.insert(name.to_string(), value); - return Ok(()); - } - if let Some(parent) = &self.parent { - return parent.borrow_mut().assign(name, value); - } - Err(format!("Cannot assign to undefined variable '{}'", name)) - } - - pub fn resolve(&self, name: &str) -> Option { - if let Some(val) = self.values.get(name) { - return Some(val.clone()); - } - if let Some(parent) = &self.parent { - return parent.borrow().resolve(name); - } - None - } -} - -/// Evaluation Context (Scope management) -pub struct Context { - pub scope: Rc>, -} - -impl Default for Context { - fn default() -> Self { - Self::new() - } -} - -impl Context { - pub fn new() -> Self { - let root_scope = Scope::new(None); - // Registering stdlib directly in Scope for simple eval is skipped to keep it clean. - // If needed, we can re-add it, but VM is the primary execution engine. - Self { - scope: Rc::new(RefCell::new(root_scope)), - } - } -} - #[derive(Debug)] pub enum UntypedKind { Nop, @@ -112,92 +44,11 @@ pub enum UntypedKind { Block { exprs: Vec>, }, + Tuple { + elements: Vec>, + }, + Map { + entries: Vec<(Node, Node)>, + }, Extension(Box), } - -impl Node { - // This eval is a simple tree-walker, distinct from VM execution. - // It's useful for macros or constant folding. - pub fn eval(&self, ctx: &mut Context) -> Result { - match &self.kind { - UntypedKind::Nop => Ok(Value::Void), - UntypedKind::Constant(v) => Ok(v.clone()), - - UntypedKind::Identifier(name) => { - let scope = ctx.scope.borrow(); - scope.resolve(name) - .ok_or_else(|| format!("Undefined variable: '{}'", name)) - }, - - UntypedKind::If { cond, then_br, else_br } => { - let cond_val = cond.eval(ctx)?; - if cond_val.is_truthy() { - then_br.eval(ctx) - } else if let Some(eb) = else_br { - eb.eval(ctx) - } else { - Ok(Value::Void) - } - }, - - UntypedKind::Def { name, value } => { - let val = value.eval(ctx)?; - let mut scope = ctx.scope.borrow_mut(); - scope.define(name, val.clone()); - Ok(val) - }, - - UntypedKind::Assign { target, value } => { - let val = value.eval(ctx)?; - if let UntypedKind::Identifier(name) = &target.kind { - let mut scope = ctx.scope.borrow_mut(); - scope.assign(name, val.clone())?; - Ok(val) - } else { - Err("Assignment target must be an identifier".to_string()) - } - }, - - UntypedKind::Block { exprs } => { - let mut last_val = Value::Void; - for expr in exprs { - last_val = expr.eval(ctx)?; - } - Ok(last_val) - }, - - UntypedKind::Lambda { params, body } => { - let captured_scope = ctx.scope.clone(); - let params = params.clone(); - let body = body.clone(); - - Ok(Value::Function(Rc::new(move |args| { - let mut new_scope = Scope::new(Some(captured_scope.clone())); - for (i, param) in params.iter().enumerate() { - if i < args.len() { - new_scope.define(param, args[i].clone()); - } else { - new_scope.define(param, Value::Void); - } - } - let mut sub_ctx = Context { scope: Rc::new(RefCell::new(new_scope)) }; - body.eval(&mut sub_ctx).unwrap_or(Value::Void) - }))) - }, - - UntypedKind::Call { callee, args } => { - let func_val = callee.eval(ctx)?; - let mut eval_args = Vec::with_capacity(args.len()); - for arg in args { - eval_args.push(arg.eval(ctx)?); - } - match func_val { - Value::Function(f) => Ok(f(eval_args)), - _ => Err(format!("Attempt to call a non-function value: {}", func_val)), - } - }, - - UntypedKind::Extension(ext) => ext.eval(ctx), - } - } -} diff --git a/src/ast/parser.rs b/src/ast/parser.rs index 7dda57a..7338a0e 100644 --- a/src/ast/parser.rs +++ b/src/ast/parser.rs @@ -1,7 +1,7 @@ use std::rc::Rc; use crate::ast::lexer::{Lexer, Token, TokenKind}; use crate::ast::types::{Identity, NodeIdentity, Value, Keyword}; -use crate::ast::nodes::{Node, UntypedKind, Context}; +use crate::ast::nodes::{Node, UntypedKind}; pub struct Parser<'a> { lexer: Lexer<'a>, @@ -202,29 +202,24 @@ impl<'a> Parser<'a> { fn parse_vector_literal(&mut self) -> Result, String> { let token = self.advance()?; let mut elements = Vec::new(); - let mut temp_ctx = Context::new(); while *self.peek() != TokenKind::RightBracket && *self.peek() != TokenKind::EOF { let expr = self.parse_expression()?; - match expr.eval(&mut temp_ctx) { - Ok(val) => elements.push(val), - Err(e) => return Err(format!("Vector literal error: {}", e)), - } + elements.push(expr); } self.expect(TokenKind::RightBracket)?; Ok(Node { identity: Rc::new(NodeIdentity { location: token.location }), - kind: UntypedKind::Constant(Value::List(Rc::new(elements))), + kind: UntypedKind::Tuple { elements }, ty: (), }) } fn parse_map_literal(&mut self) -> Result, String> { let token = self.advance()?; - let mut map = std::collections::HashMap::new(); - let mut temp_ctx = Context::new(); + let mut entries = Vec::new(); while *self.peek() != TokenKind::RightBrace { if *self.peek() == TokenKind::EOF { @@ -232,24 +227,26 @@ impl<'a> Parser<'a> { } let key_node = self.parse_expression()?; - let key = match key_node.eval(&mut temp_ctx)? { - Value::Keyword(k) => k, - _ => return Err("Map keys must be keywords".to_string()), - }; + // We check for keyword kind here (syntactically) to avoid ambiguity, but + // strictly we could allow any expression and check at runtime. + // Delphi enforces keywords. We can do minimal check here. + match &key_node.kind { + UntypedKind::Constant(Value::Keyword(_)) => {}, + _ => return Err("Map keys must be keywords (syntactically)".to_string()), + } if *self.peek() == TokenKind::RightBrace { return Err("Map literal must have even number of forms".to_string()); } let val_node = self.parse_expression()?; - let val = val_node.eval(&mut temp_ctx)?; - - map.insert(key, val); + + entries.push((key_node, val_node)); } self.expect(TokenKind::RightBrace)?; Ok(Node { identity: Rc::new(NodeIdentity { location: token.location }), - kind: UntypedKind::Constant(Value::Record(Rc::new(map))), + kind: UntypedKind::Map { entries }, ty: (), }) } diff --git a/src/ast/vm.rs b/src/ast/vm.rs index 1633daf..2c446e4 100644 --- a/src/ast/vm.rs +++ b/src/ast/vm.rs @@ -1,5 +1,6 @@ use std::rc::Rc; use std::cell::RefCell; +use std::collections::HashMap; use std::any::Any; use crate::ast::compiler::bound_nodes::{Address, BoundKind}; use crate::ast::nodes::Node; @@ -28,9 +29,6 @@ struct CallFrame { pub struct VM { stack: Vec, - // Globals are mutable shared state within the VM thread. - // However, if we move to frozen roots, this might change to a read-only structure. - // For now, mutable RefCell Vec is fine for single threaded execution. globals: Rc>>, frames: Vec, } @@ -151,6 +149,29 @@ impl VM { }, _ => Err(format!("Attempt to call non-function: {}", func_val)), } + }, + + BoundKind::Tuple { elements } => { + let mut vals = Vec::with_capacity(elements.len()); + for e in elements { + vals.push(self.eval(e)?); + } + Ok(Value::List(Rc::new(vals))) + }, + + BoundKind::Map { entries } => { + let mut map = HashMap::new(); + for (k, v) in entries { + let key = self.eval(k)?; + let val = self.eval(v)?; + + if let Value::Keyword(kw) = key { + map.insert(kw, val); + } else { + return Err(format!("Map key must be keyword, got {}", key)); + } + } + Ok(Value::Record(Rc::new(map))) } } }