diff --git a/lispers-macro/src/lib.rs b/lispers-macro/src/lib.rs index 389a796..f2f9bb2 100644 --- a/lispers-macro/src/lib.rs +++ b/lispers-macro/src/lib.rs @@ -21,16 +21,16 @@ impl syn::parse::Parse for FlagOrKV { } } -struct Attrs { +struct NativeLispAttrs { pub eval: bool, pub fname: Option, } -impl syn::parse::Parse for Attrs { +impl syn::parse::Parse for NativeLispAttrs { fn parse(input: syn::parse::ParseStream) -> syn::Result { let exprs = Punctuated::::parse_terminated(input)?; - let mut ret = Attrs { + let mut ret = NativeLispAttrs { eval: false, fname: None, }; @@ -58,6 +58,47 @@ impl syn::parse::Parse for Attrs { } } +struct NativeLispProxyAttrs { + pub eval: bool, + pub fname: Ident, + pub dispatcher: Vec, +} + +impl syn::parse::Parse for NativeLispProxyAttrs { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let exprs = Punctuated::::parse_terminated(input)?; + + let mut ret = NativeLispProxyAttrs { + eval: false, + fname: Ident::new("proxy", proc_macro2::Span::call_site()), + dispatcher: Vec::new(), + }; + + 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() == "dispatch" { + ret.dispatcher.push(v); + } else if k.to_string() == "fname" { + ret.fname = 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 @@ -69,7 +110,7 @@ pub fn native_lisp_function(attr: TokenStream, item: TokenStream) -> TokenStream let ret = &sig.output; // Parse attrs - let attr = parse_macro_input!(attr as Attrs); + let attr = parse_macro_input!(attr as NativeLispAttrs); // Extract argument conversion statements let mut conversion_statements = Vec::new(); @@ -77,15 +118,14 @@ pub fn native_lisp_function(attr: TokenStream, item: TokenStream) -> TokenStream 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(); + let arg_name_str = ident.ident.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()?; + let #ident: #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 #ident: #ty = args_iter.next().ok_or(EvalError::ArgumentError(format!("Missing Argument {}", #arg_name_str)))?.try_into()?; }); } } @@ -97,7 +137,7 @@ pub fn native_lisp_function(attr: TokenStream, item: TokenStream) -> TokenStream None => func_name.clone(), }; - let gen = quote! { + quote! { #vis fn #func_name(env: &Environment, expr: Expression) -> Result { let args: Vec = expr.try_into()?; let mut args_iter = args.into_iter(); @@ -106,9 +146,47 @@ pub fn native_lisp_function(attr: TokenStream, item: TokenStream) -> TokenStream Ok((|| #ret #block)()?.into()) } + } + .into() +} + +#[proc_macro] +pub fn native_lisp_function_proxy(item: TokenStream) -> TokenStream { + let args = parse_macro_input!(item as NativeLispProxyAttrs); + let fname = &args.fname; + + let eval_statement = if args.eval { + quote! { + let exprs: Vec = expr.try_into()?; + let exprs = exprs.into_iter().map(|expr| eval(env, expr)).collect::, EvalError>>()?; + let expr: Expression = exprs.into(); + } + } else { + quote! {} }; - let out: TokenStream = gen.into(); - print!("{}", out); - out + let try_apply_statements = args + .dispatcher + .iter() + .map(|impl_name| { + quote! { + match #impl_name(env, expr.clone()) { + Err(EvalError::ArgumentError(e)) => {/*Pass*/}, + Err(EvalError::TypeError(e)) => {/*Pass*/}, + x => return x, + } + } + }) + .collect::>(); + + quote! { + fn #fname(env: &Environment, expr: Expression) -> Result { + #eval_statement + + #(#try_apply_statements)* + + Err(EvalError::TypeError("No applicable method found".to_string())) + } + } + .into() } diff --git a/src/bin/rt_lisp_demo.rs b/src/bin/rt_lisp_demo.rs index eaef614..2513a39 100644 --- a/src/bin/rt_lisp_demo.rs +++ b/src/bin/rt_lisp_demo.rs @@ -7,6 +7,9 @@ use lispers_core::parser::ExpressionStream; fn main() { let programs = [ "(vadd (vector 1 2 3) (vector 4 5 6))", + "(vadd (vector 1 2 3) (point 4 5 6))", + "(vadd (point 1 2 3) (vector 4 5 6))", + "(vadd (point 1 2 3) (point 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))", diff --git a/src/raytracer/lisp.rs b/src/raytracer/lisp.rs index b2a9edb..b83ee64 100644 --- a/src/raytracer/lisp.rs +++ b/src/raytracer/lisp.rs @@ -1,6 +1,6 @@ use crate::raytracer::{scene::Scene, types::Light}; -use lispers_macro::native_lisp_function; +use lispers_macro::{native_lisp_function, native_lisp_function_proxy}; use lispers_core::lisp::{ environment::EnvironmentLayer, @@ -16,110 +16,72 @@ use super::{ types::{Color, Material, Point3, RTObjectWrapper, Vector3}, }; -pub fn point(env: &Environment, expr: Expression) -> Result { - let [x, y, z]: [Expression; 3] = expr.try_into()?; - - let x: f64 = eval(env, x)?.try_into()?; - let y: f64 = eval(env, y)?.try_into()?; - let z: f64 = eval(env, z)?.try_into()?; - - Ok(ForeignDataWrapper::new(Point3::new(x, y, z)).into()) +#[native_lisp_function(eval)] +pub fn point(x: f64, y: f64, z: f64) -> Result, EvalError> { + Ok(ForeignDataWrapper::new(Point3::new(x, y, z))) } -pub fn vector(env: &Environment, expr: Expression) -> Result { - let [x, y, z]: [Expression; 3] = expr.try_into()?; - - let x: f64 = eval(env, x)?.try_into()?; - let y: f64 = eval(env, y)?.try_into()?; - let z: f64 = eval(env, z)?.try_into()?; - - Ok(ForeignDataWrapper::new(Vector3::new(x, y, z)).into()) +#[native_lisp_function(eval)] +pub fn vector(x: f64, y: f64, z: f64) -> Result, EvalError> { + Ok(ForeignDataWrapper::new(Vector3::new(x, y, z))) } -pub fn color(env: &Environment, expr: Expression) -> Result { - let [r, g, b]: [Expression; 3] = expr.try_into()?; - - let r: f64 = eval(env, r)?.try_into()?; - let g: f64 = eval(env, g)?.try_into()?; - let b: f64 = eval(env, b)?.try_into()?; - - Ok(ForeignDataWrapper::new(Color::new(r, g, b)).into()) +#[native_lisp_function(eval)] +pub fn color(r: f64, g: f64, b: f64) -> Result, EvalError> { + Ok(ForeignDataWrapper::new(Color::new(r, g, b))) } -pub fn light(env: &Environment, expr: Expression) -> Result { - let [pos, col]: [Expression; 2] = expr.try_into()?; - - let pos: ForeignDataWrapper = eval(env, pos)?.try_into()?; - let col: ForeignDataWrapper = eval(env, col)?.try_into()?; - - let pos: Point3 = *pos; - let col: Color = *col; - - Ok(ForeignDataWrapper::new(Light::new(pos, col)).into()) +#[native_lisp_function(eval)] +pub fn light( + pos: ForeignDataWrapper, + col: ForeignDataWrapper, +) -> Result, EvalError> { + Ok(ForeignDataWrapper::new(Light::new(*pos, *col))) } -pub fn material(env: &Environment, expr: Expression) -> Result { - let [amb, dif, spe, shi, mir]: [Expression; 5] = expr.try_into()?; - - let amb: ForeignDataWrapper = eval(env, amb)?.try_into()?; - let dif: ForeignDataWrapper = eval(env, dif)?.try_into()?; - let spe: ForeignDataWrapper = eval(env, spe)?.try_into()?; - let shi: f64 = eval(env, shi)?.try_into()?; - let mir: f64 = eval(env, mir)?.try_into()?; - - let amb: Color = *amb; - let dif: Color = *dif; - let spe: Color = *spe; - - Ok(ForeignDataWrapper::new(Material::new(amb, dif, spe, shi, mir)).into()) +#[native_lisp_function(eval)] +pub fn material( + amb: ForeignDataWrapper, + dif: ForeignDataWrapper, + spe: ForeignDataWrapper, + shi: f64, + mir: f64, +) -> Result, EvalError> { + Ok(ForeignDataWrapper::new(Material::new( + *amb, *dif, *spe, shi, mir, + ))) } -pub fn sphere(env: &Environment, expr: Expression) -> Result { - let [pos, rad, mat]: [Expression; 3] = expr.try_into()?; - - let pos: ForeignDataWrapper = eval(env, pos)?.try_into()?; - let rad: f64 = eval(env, rad)?.try_into()?; - let mat: ForeignDataWrapper = eval(env, mat)?.try_into()?; - - let pos: Point3 = *pos; - let mat: Material = *mat; - - Ok(ForeignDataWrapper::new(RTObjectWrapper::from(Sphere::new(pos, rad, mat))).into()) +#[native_lisp_function(eval)] +pub fn sphere( + pos: ForeignDataWrapper, + rad: f64, + mat: ForeignDataWrapper, +) -> Result, EvalError> { + Ok(ForeignDataWrapper::new(RTObjectWrapper::from(Sphere::new(*pos, rad, *mat))).into()) } -pub fn plane(env: &Environment, expr: Expression) -> Result { - let [pos, dir, mat]: [Expression; 3] = expr.try_into()?; - - let pos: ForeignDataWrapper = eval(env, pos)?.try_into()?; - let dir: ForeignDataWrapper = eval(env, dir)?.try_into()?; - let mat: ForeignDataWrapper = eval(env, mat)?.try_into()?; - - let pos: Point3 = *pos; - let dir: Vector3 = *dir; - let mat: Material = *mat; - - Ok(ForeignDataWrapper::new(RTObjectWrapper::from(Plane::new(pos, dir, mat))).into()) +#[native_lisp_function(eval)] +pub fn plane( + pos: ForeignDataWrapper, + dir: ForeignDataWrapper, + mat: ForeignDataWrapper, +) -> Result, EvalError> { + Ok(ForeignDataWrapper::new(RTObjectWrapper::from(Plane::new(*pos, *dir, *mat))).into()) } -pub fn checkerboard(env: &Environment, expr: Expression) -> Result { - let [pos, norm, mat1, mat2, sca, up]: [Expression; 6] = expr.try_into()?; - - let pos: ForeignDataWrapper = eval(env, pos)?.try_into()?; - let norm: ForeignDataWrapper = eval(env, norm)?.try_into()?; - let mat1: ForeignDataWrapper = eval(env, mat1)?.try_into()?; - let mat2: ForeignDataWrapper = eval(env, mat2)?.try_into()?; - let sca: f64 = eval(env, sca)?.try_into()?; - let up: ForeignDataWrapper = eval(env, up)?.try_into()?; - - let pos: Point3 = *pos; - let norm: Vector3 = *norm; - let mat1: Material = *mat1; - let mat2: Material = *mat2; - let up: Vector3 = *up; - +#[native_lisp_function(eval)] +pub fn checkerboard( + pos: ForeignDataWrapper, + norm: ForeignDataWrapper, + mat1: ForeignDataWrapper, + mat2: ForeignDataWrapper, + sca: f64, + up: ForeignDataWrapper, +) -> Result, EvalError> { Ok( ForeignDataWrapper::new(RTObjectWrapper::from(Checkerboard::new( - pos, norm, mat1, mat2, sca, up, + *pos, *norm, *mat1, *mat2, sca, *up, ))) .into(), ) @@ -147,50 +109,53 @@ pub fn scene(env: &Environment, expr: Expression) -> Result Result { - let [sce, obj]: [Expression; 2] = expr.try_into()?; - - let mut sce: ForeignDataWrapper = eval(env, sce)?.try_into()?; - let obj: ForeignDataWrapper = eval(env, obj)?.try_into()?; - +#[native_lisp_function] +pub fn scene_add_object( + mut sce: ForeignDataWrapper, + obj: ForeignDataWrapper, +) -> Result, EvalError> { sce.add_object(obj.clone()); - - Ok(sce.into()) + Ok(sce) } -pub fn scene_add_light(env: &Environment, expr: Expression) -> Result { - let [sce, lgt]: [Expression; 2] = expr.try_into()?; - - let mut sce: ForeignDataWrapper = eval(env, sce)?.try_into()?; - let lgt: ForeignDataWrapper = eval(env, lgt)?.try_into()?; - +#[native_lisp_function] +pub fn scene_add_light( + mut sce: ForeignDataWrapper, + lgt: ForeignDataWrapper, +) -> Result, EvalError> { sce.add_light(*lgt); - - Ok(sce.into()) + Ok(sce) } -pub fn camera(env: &Environment, expr: Expression) -> Result { - let [pos, cnt, up, fovy, w, h]: [Expression; 6] = expr.try_into()?; +native_lisp_function_proxy!( + fname = scene_add, + eval, + dispatch = scene_add_object, + dispatch = scene_add_light +); - let pos: ForeignDataWrapper = eval(env, pos)?.try_into()?; - let cnt: ForeignDataWrapper = eval(env, cnt)?.try_into()?; - let up: ForeignDataWrapper = eval(env, up)?.try_into()?; - let fovy: f64 = eval(env, fovy)?.try_into()?; - let w: i64 = eval(env, w)?.try_into()?; - let h: i64 = eval(env, h)?.try_into()?; - - Ok(ForeignDataWrapper::new(Camera::new(*pos, *cnt, *up, fovy, w as usize, h as usize)).into()) +#[native_lisp_function(eval)] +pub fn camera( + pos: ForeignDataWrapper, + cnt: ForeignDataWrapper, + up: ForeignDataWrapper, + fovy: f64, + w: i64, + h: i64, +) -> Result, EvalError> { + Ok(ForeignDataWrapper::new(Camera::new( + *pos, *cnt, *up, fovy, w as usize, h as usize, + ))) } -pub fn render(env: &Environment, expr: Expression) -> Result { - let [cam, sce, dpt, sbp, out]: [Expression; 5] = expr.try_into()?; - - let cam: ForeignDataWrapper = eval(env, cam)?.try_into()?; - let sce: ForeignDataWrapper = eval(env, sce)?.try_into()?; - let dpt: i64 = eval(env, dpt)?.try_into()?; - let sbp: i64 = eval(env, sbp)?.try_into()?; - let out: String = eval(env, out)?.try_into()?; - +#[native_lisp_function(eval)] +pub fn render( + cam: ForeignDataWrapper, + sce: ForeignDataWrapper, + dpt: i64, + sbp: i64, + out: String, +) -> Result { println!("Rendering to {}...", out); let img = cam.render(&sce, dpt as u32, sbp as u32); @@ -200,7 +165,7 @@ pub fn render(env: &Environment, expr: Expression) -> Result, b: ForeignDataWrapper, @@ -208,6 +173,80 @@ pub fn vadd_vv( Ok(ForeignDataWrapper::new(*a + *b)) } +#[native_lisp_function] +pub fn vadd_vp( + a: ForeignDataWrapper, + b: ForeignDataWrapper, +) -> Result, EvalError> { + Ok(ForeignDataWrapper::new(*b + *a)) +} + +#[native_lisp_function] +pub fn vadd_pv( + a: ForeignDataWrapper, + b: ForeignDataWrapper, +) -> Result, EvalError> { + Ok(ForeignDataWrapper::new(*a + *b)) +} + +native_lisp_function_proxy!( + fname = vadd, + eval, + dispatch = vadd_vv, + dispatch = vadd_vp, + dispatch = vadd_pv +); + +#[native_lisp_function] +pub fn vsub_vv( + a: ForeignDataWrapper, + b: ForeignDataWrapper, +) -> Result, EvalError> { + Ok(ForeignDataWrapper::new(*a - *b)) +} + +#[native_lisp_function] +pub fn vsub_vp( + a: ForeignDataWrapper, + b: ForeignDataWrapper, +) -> Result, EvalError> { + Ok(ForeignDataWrapper::new(*b - *a)) +} + +#[native_lisp_function] +pub fn vsub_pv( + a: ForeignDataWrapper, + b: ForeignDataWrapper, +) -> Result, EvalError> { + Ok(ForeignDataWrapper::new(*a - *b)) +} + +native_lisp_function_proxy!( + fname = vsub, + eval, + dispatch = vsub_vv, + dispatch = vsub_vp, + dispatch = vsub_pv +); + +#[native_lisp_function] +pub fn vmul_vs( + a: ForeignDataWrapper, + b: f64, +) -> Result, EvalError> { + Ok(ForeignDataWrapper::new(*a * b)) +} + +#[native_lisp_function] +pub fn vmul_sv( + a: f64, + b: ForeignDataWrapper, +) -> Result, EvalError> { + Ok(ForeignDataWrapper::new(*b * a)) +} + +native_lisp_function_proxy!(fname = vmul, eval, dispatch = vmul_vs, dispatch = vmul_sv); + /// Adds the raytracing functions to the given environment layer. pub fn mk_raytrace(layer: &mut EnvironmentLayer) { layer.set("point".to_string(), Expression::Function(point)); @@ -233,4 +272,6 @@ 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)); + layer.set("vsub".to_string(), Expression::Function(vsub)); + layer.set("vmul".to_string(), Expression::Function(vmul)); }