Compare commits

1 Commits
Author SHA1 Message Date
jonas 762ed3c478 closure bump 2026-09-12 14:44:08 +02:00
9 changed files with 172 additions and 14 deletions
+25 -1
View File
@@ -1,5 +1,5 @@
use super::{expression::Expression, prelude::mk_prelude}; 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)] #[derive(PartialEq, Clone, Debug)]
/// A Environment is a stack of `EnvironmentLayer`s. Each `EnvironmentLayer` is a mapping from /// 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::<Vec<_>>()
.join(", ")
)
}
}
#[test] #[test]
fn test_environment() { fn test_environment() {
let mut env = Environment::new(); let mut env = Environment::new();
+13 -1
View File
@@ -97,12 +97,23 @@ fn dispatch_anonymous_function(
} }
for (arg, symbol) in args.iter_mut().zip(argument_symbols.iter()) { 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) 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<Expression, EvalError> {
println!("Dispatching closure: {} -> {}", &layer, &body);
let env = env.overlay(layer);
eval(&env, body)
}
/// Evaluate an expression inside an environment /// Evaluate an expression inside an environment
pub fn eval(env: &Environment, expr: Expression) -> Result<Expression, EvalError> { pub fn eval(env: &Environment, expr: Expression) -> Result<Expression, EvalError> {
match expr { match expr {
@@ -114,6 +125,7 @@ pub fn eval(env: &Environment, expr: Expression) -> Result<Expression, EvalError
} => dispatch_anonymous_function(env, argument_symbols, *body, *rhs), } => dispatch_anonymous_function(env, argument_symbols, *body, *rhs),
a => Err(EvalError::NotAFunction(a)), a => Err(EvalError::NotAFunction(a)),
}, },
Expression::Closure { layer, body } => dispatch_closure(env, layer, *body),
Expression::Quote(e) => Ok(*e), Expression::Quote(e) => Ok(*e),
Expression::Symbol(s) => env.get(&s).ok_or(EvalError::SymbolNotBound(s)), Expression::Symbol(s) => env.get(&s).ok_or(EvalError::SymbolNotBound(s)),
x => Ok(x), x => Ok(x),
+12
View File
@@ -7,6 +7,7 @@ use std::ops::DerefMut;
use as_any::AsAny; use as_any::AsAny;
use super::environment::Environment; use super::environment::Environment;
use super::environment::EnvironmentLayer;
use super::eval::CellIterator; use super::eval::CellIterator;
use super::eval::EvalError; use super::eval::EvalError;
@@ -128,6 +129,10 @@ pub enum Expression {
Cell(Box<Expression>, Box<Expression>), Cell(Box<Expression>, Box<Expression>),
/// A function expression pointing to native code. /// A function expression pointing to native code.
Function(fn(&Environment, Expression) -> Result<Expression, EvalError>), Function(fn(&Environment, Expression) -> Result<Expression, EvalError>),
Closure {
layer: EnvironmentLayer,
body: Box<Expression>,
},
/// A anonymous function expression consisting of bound symbols and a body expression. /// A anonymous function expression consisting of bound symbols and a body expression.
AnonymousFunction { AnonymousFunction {
argument_symbols: Vec<String>, argument_symbols: Vec<String>,
@@ -169,7 +174,9 @@ impl PartialEq for Expression {
(ForeignExpression(f1), ForeignExpression(f2)) => PartialEq::eq(f1, f2), (ForeignExpression(f1), ForeignExpression(f2)) => PartialEq::eq(f1, f2),
(Quote(e1), Quote(e2)) => PartialEq::eq(e1, e2), (Quote(e1), Quote(e2)) => PartialEq::eq(e1, e2),
(Symbol(s1), Symbol(s2)) => PartialEq::eq(s1, s2), (Symbol(s1), Symbol(s2)) => PartialEq::eq(s1, s2),
(Integer(i1), Integer(i2)) => PartialEq::eq(i1, i2),
(Float(f1), Float(f2)) => PartialEq::eq(f1, f2), (Float(f1), Float(f2)) => PartialEq::eq(f1, f2),
(String(s1), String(s2)) => PartialEq::eq(s1, s2),
(Nil, Nil) => true, (Nil, Nil) => true,
(True, True) => true, (True, True) => true,
_ => false, _ => false,
@@ -198,6 +205,8 @@ impl PartialOrd for Expression {
(Quote(e1), Quote(e2)) => e1.partial_cmp(e2), (Quote(e1), Quote(e2)) => e1.partial_cmp(e2),
(Symbol(s1), Symbol(s2)) => s1.partial_cmp(s2), (Symbol(s1), Symbol(s2)) => s1.partial_cmp(s2),
(Float(f1), Float(f2)) => f1.partial_cmp(f2), (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), (Nil, Nil) => Some(std::cmp::Ordering::Equal),
(True, True) => Some(std::cmp::Ordering::Equal), (True, True) => Some(std::cmp::Ordering::Equal),
_ => None, _ => None,
@@ -407,6 +416,9 @@ impl Display for Expression {
} }
} }
Expression::Function(_) => write!(f, "<function>"), Expression::Function(_) => write!(f, "<function>"),
Expression::Closure { layer, body } => {
write!(f, "(closure captured={}, body={})", layer, body)
}
Expression::AnonymousFunction { Expression::AnonymousFunction {
argument_symbols, argument_symbols,
body, body,
+14 -3
View File
@@ -148,13 +148,18 @@ pub fn prelude_let(env: &Environment, expr: Expression) -> Result<Expression, Ev
}) })
.collect::<Result<HashMap<String, Expression>, EvalError>>()?; .collect::<Result<HashMap<String, Expression>, 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<Expression, EvalError> { pub fn prelude_if(env: &Environment, expr: Expression) -> Result<Expression, EvalError> {
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), Expression::Nil => eval(env, e_else),
_ => eval(env, e_then), _ => eval(env, e_then),
} }
@@ -348,18 +353,24 @@ pub fn prelude_include(env: &Environment, expr: Expression) -> Result<Expression
let [expr] = expr.try_into()?; let [expr] = expr.try_into()?;
let lisp_file: String = eval(env, expr)?.try_into()?; let lisp_file: String = eval(env, expr)?.try_into()?;
println!("From File: {}", env.get("FILE").unwrap());
// Try to resolve as relative to FILE // Try to resolve as relative to FILE
let resolved_lisp_file: PathBuf = PathBuf::from( let resolved_lisp_file: PathBuf = PathBuf::from(
env.get("FILE") env.get("FILE")
.map(|x| x.try_into()) .map(|x| x.try_into())
.unwrap_or(Ok(String::new()))?, .unwrap_or(Ok(String::new()))?,
) )
.canonicalize()
.map_err(|x| EvalError::RuntimeError(x.to_string()))?
.parent() .parent()
.ok_or(EvalError::RuntimeError( .ok_or(EvalError::RuntimeError(
"Could not get parent of current file.".to_string(), "Could not get parent of current file.".to_string(),
))? ))?
.join(&lisp_file); .join(&lisp_file);
println!("Loading {}", resolved_lisp_file.display());
let lisp_string = std::fs::read_to_string(&resolved_lisp_file) let lisp_string = std::fs::read_to_string(&resolved_lisp_file)
.map_err(|e| EvalError::RuntimeError(e.to_string()))?; .map_err(|e| EvalError::RuntimeError(e.to_string()))?;
+2
View File
@@ -11,6 +11,7 @@ pub enum ParserError {
UnexpectedToken(Token), UnexpectedToken(Token),
TokenizerError(TokenizerError), TokenizerError(TokenizerError),
UnexpectedEndOfInput, UnexpectedEndOfInput,
RuntimeError(String),
} }
impl From<TokenizerError> for ParserError { impl From<TokenizerError> for ParserError {
@@ -25,6 +26,7 @@ impl Display for ParserError {
ParserError::TokenizerError(t) => write!(f, "Tokenizer Error: {}", t), ParserError::TokenizerError(t) => write!(f, "Tokenizer Error: {}", t),
ParserError::UnexpectedToken(t) => write!(f, "Unexpecte Token: {}", t), ParserError::UnexpectedToken(t) => write!(f, "Unexpecte Token: {}", t),
ParserError::UnexpectedEndOfInput => write!(f, "Unexpected end of input."), ParserError::UnexpectedEndOfInput => write!(f, "Unexpected end of input."),
ParserError::RuntimeError(s) => write!(f, "{}", s),
} }
} }
} }
+47
View File
@@ -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)
+29
View File
@@ -35,3 +35,32 @@
(color 0.01 0.05 0.15) (color 0.01 0.05 0.15)
(color 0.01 0.05 0.15) (color 0.01 0.05 0.15)
20 0.7)) 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)
)
)))
)
+10 -2
View File
@@ -8,15 +8,20 @@ fn main() {
"(print myvar) (print 'myvar)", "(print myvar) (print 'myvar)",
"(car (cons 'a 'b)) (cdr (cons 'c 'd)) (cons 'a 'b)", "(car (cons 'a 'b)) (cdr (cons 'c 'd)) (cons 'a 'b)",
"(eval (car (cons 'myvar '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))))))", "(set 'pow (lambda (a b) (if (= b 0) 1 (* a (pow a (- b 1))))))",
"pow", "pow",
"(pow 2 10)", "(pow 2 10)",
"(let '((fib . (lambda (n) (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2))))))) (fib 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)))))", "(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))", "(progn (print 'hello) (print 'world))",
"(load \"(defun loaded-foo (x) (+ x 1))\")", "(load \"(defun loaded-foo (x) (+ x 1))\")",
"(loaded-foo 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(); let environment = Environment::default();
@@ -31,7 +36,10 @@ fn main() {
println!("Evaluating: {}", expr.clone()); println!("Evaluating: {}", expr.clone());
match eval(&environment, expr) { match eval(&environment, expr) {
Ok(e) => println!("=> {}", e), Ok(e) => println!("=> {}", e),
Err(e) => println!("Error: {}", e), Err(e) => {
println!("Error: {}", e);
return;
}
} }
} }
} }
+20 -7
View File
@@ -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(); let args: Vec<_> = std::env::args().collect();
if args.len() != 2 { if args.len() != 2 {
@@ -41,6 +36,21 @@ fn main() {
return; 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( for r in ExpressionStream::from_char_stream(
std::fs::read_to_string(scenes.get(&args[1]).expect("Scene file not found").path()) std::fs::read_to_string(scenes.get(&args[1]).expect("Scene file not found").path())
.expect("Failed to read scene file") .expect("Failed to read scene file")
@@ -49,13 +59,16 @@ fn main() {
match r { match r {
Err(err) => { Err(err) => {
println!("ParserError: {:?}", err); println!("ParserError: {:?}", err);
break; return;
} }
Ok(expr) => { Ok(expr) => {
println!("Evaluating: {}", expr.clone()); println!("Evaluating: {}", expr.clone());
match eval(&environment, expr) { match eval(&environment, expr) {
Ok(e) => println!("=> {}", e), Ok(e) => println!("=> {}", e),
Err(e) => println!("Error: {}", e), Err(e) => {
println!("Error: {}", e);
return;
}
} }
} }
} }