|
1 | 1 | mod value; |
2 | 2 |
|
3 | | -pub use value::Value; |
| 3 | +pub use value::{CapturedEnv, Closure, Value}; |
4 | 4 |
|
5 | 5 | use crate::ast::*; |
| 6 | +use std::cell::RefCell; |
6 | 7 | use std::collections::HashMap; |
7 | 8 | use std::io::{self, Write}; |
| 9 | +use std::rc::Rc; |
8 | 10 | use thiserror::Error; |
9 | 11 |
|
10 | 12 | #[derive(Error, Debug)] |
@@ -474,7 +476,89 @@ impl Interpreter { |
474 | 476 | other => Ok(other), // Non-result values pass through |
475 | 477 | } |
476 | 478 | } |
| 479 | + Expr::Lambda(lambda) => { |
| 480 | + // Capture the current environment |
| 481 | + let captured = self.capture_environment(); |
| 482 | + Ok(Value::Function(Closure { |
| 483 | + params: lambda.params.clone(), |
| 484 | + body: lambda.body.clone(), |
| 485 | + env: Rc::new(RefCell::new(captured)), |
| 486 | + })) |
| 487 | + } |
| 488 | + Expr::CallExpr(callee, args) => { |
| 489 | + let callee_val = self.evaluate(callee)?; |
| 490 | + let arg_values: Vec<Value> = args |
| 491 | + .iter() |
| 492 | + .map(|a| self.evaluate(a)) |
| 493 | + .collect::<Result<_>>()?; |
| 494 | + |
| 495 | + match callee_val { |
| 496 | + Value::Function(closure) => self.call_closure(&closure, arg_values), |
| 497 | + _ => Err(RuntimeError::TypeError("Cannot call non-function value".into())), |
| 498 | + } |
| 499 | + } |
| 500 | + } |
| 501 | + } |
| 502 | + |
| 503 | + fn capture_environment(&self) -> CapturedEnv { |
| 504 | + // Flatten all scopes into a single map for the closure |
| 505 | + let mut bindings = HashMap::new(); |
| 506 | + for scope in &self.env.scopes { |
| 507 | + for (name, value) in scope { |
| 508 | + bindings.insert(name.clone(), value.clone()); |
| 509 | + } |
| 510 | + } |
| 511 | + CapturedEnv::from_map(bindings) |
| 512 | + } |
| 513 | + |
| 514 | + fn call_closure(&mut self, closure: &Closure, args: Vec<Value>) -> Result<Value> { |
| 515 | + if closure.params.len() != args.len() { |
| 516 | + return Err(RuntimeError::ArityMismatch { |
| 517 | + expected: closure.params.len(), |
| 518 | + got: args.len(), |
| 519 | + }); |
| 520 | + } |
| 521 | + |
| 522 | + // Save current environment |
| 523 | + let saved_env = self.env.clone(); |
| 524 | + |
| 525 | + // Create new environment with captured bindings |
| 526 | + self.env = Environment::new(); |
| 527 | + |
| 528 | + // Add captured bindings |
| 529 | + let captured = closure.env.borrow(); |
| 530 | + for (name, value) in &captured.bindings { |
| 531 | + self.env.define(name.clone(), value.clone()); |
| 532 | + } |
| 533 | + |
| 534 | + // Push new scope for parameters |
| 535 | + self.env.push_scope(); |
| 536 | + for (param, arg) in closure.params.iter().zip(args) { |
| 537 | + self.env.define(param.name.clone(), arg); |
477 | 538 | } |
| 539 | + |
| 540 | + // Execute the closure body |
| 541 | + let result = match &closure.body { |
| 542 | + LambdaBody::Expr(expr) => self.evaluate(expr), |
| 543 | + LambdaBody::Block(stmts) => { |
| 544 | + let mut result = Value::Unit; |
| 545 | + for stmt in stmts { |
| 546 | + match self.execute_statement(stmt)? { |
| 547 | + ControlFlow::Return(v) => { |
| 548 | + result = v; |
| 549 | + break; |
| 550 | + } |
| 551 | + ControlFlow::Continue => {} |
| 552 | + } |
| 553 | + } |
| 554 | + Ok(result) |
| 555 | + } |
| 556 | + }; |
| 557 | + |
| 558 | + // Restore environment |
| 559 | + self.env = saved_env; |
| 560 | + |
| 561 | + result |
478 | 562 | } |
479 | 563 |
|
480 | 564 | fn apply_index(&self, target: Value, index: Value) -> Result<Value> { |
@@ -605,6 +689,14 @@ impl Interpreter { |
605 | 689 | } |
606 | 690 |
|
607 | 691 | fn call_function(&mut self, name: &str, args: Vec<Value>) -> Result<Value> { |
| 692 | + // First, check if name refers to a variable holding a closure |
| 693 | + if let Some(value) = self.env.get(name).cloned() { |
| 694 | + if let Value::Function(closure) = value { |
| 695 | + return self.call_closure(&closure, args); |
| 696 | + } |
| 697 | + } |
| 698 | + |
| 699 | + // Otherwise, look up as a named function |
608 | 700 | let func = self |
609 | 701 | .functions |
610 | 702 | .get(name) |
@@ -904,4 +996,70 @@ mod tests { |
904 | 996 | "#; |
905 | 997 | assert!(run_program(source).is_ok()); |
906 | 998 | } |
| 999 | + |
| 1000 | + #[test] |
| 1001 | + fn test_lambda_expression() { |
| 1002 | + let source = r#" |
| 1003 | + to main() { |
| 1004 | + remember add = |x, y| -> x + y; |
| 1005 | + remember result = add(3, 4); |
| 1006 | + print(result); |
| 1007 | + } |
| 1008 | + "#; |
| 1009 | + assert!(run_program(source).is_ok()); |
| 1010 | + } |
| 1011 | + |
| 1012 | + #[test] |
| 1013 | + fn test_lambda_block() { |
| 1014 | + let source = r#" |
| 1015 | + to main() { |
| 1016 | + remember greet = |name| { |
| 1017 | + give back "Hello, " + name; |
| 1018 | + }; |
| 1019 | + remember msg = greet("World"); |
| 1020 | + print(msg); |
| 1021 | + } |
| 1022 | + "#; |
| 1023 | + assert!(run_program(source).is_ok()); |
| 1024 | + } |
| 1025 | + |
| 1026 | + #[test] |
| 1027 | + fn test_closure_captures() { |
| 1028 | + let source = r#" |
| 1029 | + to main() { |
| 1030 | + remember multiplier = 10; |
| 1031 | + remember times_ten = |x| -> x * multiplier; |
| 1032 | + remember result = times_ten(5); |
| 1033 | + print(result); |
| 1034 | + } |
| 1035 | + "#; |
| 1036 | + assert!(run_program(source).is_ok()); |
| 1037 | + } |
| 1038 | + |
| 1039 | + #[test] |
| 1040 | + fn test_higher_order_function() { |
| 1041 | + let source = r#" |
| 1042 | + to apply(f, x: Int) -> Int { |
| 1043 | + give back f(x); |
| 1044 | + } |
| 1045 | + to main() { |
| 1046 | + remember double = |x| -> x * 2; |
| 1047 | + remember result = apply(double, 21); |
| 1048 | + print(result); |
| 1049 | + } |
| 1050 | + "#; |
| 1051 | + assert!(run_program(source).is_ok()); |
| 1052 | + } |
| 1053 | + |
| 1054 | + #[test] |
| 1055 | + fn test_lambda_no_params() { |
| 1056 | + let source = r#" |
| 1057 | + to main() { |
| 1058 | + remember get_five = || -> 5; |
| 1059 | + remember result = get_five(); |
| 1060 | + print(result); |
| 1061 | + } |
| 1062 | + "#; |
| 1063 | + assert!(run_program(source).is_ok()); |
| 1064 | + } |
907 | 1065 | } |
0 commit comments