diff --git a/src/bin/demo.rs b/src/bin/demo.rs index 4023f19..f9cc5fc 100644 --- a/src/bin/demo.rs +++ b/src/bin/demo.rs @@ -12,7 +12,6 @@ fn main() { "pow", "(pow 2 10)", "(let '((fib . (lambda (n) (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2))))))) (fib 10))", - "(let '((a . (vec3 1 2 3)) (b . (vec3 4 5 6))) (vec3-dot (vec3-norm (vec3-add a b)) a))", "(defun do-n-times (f n) (if (= n 0) '() (cons (f) (do-n-times f (- n 1)))))", "(do-n-times (lambda () (print 'hello)) 5)", "(progn (print 'hello) (print 'world))", diff --git a/src/lisp/environment.rs b/src/lisp/environment.rs index 7c57c1b..2bda578 100644 --- a/src/lisp/environment.rs +++ b/src/lisp/environment.rs @@ -1,4 +1,4 @@ -use super::{expression::Expression, prelude::mk_prelude, vec::mk_vec3}; +use super::{expression::Expression, prelude::mk_prelude}; use std::{cell::RefCell, collections::HashMap, rc::Rc}; #[derive(PartialEq, Clone, Debug)] @@ -124,11 +124,10 @@ impl<'a> Environment<'a> { } impl Default for Environment<'_> { - /// Get the default prelude+vec3 layer + /// Get the default prelude layer fn default() -> Self { let mut d = EnvironmentLayer::new(); mk_prelude(&mut d); - mk_vec3(&mut d); Environment { layer: d, outer: None, diff --git a/src/lisp/eval.rs b/src/lisp/eval.rs index 5b2a2e9..a122849 100644 --- a/src/lisp/eval.rs +++ b/src/lisp/eval.rs @@ -13,6 +13,7 @@ pub enum EvalError { ArgumentError(String), TypeError(String), NotASymbol(Expression), + RuntimeError(String), } impl Display for EvalError { @@ -24,6 +25,7 @@ impl Display for EvalError { EvalError::ArgumentError(s) => write!(f, "Argument error: {}", s), EvalError::TypeError(s) => write!(f, "Type error: {}", s), EvalError::NotASymbol(e) => write!(f, "Expression {} is not a symbol", e), + EvalError::RuntimeError(s) => write!(f, "Runtime error: {}", s), } } } diff --git a/src/lisp/expression.rs b/src/lisp/expression.rs index 11892cc..f741ad7 100644 --- a/src/lisp/expression.rs +++ b/src/lisp/expression.rs @@ -1,5 +1,8 @@ +use std::any::Any; use std::fmt::Debug; use std::fmt::Display; +use std::ops::Deref; +use std::ops::DerefMut; use as_any::AsAny; @@ -9,51 +12,110 @@ use super::eval::EvalError; /// A trait for foreign data types that can be used in lisp expressions. /// Note: This trait requires explicit implementation of: -/// - partial_cmp -/// - clone_data -/// - eq -/// To avoid a derive cycle. +/// - partial_cmp_impl +/// - clone_impl +/// - eq_impl +/// - as_any_box +/// to ensure object safety. pub trait ForeignData: Debug + Display + AsAny { - fn partial_cmp(&self, other: &dyn ForeignData) -> Option; - fn clone_data(&self) -> Box; - fn eq(&self, other: &dyn ForeignData) -> bool; + fn partial_cmp_impl(&self, other: &dyn ForeignData) -> Option; + fn clone_impl(&self) -> Box; + fn eq_impl(&self, other: &dyn ForeignData) -> bool; + fn as_any_box(self: Box) -> Box; } -#[derive(Debug)] -/// A Wrapper struct for foreign data types injected in expressions. -pub struct ForeignDataWrapper { - /// The actual foreign data. - pub data: Box, -} +impl ForeignData for T { + fn partial_cmp_impl(&self, other: &dyn ForeignData) -> Option { + if let Some(other) = other.as_any().downcast_ref::() { + self.partial_cmp(other) + } else { + None + } + } -impl ForeignDataWrapper { - /// Create a new ForeignDataWrapper from a ForeignData trait object. - pub fn new(data: Box) -> Self { - ForeignDataWrapper { data } + fn clone_impl(&self) -> Box { + Box::new(self.clone()) + } + + fn eq_impl(&self, other: &dyn ForeignData) -> bool { + if let Some(other) = other.as_any().downcast_ref::() { + self.eq(other) + } else { + false + } + } + + fn as_any_box(self: Box) -> Box { + self } } -impl Clone for ForeignDataWrapper { +/// A wrapper struct around any foreign data type. This struct is used to convert +/// any T implementing ForeignData to an Expression and vice versa. +#[derive(Debug)] +pub struct ForeignDataWrapper(pub Box); +impl ForeignDataWrapper { + /// Create a new ForeignDataWrapper from an object implementing ForeignData. + pub fn new(data: T) -> Self { + ForeignDataWrapper(Box::new(data)) + } +} + +impl Deref for ForeignDataWrapper { + type Target = T; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for ForeignDataWrapper { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +#[derive(Debug)] +/// A Store struct for foreign data types injected in expressions. +pub struct ForeignDataStore { + /// The actual foreign data. + data: Box, +} + +/// The ForeignDataStore struct is used to store any foreign data type in an Expression +/// and cannot be constructed outside of this scope. +impl ForeignDataStore { + /// Create a new ForeignDataStore from a ForeignData trait object. + fn new(data: Box) -> Self { + ForeignDataStore { data } + } + + /// Get the contained box as an Any-Box with type info of the actual data. + fn as_any_box(self) -> Box { + self.data.as_any_box() + } +} + +impl Clone for ForeignDataStore { fn clone(&self) -> Self { - ForeignDataWrapper { - data: self.data.clone_data(), + ForeignDataStore { + data: self.data.clone_impl(), } } } -impl PartialEq for ForeignDataWrapper { +impl PartialEq for ForeignDataStore { fn eq(&self, other: &Self) -> bool { - self.data.eq(other.data.as_ref()) + self.data.eq_impl(other.data.as_ref()) } } -impl PartialOrd for ForeignDataWrapper { +impl PartialOrd for ForeignDataStore { fn partial_cmp(&self, other: &Self) -> Option { - self.data.partial_cmp(other.data.as_ref()) + self.data.partial_cmp_impl(other.data.as_ref()) } } -impl Display for ForeignDataWrapper { +impl Display for ForeignDataStore { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.data) } @@ -72,7 +134,7 @@ pub enum Expression { body: Box, }, /// A foreign data expression. - ForeignExpression(ForeignDataWrapper), + ForeignExpression(ForeignDataStore), /// A Quoted expression. Quote(Box), /// A symbol. @@ -89,6 +151,29 @@ pub enum Expression { Nil, } +impl From> for Expression { + fn from(value: ForeignDataWrapper) -> Expression { + Expression::ForeignExpression(ForeignDataStore::new(value.0)) + } +} + +impl TryFrom for ForeignDataWrapper { + type Error = EvalError; + fn try_from(value: Expression) -> Result { + match value { + Expression::ForeignExpression(f) => match f.as_any_box().downcast::() { + Ok(data) => Ok(ForeignDataWrapper(data)), + Err(_) => Err(EvalError::TypeError( + "Expression is not a ForeignDataWrapper".to_string(), + )), + }, + _ => Err(EvalError::TypeError( + "Expression is not a ForeignDataWrapper".to_string(), + )), + } + } +} + impl From Result> for Expression { fn from(f: fn(&Environment, Expression) -> Result) -> Self { Expression::Function(f) diff --git a/src/lisp/mod.rs b/src/lisp/mod.rs index 0583b4d..c8519eb 100644 --- a/src/lisp/mod.rs +++ b/src/lisp/mod.rs @@ -2,7 +2,6 @@ pub mod environment; pub mod eval; pub mod expression; pub mod prelude; -pub mod vec; pub use environment::Environment; pub use eval::eval; diff --git a/src/lisp/vec.rs b/src/lisp/vec.rs deleted file mode 100644 index 266ada9..0000000 --- a/src/lisp/vec.rs +++ /dev/null @@ -1,141 +0,0 @@ -use std::fmt::Display; - -use super::{ - environment::{Environment, EnvironmentLayer}, - eval::{eval, EvalError}, - expression::ForeignData, - expression::{Expression, ForeignDataWrapper}, -}; - -#[derive(Debug, Clone, Copy)] -/// A simple 3d vector. -struct Vec3 { - x: f64, - y: f64, - z: f64, -} - -impl Display for Vec3 { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "(vec3 {} {} {})", self.x, self.y, self.z) - } -} - -impl ForeignData for Vec3 { - fn clone_data(&self) -> Box { - Box::new(*self) - } - fn eq(&self, other: &dyn ForeignData) -> bool { - if let Some(other) = other.as_any().downcast_ref::() { - self.x == other.x && self.y == other.y && self.z == other.z - } else { - false - } - } - fn partial_cmp(&self, other: &dyn ForeignData) -> Option { - if let Some(other) = other.as_any().downcast_ref::() { - Some( - self.x - .partial_cmp(&other.x)? - .then(self.y.partial_cmp(&other.y)?) - .then(self.z.partial_cmp(&other.z)?), - ) - } else { - None - } - } -} - -impl TryFrom for Vec3 { - type Error = EvalError; - fn try_from(value: Expression) -> Result { - match value { - Expression::ForeignExpression(fe) => { - if let Some(vec) = fe.data.as_ref().as_any().downcast_ref::() { - Ok(*vec) - } else { - Err(EvalError::TypeError("Expected vec3".to_string())) - } - } - _ => Err(EvalError::TypeError("Expected vec3".to_string())), - } - } -} - -impl From for Expression { - fn from(value: Vec3) -> Self { - Expression::ForeignExpression(ForeignDataWrapper::new(Box::new(value))) - } -} - -/// Create a vec3 expression from a list of 3 floats -pub fn vec_vec(_env: &Environment, expr: Expression) -> Result { - let [x, y, z]: [f64; 3] = expr.try_into()?; - - Ok(Vec3 { x, y, z }.into()) -} - -/// Add two vec3 expressions -pub fn vec_add(env: &Environment, expr: Expression) -> Result { - let [a, b]: [Expression; 2] = expr.try_into()?; - - let a = Vec3::try_from(eval(env, a)?)?; - let b = Vec3::try_from(eval(env, b)?)?; - - Ok(Vec3 { - x: a.x + b.x, - y: a.y + b.y, - z: a.z + b.z, - } - .into()) -} - -/// Scale a vector by a factor. First argument is the factor, second the vector -pub fn vec_scale(env: &Environment, expr: Expression) -> Result { - let [a, b]: [Expression; 2] = expr.try_into()?; - - let a = f64::try_from(eval(env, a)?)?; - let b = Vec3::try_from(eval(env, b)?)?; - - Ok(Vec3 { - x: a * b.x, - y: a * b.y, - z: a * b.z, - } - .into()) -} - -/// Calculate the dot product of two vec3 -pub fn vec_dot(env: &Environment, expr: Expression) -> Result { - let [a, b]: [Expression; 2] = expr.try_into()?; - - let a = Vec3::try_from(eval(env, a)?)?; - let b = Vec3::try_from(eval(env, b)?)?; - - Ok(Expression::Float(a.x * b.x + a.y * b.y + a.z * b.z)) -} - -/// Get the L2-norm of a vector -pub fn vec_norm(env: &Environment, expr: Expression) -> Result { - let [arg]: [Expression; 1] = expr.try_into()?; - - let vec = Vec3::try_from(eval(env, arg)?)?; - - let length = (vec.x.powi(2) + vec.y.powi(2) + vec.z.powi(2)).sqrt(); - - Ok(Vec3 { - x: vec.x / length, - y: vec.y / length, - z: vec.z / length, - } - .into()) -} - -/// Add vec3 functions to a layer -pub fn mk_vec3(layer: &mut EnvironmentLayer) { - layer.set("vec3".to_string(), Expression::Function(vec_vec)); - layer.set("vec3-add".to_string(), Expression::Function(vec_add)); - layer.set("vec3-scale".to_string(), Expression::Function(vec_scale)); - layer.set("vec3-dot".to_string(), Expression::Function(vec_dot)); - layer.set("vec3-norm".to_string(), Expression::Function(vec_norm)); -}