From 6f8499acafa1085b10a29863aba0c7fddc1f5e55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20R=C3=B6ger?= Date: Sat, 9 Nov 2024 19:22:01 +0100 Subject: [PATCH] feat: add foregin exprssions and vec3 functions --- Cargo.lock | 7 +++ Cargo.toml | 1 + src/lisp/environment.rs | 11 ++--- src/lisp/expression.rs | 58 +++++++++++++++++++++++++ src/lisp/mod.rs | 3 +- src/lisp/vec.rs | 96 +++++++++++++++++++++++++++++++++++++++++ src/main.rs | 1 + 7 files changed, 170 insertions(+), 7 deletions(-) create mode 100644 src/lisp/vec.rs diff --git a/Cargo.lock b/Cargo.lock index 4de70a7..7d2c045 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "as-any" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a30a44e99a1c83ccb2a6298c563c888952a1c9134953db26876528f84c93a" + [[package]] name = "autocfg" version = "1.4.0" @@ -125,6 +131,7 @@ checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" name = "lispers" version = "0.1.0" dependencies = [ + "as-any", "futures", "nix", ] diff --git a/Cargo.toml b/Cargo.toml index bb4667a..1eeca67 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,5 +7,6 @@ version = "0.1.0" edition = "2021" [dependencies] +as-any = "0.3.1" futures = "0.3.30" nix = "0.29.0" diff --git a/src/lisp/environment.rs b/src/lisp/environment.rs index dad1750..7c57c1b 100644 --- a/src/lisp/environment.rs +++ b/src/lisp/environment.rs @@ -1,4 +1,4 @@ -use super::{expression::Expression, prelude::mk_prelude}; +use super::{expression::Expression, prelude::mk_prelude, vec::mk_vec3}; use std::{cell::RefCell, collections::HashMap, rc::Rc}; #[derive(PartialEq, Clone, Debug)] @@ -124,12 +124,13 @@ impl<'a> Environment<'a> { } impl Default for Environment<'_> { - /// Get the default prelude layer + /// Get the default prelude+vec3 layer fn default() -> Self { - let mut prelude = EnvironmentLayer::new(); - mk_prelude(&mut prelude); + let mut d = EnvironmentLayer::new(); + mk_prelude(&mut d); + mk_vec3(&mut d); Environment { - layer: prelude, + layer: d, outer: None, shared: Rc::new(RefCell::new(EnvironmentLayer::new())), } diff --git a/src/lisp/expression.rs b/src/lisp/expression.rs index fb7b088..8a34a98 100644 --- a/src/lisp/expression.rs +++ b/src/lisp/expression.rs @@ -1,9 +1,64 @@ +use std::fmt::Debug; use std::fmt::Display; +use as_any::AsAny; + use super::environment::Environment; use super::eval::CellIterator; 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. +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; +} + +#[derive(Debug)] +/// A Wrapper struct for foreign data types injected in expressions. +pub struct ForeignDataWrapper { + /// The actual foreign data. + pub data: Box, +} + +impl ForeignDataWrapper { + /// Create a new ForeignDataWrapper from a ForeignData trait object. + pub fn new(data: Box) -> Self { + ForeignDataWrapper { data } + } +} + +impl Clone for ForeignDataWrapper { + fn clone(&self) -> Self { + ForeignDataWrapper { + data: self.data.clone_data(), + } + } +} + +impl PartialEq for ForeignDataWrapper { + fn eq(&self, other: &Self) -> bool { + self.data.eq(other.data.as_ref()) + } +} + +impl PartialOrd for ForeignDataWrapper { + fn partial_cmp(&self, other: &Self) -> Option { + self.data.partial_cmp(other.data.as_ref()) + } +} + +impl Display for ForeignDataWrapper { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.data) + } +} + #[derive(Clone, Debug, PartialEq, PartialOrd)] /// A sum type of all possible lisp expressions. pub enum Expression { @@ -16,6 +71,8 @@ pub enum Expression { argument_symbols: Vec, body: Box, }, + /// A foreign data expression. + ForeignExpression(ForeignDataWrapper), /// A Quoted expression. Quote(Box), /// A symbol. @@ -155,6 +212,7 @@ impl TryFrom for (Expression, Expression) { impl Display for Expression { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { + Expression::ForeignExpression(e) => write!(f, "{}", e), Expression::Cell(a, b) => { match self.clone().try_into() as Result, EvalError> { Ok(lst) => write!( diff --git a/src/lisp/mod.rs b/src/lisp/mod.rs index 8a62709..0583b4d 100644 --- a/src/lisp/mod.rs +++ b/src/lisp/mod.rs @@ -2,9 +2,8 @@ pub mod environment; pub mod eval; pub mod expression; pub mod prelude; +pub mod vec; pub use environment::Environment; -pub use environment::EnvironmentLayer; pub use eval::eval; -pub use eval::EvalError; pub use expression::Expression; diff --git a/src/lisp/vec.rs b/src/lisp/vec.rs new file mode 100644 index 0000000..5e374e7 --- /dev/null +++ b/src/lisp/vec.rs @@ -0,0 +1,96 @@ +use std::fmt::Display; + +use as_any::AsAny; + +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())), + } + } +} + +/// 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(Expression::ForeignExpression(ForeignDataWrapper::new( + Box::new(Vec3 { x, y, z }), + ))) +} + +/// 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(Expression::ForeignExpression(ForeignDataWrapper::new( + Box::new(Vec3 { + x: a.x + b.x, + y: a.y + b.y, + z: a.z + b.z, + }), + ))) +} + +/// 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)); +} diff --git a/src/main.rs b/src/main.rs index 70d05c3..98d4f42 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,6 +15,7 @@ fn main() { "pow", "(pow 2 10)", "(let '((fib . (lambda (n) (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2))))))) (fib 10))", + "(vec3-add (vec3 1.0 2.0 3.0) (vec3 4.0 5.0 6.0))", ]; let environment = Environment::default();