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:
Michael Schimmel
2026-02-17 00:43:09 +01:00
parent 647fc79d28
commit 145e12b811
2 changed files with 94 additions and 53 deletions
+59 -34
View File
@@ -1,7 +1,7 @@
use std::sync::Arc;
use crate::ast::lexer::{Lexer, Token, TokenKind};
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> {
lexer: Lexer<'a>,
@@ -27,13 +27,17 @@ impl<'a> Parser<'a> {
pub fn parse_expression(&mut self) -> Result<Node<UntypedKind>, String> {
match self.peek() {
TokenKind::LeftParen => self.parse_list(),
TokenKind::LeftBracket => self.parse_vector(),
// TokenKind::LeftBrace => self.parse_map(), // TODO
TokenKind::LeftBracket => self.parse_vector_literal(),
TokenKind::Quote => {
let identity = Arc::new(NodeIdentity { location: self.current_token.location });
self.parse_reader_macro(UntypedKind::Call {
callee: Box::new(self.make_id_node("quote", identity)),
args: vec![], // will be filled
self.advance()?; // consume '
let expr = self.parse_expression()?;
Ok(Node {
identity: identity.clone(),
kind: UntypedKind::Call {
callee: Box::new(self.make_id_node("quote", identity)),
args: vec![expr],
},
})
}
_ => self.parse_atom(),
@@ -65,21 +69,24 @@ impl<'a> Parser<'a> {
fn parse_list(&mut self) -> Result<Node<UntypedKind>, String> {
let start_loc = self.advance()?.location; // consume '('
let identity = Arc::new(NodeIdentity { location: start_loc });
if *self.peek() == TokenKind::RightParen {
return Err(format!("Empty list () is not a valid expression at {:?}", start_loc));
}
// 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 result = if let UntypedKind::Identifier(name) = &head.kind {
match name.as_ref() {
"if" => self.parse_if(head.identity.clone()),
_ => self.parse_call(head),
"if" => self.parse_if(identity),
"fn" => self.parse_fn(identity),
_ => self.parse_call(head, identity), // Pass head + identity
}
} else {
self.parse_call(head)
self.parse_call(head, identity)
};
self.expect(TokenKind::RightParen)?;
@@ -87,6 +94,7 @@ impl<'a> Parser<'a> {
}
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 then_br = Box::new(self.parse_expression()?);
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> {
let mut args = Vec::new();
let identity = callee.identity.clone();
fn parse_fn(&mut self, identity: Identity) -> Result<Node<UntypedKind>, String> {
// 'fn' was consumed. Next must be [params] vector.
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 {
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 mut elements = Vec::new();
// Temporary evaluation context for literals
let mut temp_ctx = Context::new();
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()?;
// Simple direct eval for literals (Context::new creates fresh scope)
match expr.eval(&mut crate::ast::nodes::Context::new()) {
// Simple direct eval for literals inside vectors (e.g. [1 2 3])
// 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),
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> {
let token = self.advance()?;
if token.kind == kind {