feat: add foregin exprssions and vec3 functions

This commit is contained in:
Jonas Röger 2024-11-09 19:22:01 +01:00
parent 8ce07f4ea4
commit 6f8499acaf
Signed by: jonas
GPG Key ID: 4000EB35E1AE0F07
7 changed files with 170 additions and 7 deletions

7
Cargo.lock generated
View File

@ -2,6 +2,12 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 3
[[package]]
name = "as-any"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b8a30a44e99a1c83ccb2a6298c563c888952a1c9134953db26876528f84c93a"
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.4.0" version = "1.4.0"
@ -125,6 +131,7 @@ checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1"
name = "lispers" name = "lispers"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"as-any",
"futures", "futures",
"nix", "nix",
] ]

View File

@ -7,5 +7,6 @@ version = "0.1.0"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
as-any = "0.3.1"
futures = "0.3.30" futures = "0.3.30"
nix = "0.29.0" nix = "0.29.0"

View File

@ -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}; use std::{cell::RefCell, collections::HashMap, rc::Rc};
#[derive(PartialEq, Clone, Debug)] #[derive(PartialEq, Clone, Debug)]
@ -124,12 +124,13 @@ impl<'a> Environment<'a> {
} }
impl Default for Environment<'_> { impl Default for Environment<'_> {
/// Get the default prelude layer /// Get the default prelude+vec3 layer
fn default() -> Self { fn default() -> Self {
let mut prelude = EnvironmentLayer::new(); let mut d = EnvironmentLayer::new();
mk_prelude(&mut prelude); mk_prelude(&mut d);
mk_vec3(&mut d);
Environment { Environment {
layer: prelude, layer: d,
outer: None, outer: None,
shared: Rc::new(RefCell::new(EnvironmentLayer::new())), shared: Rc::new(RefCell::new(EnvironmentLayer::new())),
} }

View File

@ -1,9 +1,64 @@
use std::fmt::Debug;
use std::fmt::Display; use std::fmt::Display;
use as_any::AsAny;
use super::environment::Environment; use super::environment::Environment;
use super::eval::CellIterator; use super::eval::CellIterator;
use super::eval::EvalError; 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<std::cmp::Ordering>;
fn clone_data(&self) -> Box<dyn ForeignData>;
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<dyn ForeignData>,
}
impl ForeignDataWrapper {
/// Create a new ForeignDataWrapper from a ForeignData trait object.
pub fn new(data: Box<dyn ForeignData>) -> 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<std::cmp::Ordering> {
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)] #[derive(Clone, Debug, PartialEq, PartialOrd)]
/// A sum type of all possible lisp expressions. /// A sum type of all possible lisp expressions.
pub enum Expression { pub enum Expression {
@ -16,6 +71,8 @@ pub enum Expression {
argument_symbols: Vec<String>, argument_symbols: Vec<String>,
body: Box<Expression>, body: Box<Expression>,
}, },
/// A foreign data expression.
ForeignExpression(ForeignDataWrapper),
/// A Quoted expression. /// A Quoted expression.
Quote(Box<Expression>), Quote(Box<Expression>),
/// A symbol. /// A symbol.
@ -155,6 +212,7 @@ impl TryFrom<Expression> for (Expression, Expression) {
impl Display for Expression { impl Display for Expression {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
Expression::ForeignExpression(e) => write!(f, "{}", e),
Expression::Cell(a, b) => { Expression::Cell(a, b) => {
match self.clone().try_into() as Result<Vec<Expression>, EvalError> { match self.clone().try_into() as Result<Vec<Expression>, EvalError> {
Ok(lst) => write!( Ok(lst) => write!(

View File

@ -2,9 +2,8 @@ pub mod environment;
pub mod eval; pub mod eval;
pub mod expression; pub mod expression;
pub mod prelude; pub mod prelude;
pub mod vec;
pub use environment::Environment; pub use environment::Environment;
pub use environment::EnvironmentLayer;
pub use eval::eval; pub use eval::eval;
pub use eval::EvalError;
pub use expression::Expression; pub use expression::Expression;

96
src/lisp/vec.rs Normal file
View File

@ -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<dyn ForeignData> {
Box::new(*self)
}
fn eq(&self, other: &dyn ForeignData) -> bool {
if let Some(other) = other.as_any().downcast_ref::<Vec3>() {
self.x == other.x && self.y == other.y && self.z == other.z
} else {
false
}
}
fn partial_cmp(&self, other: &dyn ForeignData) -> Option<std::cmp::Ordering> {
if let Some(other) = other.as_any().downcast_ref::<Vec3>() {
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<Expression> for Vec3 {
type Error = EvalError;
fn try_from(value: Expression) -> Result<Self, Self::Error> {
match value {
Expression::ForeignExpression(fe) => {
if let Some(vec) = fe.data.as_ref().as_any().downcast_ref::<Vec3>() {
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<Expression, EvalError> {
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<Expression, EvalError> {
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));
}

View File

@ -15,6 +15,7 @@ fn main() {
"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))",
"(vec3-add (vec3 1.0 2.0 3.0) (vec3 4.0 5.0 6.0))",
]; ];
let environment = Environment::default(); let environment = Environment::default();