Skip to content

Commit abac6fb

Browse files
Claude/add academic proofs mg s2z (#28)
Signed-off-by: Jonathan D.A. Jewell <6759885+hyperpolymath@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com>
1 parent c207941 commit abac6fb

4 files changed

Lines changed: 307 additions & 3 deletions

File tree

src/ast/mod.rs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -236,8 +236,10 @@ pub enum Expr {
236236
Binary(BinaryOp, Box<Spanned<Expr>>, Box<Spanned<Expr>>),
237237
/// Unary operation
238238
Unary(UnaryOp, Box<Spanned<Expr>>),
239-
/// Function call
239+
/// Function call by name
240240
Call(String, Vec<Spanned<Expr>>),
241+
/// Call expression: `expr(args)` - for calling closures
242+
CallExpr(Box<Spanned<Expr>>, Vec<Spanned<Expr>>),
241243
/// Unit measurement: `expr measured in unit`
242244
UnitMeasurement(Box<Spanned<Expr>>, String),
243245
/// Gratitude literal: `thanks("name")`
@@ -252,6 +254,8 @@ pub enum Expr {
252254
Oops(Box<Spanned<Expr>>),
253255
/// Unwrap result: `expr?` or `unwrap(expr)`
254256
Unwrap(Box<Spanned<Expr>>),
257+
/// Lambda/closure: `|x, y| -> expr` or `|x, y| { ... }`
258+
Lambda(LambdaExpr),
255259
}
256260

257261
/// Binary operators
@@ -288,6 +292,23 @@ pub enum Literal {
288292
Bool(bool),
289293
}
290294

295+
/// Lambda expression body
296+
#[derive(Debug, Clone)]
297+
pub enum LambdaBody {
298+
/// Expression body: `|x| -> x + 1`
299+
Expr(Box<Spanned<Expr>>),
300+
/// Block body: `|x| { give back x + 1; }`
301+
Block(Vec<Statement>),
302+
}
303+
304+
/// Lambda/closure expression: `|x, y| -> expr` or `|x, y| { ... }`
305+
#[derive(Debug, Clone)]
306+
pub struct LambdaExpr {
307+
pub params: Vec<Parameter>,
308+
pub return_type: Option<Type>,
309+
pub body: LambdaBody,
310+
}
311+
291312
/// Emote tag: `@name(params)`
292313
#[derive(Debug, Clone)]
293314
pub struct EmoteTag {
@@ -351,7 +372,7 @@ pub enum PragmaDirective {
351372
}
352373

353374
/// Type annotation
354-
#[derive(Debug, Clone)]
375+
#[derive(Debug, Clone, PartialEq)]
355376
pub enum Type {
356377
/// Basic types: String, Int, Float, Bool, or custom
357378
Basic(String),
@@ -361,6 +382,8 @@ pub enum Type {
361382
Optional(Box<Type>),
362383
/// Reference type: &T
363384
Reference(Box<Type>),
385+
/// Function type: (T1, T2) -> R
386+
Function(Vec<Type>, Box<Type>),
364387
}
365388

366389
/// Type definition: `type Name = ...;`

src/interpreter/mod.rs

Lines changed: 159 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
mod value;
22

3-
pub use value::Value;
3+
pub use value::{CapturedEnv, Closure, Value};
44

55
use crate::ast::*;
6+
use std::cell::RefCell;
67
use std::collections::HashMap;
78
use std::io::{self, Write};
9+
use std::rc::Rc;
810
use thiserror::Error;
911

1012
#[derive(Error, Debug)]
@@ -474,7 +476,89 @@ impl Interpreter {
474476
other => Ok(other), // Non-result values pass through
475477
}
476478
}
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);
477538
}
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
478562
}
479563

480564
fn apply_index(&self, target: Value, index: Value) -> Result<Value> {
@@ -605,6 +689,14 @@ impl Interpreter {
605689
}
606690

607691
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
608700
let func = self
609701
.functions
610702
.get(name)
@@ -904,4 +996,70 @@ mod tests {
904996
"#;
905997
assert!(run_program(source).is_ok());
906998
}
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+
}
9071065
}

src/interpreter/value.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,47 @@
1+
use crate::ast::{LambdaBody, Parameter};
2+
use std::collections::HashMap;
13
use std::fmt;
4+
use std::rc::Rc;
5+
use std::cell::RefCell;
6+
7+
/// Captured environment for closures
8+
#[derive(Debug, Clone)]
9+
pub struct CapturedEnv {
10+
pub bindings: HashMap<String, Value>,
11+
}
12+
13+
impl CapturedEnv {
14+
pub fn new() -> Self {
15+
Self {
16+
bindings: HashMap::new(),
17+
}
18+
}
19+
20+
pub fn from_map(bindings: HashMap<String, Value>) -> Self {
21+
Self { bindings }
22+
}
23+
}
24+
25+
impl Default for CapturedEnv {
26+
fn default() -> Self {
27+
Self::new()
28+
}
29+
}
30+
31+
/// A closure captures its environment at creation time
32+
#[derive(Debug, Clone)]
33+
pub struct Closure {
34+
pub params: Vec<Parameter>,
35+
pub body: LambdaBody,
36+
pub env: Rc<RefCell<CapturedEnv>>,
37+
}
38+
39+
impl PartialEq for Closure {
40+
fn eq(&self, _other: &Self) -> bool {
41+
// Closures are never equal (like function identity)
42+
false
43+
}
44+
}
245

346
/// Runtime value in WokeLang
447
#[derive(Debug, Clone, PartialEq)]
@@ -13,6 +56,8 @@ pub enum Value {
1356
Okay(Box<Value>),
1457
/// Result error: `Oops(message)`
1558
Oops(String),
59+
/// First-class function/closure
60+
Function(Closure),
1661
}
1762

1863
impl Value {
@@ -27,6 +72,7 @@ impl Value {
2772
Value::Unit => false,
2873
Value::Okay(_) => true,
2974
Value::Oops(_) => false,
75+
Value::Function(_) => true,
3076
}
3177
}
3278

@@ -70,6 +116,10 @@ impl fmt::Display for Value {
70116
Value::Unit => write!(f, "()"),
71117
Value::Okay(v) => write!(f, "Okay({})", v),
72118
Value::Oops(e) => write!(f, "Oops(\"{}\")", e),
119+
Value::Function(closure) => {
120+
let param_names: Vec<_> = closure.params.iter().map(|p| p.name.as_str()).collect();
121+
write!(f, "|{}| -> <closure>", param_names.join(", "))
122+
}
73123
}
74124
}
75125
}

0 commit comments

Comments
 (0)