feat: add native_lisp_function macro

- refactor project layout to use child crates
  - lispers-core: parser and evaluator
  - lispers-macro: proc macros
This commit is contained in:
Jonas Röger 2025-01-04 20:12:11 +01:00
parent 9179f06132
commit 3e11142361
Signed by: jonas
GPG Key ID: 4000EB35E1AE0F07
21 changed files with 243 additions and 72 deletions

119
Cargo.lock generated
View File

@ -16,9 +16,9 @@ checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1"
[[package]]
name = "anyhow"
version = "1.0.93"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775"
checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
[[package]]
name = "approx"
@ -125,9 +125,9 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
[[package]]
name = "bytemuck"
version = "1.20.0"
version = "1.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b37c88a63ffd85d15b406896cc343916d7cf57838a847b3a6f2ca5d39a5695a"
checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3"
[[package]]
name = "byteorder"
@ -143,9 +143,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
[[package]]
name = "cc"
version = "1.2.1"
version = "1.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47"
checksum = "a012a0df96dd6d06ba9a1b29d6402d1a5d77c6befd2566afdc26e10603dc93d7"
dependencies = [
"jobserver",
"libc",
@ -191,9 +191,9 @@ dependencies = [
[[package]]
name = "crossbeam-deque"
version = "0.8.5"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
dependencies = [
"crossbeam-epoch",
"crossbeam-utils",
@ -210,9 +210,9 @@ dependencies = [
[[package]]
name = "crossbeam-utils"
version = "0.8.20"
version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "crunchy"
@ -249,9 +249,9 @@ dependencies = [
[[package]]
name = "fdeflate"
version = "0.3.6"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07c6f4c64c1d33a3111c4466f7365ebdcc37c5bd1ea0d62aae2e3d722aacbedb"
checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c"
dependencies = [
"simd-adler32",
]
@ -388,9 +388,9 @@ dependencies = [
[[package]]
name = "hashbrown"
version = "0.15.1"
version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3"
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
[[package]]
name = "heck"
@ -439,9 +439,9 @@ checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408"
[[package]]
name = "indexmap"
version = "2.6.0"
version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da"
checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f"
dependencies = [
"equivalent",
"hashbrown",
@ -490,9 +490,9 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
[[package]]
name = "libc"
version = "0.2.164"
version = "0.2.169"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f"
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
[[package]]
name = "libfuzzer-sys"
@ -511,11 +511,29 @@ dependencies = [
"as-any",
"futures",
"image",
"lispers-core",
"lispers-macro",
"nalgebra",
"nix",
"rayon",
]
[[package]]
name = "lispers-core"
version = "0.1.0"
dependencies = [
"as-any",
]
[[package]]
name = "lispers-macro"
version = "0.1.0"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "log"
version = "0.4.22"
@ -565,9 +583,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "miniz_oxide"
version = "0.8.0"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394"
dependencies = [
"adler2",
"simd-adler32",
@ -725,9 +743,9 @@ checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
[[package]]
name = "png"
version = "0.17.14"
version = "0.17.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52f9d46a34a05a6a57566bc2bfae066ef07585a6e3fa30fbbdff5936380623f0"
checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526"
dependencies = [
"bitflags 1.3.2",
"crc32fast",
@ -747,9 +765,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.89"
version = "1.0.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
dependencies = [
"unicode-ident",
]
@ -790,9 +808,9 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
[[package]]
name = "quote"
version = "1.0.37"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
dependencies = [
"proc-macro2",
]
@ -911,27 +929,27 @@ checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a"
[[package]]
name = "safe_arch"
version = "0.7.2"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3460605018fdc9612bce72735cba0d27efbcd9904780d44c7e3a9948f96148a"
checksum = "96b02de82ddbe1b636e6170c21be622223aea188ef2e139be0a5b219ec215323"
dependencies = [
"bytemuck",
]
[[package]]
name = "serde"
version = "1.0.215"
version = "1.0.217"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f"
checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.215"
version = "1.0.217"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0"
checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
dependencies = [
"proc-macro2",
"quote",
@ -998,9 +1016,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]]
name = "syn"
version = "2.0.87"
version = "2.0.94"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d"
checksum = "987bc0be1cdea8b10216bd06e2ca407d40b9543468fafd3ddfb02f36e77f71f3"
dependencies = [
"proc-macro2",
"quote",
@ -1128,9 +1146,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.95"
version = "0.2.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e"
checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396"
dependencies = [
"cfg-if",
"once_cell",
@ -1139,13 +1157,12 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.95"
version = "0.2.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358"
checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn",
@ -1154,9 +1171,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.95"
version = "0.2.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56"
checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@ -1164,9 +1181,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.95"
version = "0.2.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68"
checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2"
dependencies = [
"proc-macro2",
"quote",
@ -1177,9 +1194,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.95"
version = "0.2.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d"
checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6"
[[package]]
name = "weezl"
@ -1189,9 +1206,9 @@ checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082"
[[package]]
name = "wide"
version = "0.7.30"
version = "0.7.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58e6db2670d2be78525979e9a5f9c69d296fd7d670549fe9ebf70f8708cb5019"
checksum = "cc0ca27312d1e9218687a4e804cd4b7410bf54125d54d13e5170efbf09066d24"
dependencies = [
"bytemuck",
"safe_arch",
@ -1199,9 +1216,9 @@ dependencies = [
[[package]]
name = "winnow"
version = "0.6.20"
version = "0.6.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b"
checksum = "39281189af81c07ec09db316b302a3e67bf9bd7cbf6c820b50e35fee9c2fa980"
dependencies = [
"memchr",
]
@ -1244,9 +1261,9 @@ dependencies = [
[[package]]
name = "zune-jpeg"
version = "0.4.13"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16099418600b4d8f028622f73ff6e3deaabdff330fb9a2a131dea781ee8b0768"
checksum = "99a5bab8d7dedf81405c4bb1f2b83ea057643d9cb28778cea9eecddeedd2e028"
dependencies = [
"zune-core",
]

View File

@ -22,10 +22,20 @@ path = "src/bin/repl.rs"
name = "rt_lisp_demo"
path = "src/bin/rt_lisp_demo.rs"
[dependencies]
[workspace]
members = [ "lispers-core", "lispers-macro"]
[workspace.dependencies]
lispers-core = {path = "lispers-core"}
lispers-macro = {path = "lispers-macro"}
as-any = "0.3.1"
[dependencies]
as-any = {workspace = true}
futures = "0.3.30"
image = "0.25.5"
nalgebra = "0.33.2"
nix = "0.29.0"
rayon = "1.10.0"
lispers-core = {workspace = true}
lispers-macro = {workspace = true}

7
lispers-core/Cargo.toml Normal file
View File

@ -0,0 +1,7 @@
[package]
name = "lispers-core"
version = "0.1.0"
edition = "2021"
[dependencies]
as-any = {workspace = true}

2
lispers-core/src/lib.rs Normal file
View File

@ -0,0 +1,2 @@
pub mod lisp;
pub mod parser;

12
lispers-macro/Cargo.toml Normal file
View File

@ -0,0 +1,12 @@
[package]
name = "lispers-macro"
version = "0.1.0"
edition = "2021"
[lib]
proc-macro = true
[dependencies]
proc-macro2 = "1.0.92"
quote = "1.0.38"
syn = "2.0.94"

114
lispers-macro/src/lib.rs Normal file
View File

@ -0,0 +1,114 @@
extern crate proc_macro;
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, punctuated::Punctuated, FnArg, Ident, ItemFn, Pat, PatType, Token};
enum FlagOrKV {
Flag(Ident),
KV(Ident, Ident),
}
impl syn::parse::Parse for FlagOrKV {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let ident: Ident = input.parse()?;
if input.peek(Token![=]) {
input.parse::<Token![=]>()?;
let value: Ident = input.parse()?;
Ok(FlagOrKV::KV(ident, value))
} else {
Ok(FlagOrKV::Flag(ident))
}
}
}
struct Attrs {
pub eval: bool,
pub fname: Option<Ident>,
}
impl syn::parse::Parse for Attrs {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let exprs = Punctuated::<FlagOrKV, Token![,]>::parse_terminated(input)?;
let mut ret = Attrs {
eval: false,
fname: None,
};
for e in exprs {
match e {
FlagOrKV::Flag(flag) => {
if flag.to_string() == "eval" {
ret.eval = true;
} else {
return Err(syn::Error::new_spanned(flag, "Unknown flag"));
}
}
FlagOrKV::KV(k, v) => {
if k.to_string() == "fname" {
ret.fname = Some(v);
} else {
return Err(syn::Error::new_spanned(k, "Unknown key"));
}
}
}
}
Ok(ret)
}
}
#[proc_macro_attribute]
pub fn native_lisp_function(attr: TokenStream, item: TokenStream) -> TokenStream {
// Parse function
let input = parse_macro_input!(item as ItemFn);
let vis = &input.vis;
let sig = &input.sig;
let func_name = &sig.ident;
let block = &input.block;
let ret = &sig.output;
// Parse attrs
let attr = parse_macro_input!(attr as Attrs);
// Extract argument conversion statements
let mut conversion_statements = Vec::new();
for arg in &sig.inputs {
if let FnArg::Typed(PatType { pat, ty, .. }) = arg {
if let Pat::Ident(ident) = pat.as_ref() {
let arg_name = &ident.ident;
let arg_name_str = arg_name.to_string();
if attr.eval {
conversion_statements.push(quote! {
let #arg_name: #ty = eval(env, args_iter.next().ok_or(EvalError::ArgumentError(format!("Missing Argument {}", #arg_name_str)))?)?.try_into()?;
});
} else {
conversion_statements.push(quote! {
let #arg_name: #ty = args_iter.next().ok_or(EvalError::ArgumentError(format!("Missing Argument {}", #arg_name_str)))?.try_into()?;
});
}
}
}
}
let func_name = match attr.fname {
Some(fname) => fname,
None => func_name.clone(),
};
let gen = quote! {
#vis fn #func_name(env: &Environment, expr: Expression) -> Result<Expression, EvalError> {
let args: Vec<Expression> = expr.try_into()?;
let mut args_iter = args.into_iter();
#(#conversion_statements)*
Ok((|| #ret #block)()?.into())
}
};
let out: TokenStream = gen.into();
print!("{}", out);
out
}

View File

@ -1,5 +1,5 @@
use lispers::lisp::{eval, Environment};
use lispers::parser::ExpressionStream;
use lispers_core::lisp::{eval, Environment};
use lispers_core::parser::ExpressionStream;
fn main() {
let programs = [

View File

@ -1,7 +1,7 @@
use lispers::lisp::Expression;
use lispers::parser::ParserError;
use lispers_core::lisp::Expression;
use lispers_core::parser::ParserError;
use lispers::{lisp, parser};
use lispers_core::{lisp, parser};
use std::io::Write;
fn main() {

View File

@ -1,11 +1,12 @@
use lispers::lisp::environment::EnvironmentLayer;
use lispers::lisp::prelude::mk_prelude;
use lispers::lisp::{eval, Environment};
use lispers::parser::ExpressionStream;
use lispers::raytracer::lisp::mk_raytrace;
use lispers_core::lisp::environment::EnvironmentLayer;
use lispers_core::lisp::prelude::mk_prelude;
use lispers_core::lisp::{eval, Environment};
use lispers_core::parser::ExpressionStream;
fn main() {
let programs = [
"(vadd (vector 1 2 3) (vector 4 5 6))",
"(set 'blue (material (color 0 0 1) (color 0 0 1) (color 0 0 0.6) 50 0.25))",
"(set 'green (material (color 0 1 0) (color 0 1 0) (color 0 0.6 0) 50 0.25))",
"(set 'white (material (color 1 1 1) (color 1 1 1) (color 0.6 0.6 0.6) 100 0.5))",

View File

@ -1,3 +1 @@
pub mod lisp;
pub mod parser;
pub mod raytracer;

View File

@ -1,11 +1,12 @@
use crate::{
lisp::{
use crate::raytracer::{scene::Scene, types::Light};
use lispers_macro::native_lisp_function;
use lispers_core::lisp::{
environment::EnvironmentLayer,
eval::{eval, EvalError},
expression::ForeignDataWrapper,
Environment, Expression,
},
raytracer::{scene::Scene, types::Light},
};
use super::{
@ -199,6 +200,14 @@ pub fn render(env: &Environment, expr: Expression) -> Result<Expression, EvalErr
}
}
#[native_lisp_function(fname = vadd, eval)]
pub fn vadd_vv(
a: ForeignDataWrapper<Vector3>,
b: ForeignDataWrapper<Vector3>,
) -> Result<ForeignDataWrapper<Vector3>, EvalError> {
Ok(ForeignDataWrapper::new(*a + *b))
}
/// Adds the raytracing functions to the given environment layer.
pub fn mk_raytrace(layer: &mut EnvironmentLayer) {
layer.set("point".to_string(), Expression::Function(point));
@ -223,4 +232,5 @@ pub fn mk_raytrace(layer: &mut EnvironmentLayer) {
);
layer.set("camera".to_string(), Expression::Function(camera));
layer.set("render".to_string(), Expression::Function(render));
layer.set("vadd".to_string(), Expression::Function(vadd));
}

View File

@ -219,7 +219,7 @@ impl PartialOrd for RTObjectWrapper {
#[test]
fn test_rt_wrapper_expr_conversion() {
use super::sphere::Sphere;
use crate::lisp::expression::{Expression, ForeignDataWrapper};
use lispers_core::lisp::expression::{Expression, ForeignDataWrapper};
let sphere = Sphere::new(
Point3::new(0.0, 0.0, 0.0),
1.0,