From 762ed3c47817ed96109d65e2dc46c3189868097c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20R=C3=B6ger?= Date: Sat, 12 Sep 2026 14:44:08 +0200 Subject: [PATCH] closure bump --- lispers-core/src/lisp/environment.rs | 26 ++++++++++++++- lispers-core/src/lisp/eval.rs | 14 ++++++++- lispers-core/src/lisp/expression.rs | 12 +++++++ lispers-core/src/lisp/prelude.rs | 17 ++++++++-- lispers-core/src/parser/parser.rs | 2 ++ scenes/demo-4.lisp | 47 ++++++++++++++++++++++++++++ scenes/materials.lisp | 29 +++++++++++++++++ src/bin/lisp_demo.rs | 12 +++++-- src/bin/rt_lisp_demo.rs | 27 +++++++++++----- 9 files changed, 172 insertions(+), 14 deletions(-) create mode 100644 scenes/demo-4.lisp diff --git a/lispers-core/src/lisp/environment.rs b/lispers-core/src/lisp/environment.rs index 10a4cce..4d6fd70 100644 --- a/lispers-core/src/lisp/environment.rs +++ b/lispers-core/src/lisp/environment.rs @@ -1,5 +1,5 @@ use super::{expression::Expression, prelude::mk_prelude}; -use std::{cell::RefCell, collections::HashMap, rc::Rc}; +use std::{cell::RefCell, collections::HashMap, fmt::Display, rc::Rc}; #[derive(PartialEq, Clone, Debug)] /// A Environment is a stack of `EnvironmentLayer`s. Each `EnvironmentLayer` is a mapping from @@ -136,6 +136,30 @@ impl Default for Environment<'_> { } } +impl Display for Environment<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(x) = &self.outer { + write!(f, "{}<{}", self.layer, x) + } else { + write!(f, "") + } + } +} + +impl Display for EnvironmentLayer { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{{{}}}", + self.symbols + .iter() + .map(|(k, v)| format!("{}={}", k, v)) + .collect::>() + .join(", ") + ) + } +} + #[test] fn test_environment() { let mut env = Environment::new(); diff --git a/lispers-core/src/lisp/eval.rs b/lispers-core/src/lisp/eval.rs index 2871735..7c311c3 100644 --- a/lispers-core/src/lisp/eval.rs +++ b/lispers-core/src/lisp/eval.rs @@ -97,12 +97,23 @@ fn dispatch_anonymous_function( } for (arg, symbol) in args.iter_mut().zip(argument_symbols.iter()) { - overlay.set(symbol.to_owned(), eval(env, arg.to_owned())?); + overlay.set(symbol.to_owned(), eval(&env, arg.to_owned())?); } eval(&env.overlay(overlay), body) } +/// Dispatch a closure. Evaluates `body` in `env` overlayed with the `layer` +fn dispatch_closure( + env: &Environment, + layer: EnvironmentLayer, + body: Expression, +) -> Result { + println!("Dispatching closure: {} -> {}", &layer, &body); + let env = env.overlay(layer); + eval(&env, body) +} + /// Evaluate an expression inside an environment pub fn eval(env: &Environment, expr: Expression) -> Result { match expr { @@ -114,6 +125,7 @@ pub fn eval(env: &Environment, expr: Expression) -> Result dispatch_anonymous_function(env, argument_symbols, *body, *rhs), a => Err(EvalError::NotAFunction(a)), }, + Expression::Closure { layer, body } => dispatch_closure(env, layer, *body), Expression::Quote(e) => Ok(*e), Expression::Symbol(s) => env.get(&s).ok_or(EvalError::SymbolNotBound(s)), x => Ok(x), diff --git a/lispers-core/src/lisp/expression.rs b/lispers-core/src/lisp/expression.rs index 67883cf..5d27092 100644 --- a/lispers-core/src/lisp/expression.rs +++ b/lispers-core/src/lisp/expression.rs @@ -7,6 +7,7 @@ use std::ops::DerefMut; use as_any::AsAny; use super::environment::Environment; +use super::environment::EnvironmentLayer; use super::eval::CellIterator; use super::eval::EvalError; @@ -128,6 +129,10 @@ pub enum Expression { Cell(Box, Box), /// A function expression pointing to native code. Function(fn(&Environment, Expression) -> Result), + Closure { + layer: EnvironmentLayer, + body: Box, + }, /// A anonymous function expression consisting of bound symbols and a body expression. AnonymousFunction { argument_symbols: Vec, @@ -169,7 +174,9 @@ impl PartialEq for Expression { (ForeignExpression(f1), ForeignExpression(f2)) => PartialEq::eq(f1, f2), (Quote(e1), Quote(e2)) => PartialEq::eq(e1, e2), (Symbol(s1), Symbol(s2)) => PartialEq::eq(s1, s2), + (Integer(i1), Integer(i2)) => PartialEq::eq(i1, i2), (Float(f1), Float(f2)) => PartialEq::eq(f1, f2), + (String(s1), String(s2)) => PartialEq::eq(s1, s2), (Nil, Nil) => true, (True, True) => true, _ => false, @@ -198,6 +205,8 @@ impl PartialOrd for Expression { (Quote(e1), Quote(e2)) => e1.partial_cmp(e2), (Symbol(s1), Symbol(s2)) => s1.partial_cmp(s2), (Float(f1), Float(f2)) => f1.partial_cmp(f2), + (Integer(i1), Integer(i2)) => i1.partial_cmp(i2), + (String(s1), String(s2)) => s1.partial_cmp(s2), (Nil, Nil) => Some(std::cmp::Ordering::Equal), (True, True) => Some(std::cmp::Ordering::Equal), _ => None, @@ -407,6 +416,9 @@ impl Display for Expression { } } Expression::Function(_) => write!(f, ""), + Expression::Closure { layer, body } => { + write!(f, "(closure captured={}, body={})", layer, body) + } Expression::AnonymousFunction { argument_symbols, body, diff --git a/lispers-core/src/lisp/prelude.rs b/lispers-core/src/lisp/prelude.rs index e31628f..10e92ef 100644 --- a/lispers-core/src/lisp/prelude.rs +++ b/lispers-core/src/lisp/prelude.rs @@ -148,13 +148,18 @@ pub fn prelude_let(env: &Environment, expr: Expression) -> Result, EvalError>>()?; - eval(&env.overlay(bindings.into()), body) + Ok(Expression::Closure { + layer: bindings.into(), + body: Box::new(body), + }) } pub fn prelude_if(env: &Environment, expr: Expression) -> Result { - let [predicate, e_then, e_else] = expr.try_into()?; + let [predicate, e_then, e_else]: [Expression; 3] = expr.try_into()?; - match eval(env, predicate)? { + let val = eval(env, predicate.clone())?; + + match val { Expression::Nil => eval(env, e_else), _ => eval(env, e_then), } @@ -348,18 +353,24 @@ pub fn prelude_include(env: &Environment, expr: Expression) -> Result for ParserError { @@ -25,6 +26,7 @@ impl Display for ParserError { ParserError::TokenizerError(t) => write!(f, "Tokenizer Error: {}", t), ParserError::UnexpectedToken(t) => write!(f, "Unexpecte Token: {}", t), ParserError::UnexpectedEndOfInput => write!(f, "Unexpected end of input."), + ParserError::RuntimeError(s) => write!(f, "{}", s), } } } diff --git a/scenes/demo-4.lisp b/scenes/demo-4.lisp new file mode 100644 index 0000000..6eb72b1 --- /dev/null +++ b/scenes/demo-4.lisp @@ -0,0 +1,47 @@ +(include "./materials.lisp") + +(set 'frames 300) +(set 'fps 30) +(set 'ray-depth 5) +(set 'sub-pixel 2) + +(set 'mz (mandel-zoom frames 1800 5000)) + +(defun mandel-plane (t) + (texture-plane + (mz t) + (point 0 0 0) + (vector 0 1 0) + 1.0 + (vector 1 0 0) + )) + +(set 'l1 (light (point 3 10 5) (color 1 1 1))) +(set 'l2 (light (point 2 10 5) (color 1 1 1))) + +(defun scn (t) + (scene + (color 0.1 0.1 0.1) + '((mandel-plane t)) + '(l1 l2) + )) + +(defun cam (t c) c) + +(set 'base-cam + (camera + (point 0 3 6) + (point 0 0 0) + (vector 0 1 0) + 40 1920 1080)) + + +(render-animation + base-cam + "demo-4-animation.mp4" + scn + cam + frames + fps + ray-depth + sub-pixel) diff --git a/scenes/materials.lisp b/scenes/materials.lisp index de83466..a7e3545 100644 --- a/scenes/materials.lisp +++ b/scenes/materials.lisp @@ -35,3 +35,32 @@ (color 0.01 0.05 0.15) (color 0.01 0.05 0.15) 20 0.7)) + +(set 'mandelbrot-red + (mandelbrot-texture + 1800.0 + (point2 -0.7489967346191402 -0.06952285766601607) + 1000 + (color 0.3 0 0) + (color 0.3 0 0) + (color 0.3 0 0) + )) + + +(defun mandel-zoom (n z1 z2) + (let '((n . n) + (z1 . z1) + (z2 . z2)) + (lambda (t) + (let '((pct . (/ t (* n 1.0))) + (range . (- z2 z1))) + (mandelbrot-texture + (+ z1 (* pct range)) + (point2 -0.7489967346191402 -0.06952285766601607) + 1000 + (color 0.3 0 0) + (color 0.3 0 0) + (color 0.3 0 0) + ) + ))) + ) diff --git a/src/bin/lisp_demo.rs b/src/bin/lisp_demo.rs index 6711a56..878d0ed 100644 --- a/src/bin/lisp_demo.rs +++ b/src/bin/lisp_demo.rs @@ -8,15 +8,20 @@ fn main() { "(print myvar) (print 'myvar)", "(car (cons 'a 'b)) (cdr (cons 'c 'd)) (cons 'a 'b)", "(eval (car (cons 'myvar 'b)))", + "(defun test (i) (progn (println i) (* 2 i)))", + "(test 10)", "(set 'pow (lambda (a b) (if (= b 0) 1 (* a (pow a (- b 1))))))", "pow", "(pow 2 10)", "(let '((fib . (lambda (n) (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2))))))) (fib 10))", "(defun do-n-times (f n) (if (= n 0) '() (cons (f) (do-n-times f (- n 1)))))", - "(do-n-times (lambda () (print 'hello)) 5)", + "(do-n-times (lambda () (println 'hello)) 5)", "(progn (print 'hello) (print 'world))", "(load \"(defun loaded-foo (x) (+ x 1))\")", "(loaded-foo 1)", + "(defun bind-test (x) (let '((x . x)) (lambda (y) (+ x y))))", + "(set 'add10 (bind-test 10))", + "(add10 10)", ]; let environment = Environment::default(); @@ -31,7 +36,10 @@ fn main() { println!("Evaluating: {}", expr.clone()); match eval(&environment, expr) { Ok(e) => println!("=> {}", e), - Err(e) => println!("Error: {}", e), + Err(e) => { + println!("Error: {}", e); + return; + } } } } diff --git a/src/bin/rt_lisp_demo.rs b/src/bin/rt_lisp_demo.rs index 71b86ca..e3d0d5f 100644 --- a/src/bin/rt_lisp_demo.rs +++ b/src/bin/rt_lisp_demo.rs @@ -25,11 +25,6 @@ fn main() { } } - let mut layer = EnvironmentLayer::new(); - mk_prelude(&mut layer); - mk_raytrace(&mut layer); - let environment = Environment::from_layer(layer); - let args: Vec<_> = std::env::args().collect(); if args.len() != 2 { @@ -41,6 +36,21 @@ fn main() { return; } + let mut layer = EnvironmentLayer::new(); + mk_prelude(&mut layer); + mk_raytrace(&mut layer); + let mut environment = Environment::from_layer(layer); + environment.set( + "FILE".to_string(), + scenes + .get(&args[1]) + .expect("Scene file not found") + .path() + .display() + .to_string() + .into(), + ); + for r in ExpressionStream::from_char_stream( std::fs::read_to_string(scenes.get(&args[1]).expect("Scene file not found").path()) .expect("Failed to read scene file") @@ -49,13 +59,16 @@ fn main() { match r { Err(err) => { println!("ParserError: {:?}", err); - break; + return; } Ok(expr) => { println!("Evaluating: {}", expr.clone()); match eval(&environment, expr) { Ok(e) => println!("=> {}", e), - Err(e) => println!("Error: {}", e), + Err(e) => { + println!("Error: {}", e); + return; + } } } }