Add lambda expression support
Introduces `UntypedKind::Lambda` to represent function closures. Adds `parse_fn` to the parser to handle lambda syntax. Updates `Node::eval` to correctly evaluate lambda expressions, creating new scopes for each invocation with captured variables. Improves handling of call expressions by passing the AST node's identity. Removes unnecessary comments and simplifies some error handling.
This commit is contained in:
+35
-19
@@ -16,7 +16,7 @@ pub trait CustomNode: Debug + Send + Sync {
|
|||||||
fn display_name(&self) -> &'static str;
|
fn display_name(&self) -> &'static str;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Dynamic Scope for the interpreter (Temporary solution)
|
/// Dynamic Scope for the interpreter
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Scope {
|
pub struct Scope {
|
||||||
values: HashMap<String, Value>,
|
values: HashMap<String, Value>,
|
||||||
@@ -54,10 +54,7 @@ pub struct Context {
|
|||||||
impl Context {
|
impl Context {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let mut root_scope = Scope::new(None);
|
let mut root_scope = Scope::new(None);
|
||||||
|
|
||||||
// Register standard library (built-ins)
|
|
||||||
register_stdlib(&mut root_scope);
|
register_stdlib(&mut root_scope);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
scope: Arc::new(Mutex::new(root_scope)),
|
scope: Arc::new(Mutex::new(root_scope)),
|
||||||
}
|
}
|
||||||
@@ -65,19 +62,15 @@ impl Context {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn register_stdlib(scope: &mut Scope) {
|
fn register_stdlib(scope: &mut Scope) {
|
||||||
// Helper macro to reduce boilerplate
|
|
||||||
macro_rules! bin_op {
|
macro_rules! bin_op {
|
||||||
($name:expr, $op:tt) => {
|
($name:expr, $op:tt) => {
|
||||||
scope.define($name, Value::Function(Arc::new(|args| {
|
scope.define($name, Value::Function(Arc::new(|args| {
|
||||||
if args.len() < 2 { return Value::Void; } // Error handling later
|
if args.len() < 2 { return Value::Void; }
|
||||||
|
|
||||||
// Fold allow multi-arg: (+ 1 2 3) -> 6
|
|
||||||
let mut acc = match &args[0] {
|
let mut acc = match &args[0] {
|
||||||
Value::Int(i) => *i as f64,
|
Value::Int(i) => *i as f64,
|
||||||
Value::Float(f) => *f,
|
Value::Float(f) => *f,
|
||||||
_ => return Value::Void,
|
_ => return Value::Void,
|
||||||
};
|
};
|
||||||
|
|
||||||
for arg in &args[1..] {
|
for arg in &args[1..] {
|
||||||
let val = match arg {
|
let val = match arg {
|
||||||
Value::Int(i) => *i as f64,
|
Value::Int(i) => *i as f64,
|
||||||
@@ -86,8 +79,6 @@ fn register_stdlib(scope: &mut Scope) {
|
|||||||
};
|
};
|
||||||
acc = acc $op val;
|
acc = acc $op val;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return Int if result is integer-like (simplified)
|
|
||||||
if acc.fract() == 0.0 {
|
if acc.fract() == 0.0 {
|
||||||
Value::Int(acc as i64)
|
Value::Int(acc as i64)
|
||||||
} else {
|
} else {
|
||||||
@@ -102,11 +93,9 @@ fn register_stdlib(scope: &mut Scope) {
|
|||||||
bin_op!("*", *);
|
bin_op!("*", *);
|
||||||
bin_op!("/", /);
|
bin_op!("/", /);
|
||||||
|
|
||||||
// Logic
|
|
||||||
scope.define(">", Value::Function(Arc::new(|args| {
|
scope.define(">", Value::Function(Arc::new(|args| {
|
||||||
if args.len() != 2 { return Value::Void; }
|
if args.len() != 2 { return Value::Void; }
|
||||||
let (v1, v2) = (&args[0], &args[1]);
|
match (&args[0], &args[1]) {
|
||||||
match (v1, v2) {
|
|
||||||
(Value::Int(a), Value::Int(b)) => Value::Bool(a > b),
|
(Value::Int(a), Value::Int(b)) => Value::Bool(a > b),
|
||||||
(Value::Float(a), Value::Float(b)) => Value::Bool(a > b),
|
(Value::Float(a), Value::Float(b)) => Value::Bool(a > b),
|
||||||
(Value::Int(a), Value::Float(b)) => Value::Bool((*a as f64) > *b),
|
(Value::Int(a), Value::Float(b)) => Value::Bool((*a as f64) > *b),
|
||||||
@@ -116,11 +105,8 @@ fn register_stdlib(scope: &mut Scope) {
|
|||||||
})));
|
})));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// The core AST variant enum for performance and structure
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum UntypedKind {
|
pub enum UntypedKind {
|
||||||
/// The "..." placeholder for incomplete code
|
|
||||||
Nop,
|
Nop,
|
||||||
Constant(Value),
|
Constant(Value),
|
||||||
Identifier(Arc<str>),
|
Identifier(Arc<str>),
|
||||||
@@ -129,11 +115,15 @@ pub enum UntypedKind {
|
|||||||
then_br: Box<Node<UntypedKind>>,
|
then_br: Box<Node<UntypedKind>>,
|
||||||
else_br: Option<Box<Node<UntypedKind>>>,
|
else_br: Option<Box<Node<UntypedKind>>>,
|
||||||
},
|
},
|
||||||
|
// CLOSURE SUPPORT
|
||||||
|
Lambda {
|
||||||
|
params: Vec<Arc<str>>,
|
||||||
|
body: Arc<Node<UntypedKind>>, // Arc for cheap cloning into closure
|
||||||
|
},
|
||||||
Call {
|
Call {
|
||||||
callee: Box<Node<UntypedKind>>,
|
callee: Box<Node<UntypedKind>>,
|
||||||
args: Vec<Node<UntypedKind>>,
|
args: Vec<Node<UntypedKind>>,
|
||||||
},
|
},
|
||||||
/// The "escape hatch" for decentralized node types
|
|
||||||
Extension(Box<dyn CustomNode>),
|
Extension(Box<dyn CustomNode>),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,10 +150,36 @@ impl Node<UntypedKind> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- CLOSURE IMPLEMENTATION ---
|
||||||
|
UntypedKind::Lambda { params, body } => {
|
||||||
|
let captured_scope = ctx.scope.clone();
|
||||||
|
let params = params.clone();
|
||||||
|
let body = body.clone();
|
||||||
|
|
||||||
|
Ok(Value::Function(Arc::new(move |args| {
|
||||||
|
// 1. New Scope with captured parent
|
||||||
|
let mut new_scope = Scope::new(Some(captured_scope.clone()));
|
||||||
|
|
||||||
|
// 2. Bind Args
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Eval Body
|
||||||
|
// Note: We swallow errors here because Value::Function signature is simple.
|
||||||
|
// In a full VM, we'd return Result<Value, Error>.
|
||||||
|
let mut sub_ctx = Context { scope: Arc::new(Mutex::new(new_scope)) };
|
||||||
|
body.eval(&mut sub_ctx).unwrap_or(Value::Void)
|
||||||
|
})))
|
||||||
|
},
|
||||||
|
|
||||||
UntypedKind::Call { callee, args } => {
|
UntypedKind::Call { callee, args } => {
|
||||||
let func_val = callee.eval(ctx)?;
|
let func_val = callee.eval(ctx)?;
|
||||||
|
|
||||||
// Evaluate all arguments first (strict evaluation)
|
|
||||||
let mut eval_args = Vec::with_capacity(args.len());
|
let mut eval_args = Vec::with_capacity(args.len());
|
||||||
for arg in args {
|
for arg in args {
|
||||||
eval_args.push(arg.eval(ctx)?);
|
eval_args.push(arg.eval(ctx)?);
|
||||||
|
|||||||
+59
-34
@@ -1,7 +1,7 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use crate::ast::lexer::{Lexer, Token, TokenKind};
|
use crate::ast::lexer::{Lexer, Token, TokenKind};
|
||||||
use crate::ast::types::{Identity, NodeIdentity, Value, Keyword};
|
use crate::ast::types::{Identity, NodeIdentity, Value, Keyword};
|
||||||
use crate::ast::nodes::{Node, UntypedKind};
|
use crate::ast::nodes::{Node, UntypedKind, Context};
|
||||||
|
|
||||||
pub struct Parser<'a> {
|
pub struct Parser<'a> {
|
||||||
lexer: Lexer<'a>,
|
lexer: Lexer<'a>,
|
||||||
@@ -27,13 +27,17 @@ impl<'a> Parser<'a> {
|
|||||||
pub fn parse_expression(&mut self) -> Result<Node<UntypedKind>, String> {
|
pub fn parse_expression(&mut self) -> Result<Node<UntypedKind>, String> {
|
||||||
match self.peek() {
|
match self.peek() {
|
||||||
TokenKind::LeftParen => self.parse_list(),
|
TokenKind::LeftParen => self.parse_list(),
|
||||||
TokenKind::LeftBracket => self.parse_vector(),
|
TokenKind::LeftBracket => self.parse_vector_literal(),
|
||||||
// TokenKind::LeftBrace => self.parse_map(), // TODO
|
|
||||||
TokenKind::Quote => {
|
TokenKind::Quote => {
|
||||||
let identity = Arc::new(NodeIdentity { location: self.current_token.location });
|
let identity = Arc::new(NodeIdentity { location: self.current_token.location });
|
||||||
self.parse_reader_macro(UntypedKind::Call {
|
self.advance()?; // consume '
|
||||||
callee: Box::new(self.make_id_node("quote", identity)),
|
let expr = self.parse_expression()?;
|
||||||
args: vec![], // will be filled
|
Ok(Node {
|
||||||
|
identity: identity.clone(),
|
||||||
|
kind: UntypedKind::Call {
|
||||||
|
callee: Box::new(self.make_id_node("quote", identity)),
|
||||||
|
args: vec![expr],
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
_ => self.parse_atom(),
|
_ => self.parse_atom(),
|
||||||
@@ -65,21 +69,24 @@ impl<'a> Parser<'a> {
|
|||||||
|
|
||||||
fn parse_list(&mut self) -> Result<Node<UntypedKind>, String> {
|
fn parse_list(&mut self) -> Result<Node<UntypedKind>, String> {
|
||||||
let start_loc = self.advance()?.location; // consume '('
|
let start_loc = self.advance()?.location; // consume '('
|
||||||
|
let identity = Arc::new(NodeIdentity { location: start_loc });
|
||||||
|
|
||||||
if *self.peek() == TokenKind::RightParen {
|
if *self.peek() == TokenKind::RightParen {
|
||||||
return Err(format!("Empty list () is not a valid expression at {:?}", start_loc));
|
return Err(format!("Empty list () is not a valid expression at {:?}", start_loc));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Peek at head to check for special forms
|
// Peek at head to check for special forms
|
||||||
|
// We parse the first expression to check if it's an identifier "if", "fn", etc.
|
||||||
let head = self.parse_expression()?;
|
let head = self.parse_expression()?;
|
||||||
|
|
||||||
let result = if let UntypedKind::Identifier(name) = &head.kind {
|
let result = if let UntypedKind::Identifier(name) = &head.kind {
|
||||||
match name.as_ref() {
|
match name.as_ref() {
|
||||||
"if" => self.parse_if(head.identity.clone()),
|
"if" => self.parse_if(identity),
|
||||||
_ => self.parse_call(head),
|
"fn" => self.parse_fn(identity),
|
||||||
|
_ => self.parse_call(head, identity), // Pass head + identity
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.parse_call(head)
|
self.parse_call(head, identity)
|
||||||
};
|
};
|
||||||
|
|
||||||
self.expect(TokenKind::RightParen)?;
|
self.expect(TokenKind::RightParen)?;
|
||||||
@@ -87,6 +94,7 @@ impl<'a> Parser<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn parse_if(&mut self, identity: Identity) -> Result<Node<UntypedKind>, String> {
|
fn parse_if(&mut self, identity: Identity) -> Result<Node<UntypedKind>, String> {
|
||||||
|
// 'if' was already consumed as head
|
||||||
let cond = Box::new(self.parse_expression()?);
|
let cond = Box::new(self.parse_expression()?);
|
||||||
let then_br = Box::new(self.parse_expression()?);
|
let then_br = Box::new(self.parse_expression()?);
|
||||||
let mut else_br = None;
|
let mut else_br = None;
|
||||||
@@ -101,10 +109,41 @@ impl<'a> Parser<'a> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_call(&mut self, callee: Node<UntypedKind>) -> Result<Node<UntypedKind>, String> {
|
fn parse_fn(&mut self, identity: Identity) -> Result<Node<UntypedKind>, String> {
|
||||||
let mut args = Vec::new();
|
// 'fn' was consumed. Next must be [params] vector.
|
||||||
let identity = callee.identity.clone();
|
let params = self.parse_param_vector()?;
|
||||||
|
let body = self.parse_expression()?;
|
||||||
|
|
||||||
|
Ok(Node {
|
||||||
|
identity,
|
||||||
|
kind: UntypedKind::Lambda {
|
||||||
|
params,
|
||||||
|
body: Arc::new(body),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_param_vector(&mut self) -> Result<Vec<Arc<str>>, String> {
|
||||||
|
if *self.peek() != TokenKind::LeftBracket {
|
||||||
|
return Err(format!("Expected parameter vector [...] for fn, found {:?}", self.peek()));
|
||||||
|
}
|
||||||
|
self.advance()?; // consume '['
|
||||||
|
|
||||||
|
let mut params = Vec::new();
|
||||||
|
while *self.peek() != TokenKind::RightBracket {
|
||||||
|
let token = self.advance()?;
|
||||||
|
match token.kind {
|
||||||
|
TokenKind::Identifier(name) => params.push(name),
|
||||||
|
_ => return Err(format!("Expected identifier in param vector, found {:?}", token.kind)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.expect(TokenKind::RightBracket)?;
|
||||||
|
Ok(params)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_call(&mut self, callee: Node<UntypedKind>, identity: Identity) -> Result<Node<UntypedKind>, String> {
|
||||||
|
let mut args = Vec::new();
|
||||||
|
|
||||||
while *self.peek() != TokenKind::RightParen && *self.peek() != TokenKind::EOF {
|
while *self.peek() != TokenKind::RightParen && *self.peek() != TokenKind::EOF {
|
||||||
args.push(self.parse_expression()?);
|
args.push(self.parse_expression()?);
|
||||||
}
|
}
|
||||||
@@ -115,17 +154,20 @@ impl<'a> Parser<'a> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_vector(&mut self) -> Result<Node<UntypedKind>, String> {
|
fn parse_vector_literal(&mut self) -> Result<Node<UntypedKind>, String> {
|
||||||
let token = self.advance()?; // consume '['
|
let token = self.advance()?; // consume '['
|
||||||
let mut elements = Vec::new();
|
let mut elements = Vec::new();
|
||||||
|
|
||||||
|
// Temporary evaluation context for literals
|
||||||
|
let mut temp_ctx = Context::new();
|
||||||
|
|
||||||
while *self.peek() != TokenKind::RightBracket && *self.peek() != TokenKind::EOF {
|
while *self.peek() != TokenKind::RightBracket && *self.peek() != TokenKind::EOF {
|
||||||
// Vector elements in Myc are usually just constants in a list
|
|
||||||
let expr = self.parse_expression()?;
|
let expr = self.parse_expression()?;
|
||||||
// Simple direct eval for literals (Context::new creates fresh scope)
|
// Simple direct eval for literals inside vectors (e.g. [1 2 3])
|
||||||
match expr.eval(&mut crate::ast::nodes::Context::new()) {
|
// In a full compiler, we would return a VectorNode here instead of evaluating immediately.
|
||||||
|
match expr.eval(&mut temp_ctx) {
|
||||||
Ok(val) => elements.push(val),
|
Ok(val) => elements.push(val),
|
||||||
Err(e) => return Err(format!("Error evaluating vector element: {}", e)),
|
Err(e) => return Err(format!("Vector literal error: {}", e)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,23 +179,6 @@ impl<'a> Parser<'a> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_reader_macro(&mut self, _template: UntypedKind) -> Result<Node<UntypedKind>, String> {
|
|
||||||
let token = self.advance()?; // consume '
|
|
||||||
let expr = self.parse_expression()?;
|
|
||||||
|
|
||||||
// 'x -> (quote x)
|
|
||||||
let identity = Arc::new(NodeIdentity { location: token.location });
|
|
||||||
let quote_id = self.make_id_node("quote", identity.clone());
|
|
||||||
|
|
||||||
Ok(Node {
|
|
||||||
identity,
|
|
||||||
kind: UntypedKind::Call {
|
|
||||||
callee: Box::new(quote_id),
|
|
||||||
args: vec![expr],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn expect(&mut self, kind: TokenKind) -> Result<(), String> {
|
fn expect(&mut self, kind: TokenKind) -> Result<(), String> {
|
||||||
let token = self.advance()?;
|
let token = self.advance()?;
|
||||||
if token.kind == kind {
|
if token.kind == kind {
|
||||||
|
|||||||
Reference in New Issue
Block a user